diff --git a/.idea/.idea.BABA_YAGA/.idea/workspace.xml b/.idea/.idea.BABA_YAGA/.idea/workspace.xml index 6db1079c..7cfc8b88 100644 --- a/.idea/.idea.BABA_YAGA/.idea/workspace.xml +++ b/.idea/.idea.BABA_YAGA/.idea/workspace.xml @@ -5,7 +5,45 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets/All for one/TargetTrainingMiniGame/Scripts/vSendScore.cs b/Assets/All for one/TargetTrainingMiniGame/Scripts/vSendScore.cs index a630f0b2..57f88679 100644 --- a/Assets/All for one/TargetTrainingMiniGame/Scripts/vSendScore.cs +++ b/Assets/All for one/TargetTrainingMiniGame/Scripts/vSendScore.cs @@ -9,7 +9,7 @@ public class vSendScore : MonoBehaviour { if (shooterScore == null) { - shooterScore = FindObjectOfType(); + shooterScore = FindFirstObjectByType(); } if (shooterScore != null) diff --git a/Assets/Scenes/Main Scene.unity b/Assets/Scenes/Main Scene.unity index 7d2624a0..decbe07d 100644 --- a/Assets/Scenes/Main Scene.unity +++ b/Assets/Scenes/Main Scene.unity @@ -185,75 +185,6 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::_PlayerDataManager _Players: _items: [] ---- !u!1001 &51806460 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalPosition.y - value: 12.48 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalPosition.z - value: -16.72 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalRotation.w - value: 0.7071067 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalRotation.x - value: 0.7071068 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalRotation.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_Name - value: Straight_Manhole_Up 1 - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: - - targetCorrespondingSourceObject: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - insertIndex: -1 - addedObject: {fileID: 1788441690} - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} ---- !u!4 &51806461 stripped -Transform: - m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: e4cc6038906227b41bbab3e2f4906158, type: 3} - m_PrefabInstance: {fileID: 51806460} - m_PrefabAsset: {fileID: 0} --- !u!1 &200732282 GameObject: m_ObjectHideFlags: 0 @@ -417,75 +348,6 @@ Transform: m_CorrespondingSourceObject: {fileID: 2207112960010484425, guid: fb7874830b9e56341bf88f2a1123c677, type: 3} m_PrefabInstance: {fileID: 3886963620680427248} m_PrefabAsset: {fileID: 0} ---- !u!1001 &841412614 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalPosition.y - value: 11.84 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalPosition.z - value: -25.93 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalRotation.w - value: 0.7071068 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalRotation.y - value: 0.70710677 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalRotation.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_Name - value: Dead_End_Manhole_Down - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: - - targetCorrespondingSourceObject: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - insertIndex: -1 - addedObject: {fileID: 1580015190} - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} ---- !u!4 &841412615 stripped -Transform: - m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: c7b8e72ff8d936143a810e1baa494428, type: 3} - m_PrefabInstance: {fileID: 841412614} - m_PrefabAsset: {fileID: 0} --- !u!1 &1142785471 GameObject: m_ObjectHideFlags: 0 @@ -742,7 +604,7 @@ Transform: serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -10, z: 0} - m_LocalScale: {x: 100, y: 1, z: 100} + m_LocalScale: {x: 50, y: 1, z: 50} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1313417867} @@ -794,142 +656,6 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::Hallucinate.GameSetup.Maze.MazeRenderer visualProfile: {fileID: 11400000, guid: 15b745b0bb979b84ea937c679ee0f1ed, type: 2} floorHeight: 3.5 ---- !u!1001 &1580015189 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 841412615} - m_Modifications: - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalPosition.y - value: -12.12 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalRotation.w - value: 0.70710677 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalRotation.x - value: 0.7071068 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - propertyPath: m_Name - value: Dead_Manhole_Up - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} ---- !u!4 &1580015190 stripped -Transform: - m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 1e0fe08755c202644ab57e3ecb75ea61, type: 3} - m_PrefabInstance: {fileID: 1580015189} - m_PrefabAsset: {fileID: 0} ---- !u!1001 &1788441689 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 51806461} - m_Modifications: - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalScale.x - value: 1 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalScale.y - value: 1 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalScale.z - value: 1 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalPosition.z - value: 6 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalRotation.x - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 919132149155446097, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - propertyPath: m_Name - value: Straight_Manhole_Down - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} ---- !u!4 &1788441690 stripped -Transform: - m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 0bcb8b5c17d1d3c4cab9ff76ce3b36bc, type: 3} - m_PrefabInstance: {fileID: 1788441689} - m_PrefabAsset: {fileID: 0} --- !u!1 &1997343488 GameObject: m_ObjectHideFlags: 0 @@ -1113,259 +839,9 @@ PrefabInstance: m_AddedGameObjects: [] m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: fb7874830b9e56341bf88f2a1123c677, type: 3} ---- !u!1001 &5166543384961234755 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 2878949705306832448, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_Name - value: Celling - objectReference: {fileID: 0} - - target: {fileID: 2878949705306832448, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalPosition.z - value: -5.54 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalRotation.w - value: 0.70710313 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalRotation.x - value: -8.6595606e-17 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalRotation.y - value: 0.7071104 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalRotation.z - value: 8.6595606e-17 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3224512652964734202, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 5629501e49db88c4bb12289a632c8c7c, type: 3} ---- !u!1001 &6183881471858100781 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 8318903820966450222, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_Name - value: Door - objectReference: {fileID: 0} - - target: {fileID: 8318903820966450222, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalPosition.x - value: -5.166 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 8663094307863400084, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: 68e1706bd55181e4aad756a4b2f9782f, type: 3} ---- !u!1001 &7477006319521516329 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalPosition.z - value: 4.75 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3804928350305649978, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4577348821146165120, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_Name - value: Wall - objectReference: {fileID: 0} - - target: {fileID: 4577348821146165120, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: b69e6489df77a9b498c1c77003e0ee1f, type: 3} ---- !u!1001 &8258928368180580295 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - serializedVersion: 3 - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalRotation.y - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalRotation.z - value: -0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 3589531372551451367, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4223043221652826205, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_Name - value: Floor - objectReference: {fileID: 0} - - target: {fileID: 4223043221652826205, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} - propertyPath: m_IsActive - value: 0 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_RemovedGameObjects: [] - m_AddedGameObjects: [] - m_AddedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: a9482a26b06970942a1b56c2d7575f2a, type: 3} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 1997343489} - {fileID: 1142785474} - - {fileID: 841412614} - - {fileID: 51806460} - - {fileID: 8258928368180580295} - - {fileID: 7477006319521516329} - - {fileID: 6183881471858100781} - - {fileID: 5166543384961234755} diff --git a/Assets/Scripts/Audio/AudioDatabase.cs b/Assets/Scripts/Audio/AudioDatabase.cs index 9a0fafa9..b5aae4a1 100644 --- a/Assets/Scripts/Audio/AudioDatabase.cs +++ b/Assets/Scripts/Audio/AudioDatabase.cs @@ -15,7 +15,7 @@ namespace Hallucinate.Audio public AudioMixerGroup MixerGroup; } - [CreateAssetMenu(fileName = "AudioDatabase", menuName = "Hallucinate/Audio/Audio Database")] + [CreateAssetMenu(fileName = "AudioDatabase", menuName = "BABA_YAGA/Audio/Audio Database")] public class AudioDatabase : ScriptableObject { [SerializeField] private List samples = new List(); diff --git a/Assets/Scripts/Camera/vThirdPersonCamera.cs b/Assets/Scripts/Camera/vThirdPersonCamera.cs index f5c9d2a2..36b8df9c 100644 --- a/Assets/Scripts/Camera/vThirdPersonCamera.cs +++ b/Assets/Scripts/Camera/vThirdPersonCamera.cs @@ -17,7 +17,7 @@ namespace Invector.vCamera { if (_instance == null) { - _instance = GameObject.FindObjectOfType(); + _instance = GameObject.FindFirstObjectByType(); //Tell unity not to destroy this object when loading a new scene! //DontDestroyOnLoad(_instance.gameObject); @@ -283,7 +283,7 @@ namespace Invector.vCamera } if (inputReader == null) inputReader = mainTarget.GetComponent(); - if (inputReader == null) inputReader = GameObject.FindObjectOfType(); + if (inputReader == null) inputReader = GameObject.FindFirstObjectByType(); firstUpdated = true; useSmooth = true; diff --git a/Assets/Scripts/GameSetup/CharacterSetupSettings.cs b/Assets/Scripts/GameSetup/CharacterSetupSettings.cs index e917be03..e3f0427b 100644 --- a/Assets/Scripts/GameSetup/CharacterSetupSettings.cs +++ b/Assets/Scripts/GameSetup/CharacterSetupSettings.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace OnlyScove.Scripts.GameSetup { - [CreateAssetMenu(fileName = "CharacterSetupSettings", menuName = "Setup/Character Setup Settings")] + [CreateAssetMenu(fileName = "CharacterSetupSettings", menuName = "BABA_YAGA/Setup/Character Setup Settings")] public class CharacterSetupSettings : ScriptableObject { [Header("Movement Constraints")] diff --git a/Assets/Scripts/GameSetup/GameSettings.cs b/Assets/Scripts/GameSetup/GameSettings.cs index c5ee4b21..4b0315fd 100644 --- a/Assets/Scripts/GameSetup/GameSettings.cs +++ b/Assets/Scripts/GameSetup/GameSettings.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace OnlyScove.Scripts { - [CreateAssetMenu(fileName = "GameSettings", menuName = "Settings/GameSettings")] + [CreateAssetMenu(fileName = "GameSettings", menuName = "BABA_YAGA/Settings/GameSettings")] public class GameSettings : ScriptableObject { [Header("Camera Settings")] diff --git a/Assets/Scripts/GameSetup/Maze/MazeVisualProfile.cs b/Assets/Scripts/GameSetup/Maze/MazeVisualProfile.cs index 524ac73c..d8f4a483 100644 --- a/Assets/Scripts/GameSetup/Maze/MazeVisualProfile.cs +++ b/Assets/Scripts/GameSetup/Maze/MazeVisualProfile.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Hallucinate.GameSetup.Maze { - [CreateAssetMenu(fileName = "MazeVisualProfile", menuName = "Hallucinate/Maze/Visual Profile")] + [CreateAssetMenu(fileName = "MazeVisualProfile", menuName = "BABA_YAGA/Maze/Visual Profile")] public class MazeVisualProfile : ScriptableObject { [Header("Rotation Offsets")] diff --git a/Assets/Scripts/GameSetup/ObjectInteraction.cs b/Assets/Scripts/GameSetup/ObjectInteraction.cs index 1f8d6350..151d85bd 100644 --- a/Assets/Scripts/GameSetup/ObjectInteraction.cs +++ b/Assets/Scripts/GameSetup/ObjectInteraction.cs @@ -1,6 +1,6 @@ using UnityEngine; -[CreateAssetMenu(fileName = "ObjectInteraction", menuName = "Scriptable Objects/ObjectInteraction")] +[CreateAssetMenu(fileName = "ObjectInteraction", menuName = "BABA_YAGA/Scriptable Objects/ObjectInteraction")] public class ObjectInteraction : ScriptableObject { [Header("UI Settings")] diff --git a/Assets/Scripts/Interaction/Item Manager/vInventory.cs b/Assets/Scripts/Interaction/Item Manager/vInventory.cs index 5faaaa66..23347600 100644 --- a/Assets/Scripts/Interaction/Item Manager/vInventory.cs +++ b/Assets/Scripts/Interaction/Item Manager/vInventory.cs @@ -123,7 +123,7 @@ namespace Invector.vItemManager canEquip = true; // search for a StandaloneInputModule in the scene - inputModule = FindObjectOfType(); + inputModule = FindFirstObjectByType(); // if there is none, a new EventSystem is created if (inputModule == null) { @@ -201,7 +201,7 @@ namespace Invector.vItemManager protected virtual IEnumerator ReloadEquipment() { yield return new WaitForEndOfFrame(); - inputModule = FindObjectOfType(); + inputModule = FindFirstObjectByType(); isOpen = true; @@ -397,7 +397,7 @@ namespace Invector.vItemManager } else { - inputModule = FindObjectOfType(); + inputModule = FindFirstObjectByType(); } } diff --git a/Assets/Scripts/Interaction/Item Manager/vItemCollectionDisplay.cs b/Assets/Scripts/Interaction/Item Manager/vItemCollectionDisplay.cs index 798b2247..02bfd11d 100644 --- a/Assets/Scripts/Interaction/Item Manager/vItemCollectionDisplay.cs +++ b/Assets/Scripts/Interaction/Item Manager/vItemCollectionDisplay.cs @@ -14,7 +14,7 @@ namespace Invector.vItemManager { get { - if (instance == null) { instance = GameObject.FindObjectOfType(); } + if (instance == null) { instance = GameObject.FindFirstObjectByType(); } return vItemCollectionDisplay.instance; } } diff --git a/Assets/Scripts/Interaction/Item Manager/vItemListData.cs b/Assets/Scripts/Interaction/Item Manager/vItemListData.cs index b86a4375..49be9e37 100644 --- a/Assets/Scripts/Interaction/Item Manager/vItemListData.cs +++ b/Assets/Scripts/Interaction/Item Manager/vItemListData.cs @@ -3,7 +3,7 @@ using UnityEngine; namespace Invector.vItemManager { - [CreateAssetMenu(menuName = "Invector/Inventory/New Item List")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Inventory/New Item List")] public class vItemListData : ScriptableObject { public List items = new List(); diff --git a/Assets/Scripts/Interaction/Item Manager/vJoystickMouseInput.cs b/Assets/Scripts/Interaction/Item Manager/vJoystickMouseInput.cs index 3750d15a..135e5fa9 100644 --- a/Assets/Scripts/Interaction/Item Manager/vJoystickMouseInput.cs +++ b/Assets/Scripts/Interaction/Item Manager/vJoystickMouseInput.cs @@ -50,7 +50,7 @@ public class vJoystickMouseInput : BaseInput { base.Awake(); if (!inputModule) - inputModule = FindObjectOfType(); + inputModule = FindFirstObjectByType(); if (inputModule) { oldOverride = inputModule.inputOverride; diff --git a/Assets/Scripts/Interaction/Item Manager/vLeaveDropItemExample.cs b/Assets/Scripts/Interaction/Item Manager/vLeaveDropItemExample.cs index ef3aea49..e50c5b32 100644 --- a/Assets/Scripts/Interaction/Item Manager/vLeaveDropItemExample.cs +++ b/Assets/Scripts/Interaction/Item Manager/vLeaveDropItemExample.cs @@ -15,7 +15,7 @@ namespace Invector.vItemManager private void vLeaveDropItensWindow(int windowID) { GUILayout.BeginVertical(); - itemManager = FindObjectOfType(); + itemManager = FindFirstObjectByType(); if (itemManager) { diff --git a/Assets/Scripts/Player/CharacterController/Editor/vCreateMeleeCharacterEditor.cs b/Assets/Scripts/Player/CharacterController/Editor/vCreateMeleeCharacterEditor.cs index 429897f8..50a4780d 100644 --- a/Assets/Scripts/Player/CharacterController/Editor/vCreateMeleeCharacterEditor.cs +++ b/Assets/Scripts/Player/CharacterController/Editor/vCreateMeleeCharacterEditor.cs @@ -260,7 +260,7 @@ namespace Invector.vMelee if (useGameController) { GameObject gC = null; - var gameController = FindObjectOfType(); + var gameController = FindFirstObjectByType(); if (gameController == null) { gC = new GameObject("vGameController_Example"); diff --git a/Assets/Scripts/Player/CharacterController/vGameController.cs b/Assets/Scripts/Player/CharacterController/vGameController.cs index b254cca2..ea46d7d1 100644 --- a/Assets/Scripts/Player/CharacterController/vGameController.cs +++ b/Assets/Scripts/Player/CharacterController/vGameController.cs @@ -172,7 +172,7 @@ namespace Invector protected virtual void FindPlayer() { - var player = GameObject.FindObjectOfType(); + var player = GameObject.FindFirstObjectByType(); if (player) { diff --git a/Assets/Scripts/Player/CharacterController/vHUDController.cs b/Assets/Scripts/Player/CharacterController/vHUDController.cs index 8f87fa63..56d8c734 100644 --- a/Assets/Scripts/Player/CharacterController/vHUDController.cs +++ b/Assets/Scripts/Player/CharacterController/vHUDController.cs @@ -55,7 +55,7 @@ namespace Invector.vCharacterController { if (_instance == null) { - _instance = GameObject.FindObjectOfType(); + _instance = GameObject.FindFirstObjectByType(); //Tell unity not to destroy this object when loading a new scene //DontDestroyOnLoad(_instance.gameObject); } diff --git a/Assets/Scripts/Player/CharacterController/vInput.cs b/Assets/Scripts/Player/CharacterController/vInput.cs index 42b442e4..072a2237 100644 --- a/Assets/Scripts/Player/CharacterController/vInput.cs +++ b/Assets/Scripts/Player/CharacterController/vInput.cs @@ -19,7 +19,7 @@ namespace Invector.vCharacterController { if (_instance == null) { - _instance = GameObject.FindObjectOfType(); + _instance = GameObject.FindFirstObjectByType(); if (_instance == null) { _instance = new GameObject("vInputType").AddComponent(); diff --git a/Assets/Scripts/Player/CharacterController/vThirdPersonInput.cs b/Assets/Scripts/Player/CharacterController/vThirdPersonInput.cs index 8176ba04..fb23d492 100644 --- a/Assets/Scripts/Player/CharacterController/vThirdPersonInput.cs +++ b/Assets/Scripts/Player/CharacterController/vThirdPersonInput.cs @@ -147,7 +147,7 @@ namespace Invector.vCharacterController public virtual void FindCamera() { - var tpCameras = FindObjectsOfType(); + var tpCameras = FindObjectsByType(FindObjectsSortMode.None); if (tpCameras.Length > 1) { @@ -591,7 +591,7 @@ namespace Invector.vCharacterController if (tpCamera == null) { - tpCamera = FindObjectOfType(); + tpCamera = FindFirstObjectByType(); if (tpCamera == null) { return; diff --git a/Assets/Scripts/Player/CharacterCreator/Script/Editor/vCreateBasicCharacterEditor.cs b/Assets/Scripts/Player/CharacterCreator/Script/Editor/vCreateBasicCharacterEditor.cs index 3ba175cf..ca4ab5e3 100644 --- a/Assets/Scripts/Player/CharacterCreator/Script/Editor/vCreateBasicCharacterEditor.cs +++ b/Assets/Scripts/Player/CharacterCreator/Script/Editor/vCreateBasicCharacterEditor.cs @@ -235,7 +235,7 @@ namespace Invector.vCharacterController if (useGameController) { GameObject gC = null; - var gameController = FindObjectOfType(); + var gameController = FindFirstObjectByType(); if (gameController == null) { gC = new GameObject("vGameController_Example"); diff --git a/Assets/Scripts/Player/Generic/BodySnapSystem/Scripts/vBodyStruct.cs b/Assets/Scripts/Player/Generic/BodySnapSystem/Scripts/vBodyStruct.cs index 5d2e7f87..cdd74682 100644 --- a/Assets/Scripts/Player/Generic/BodySnapSystem/Scripts/vBodyStruct.cs +++ b/Assets/Scripts/Player/Generic/BodySnapSystem/Scripts/vBodyStruct.cs @@ -2,7 +2,7 @@ using System.Linq; using UnityEngine; -[CreateAssetMenu(menuName = "Invector/SnapBody/New Body Struct")] +[CreateAssetMenu(menuName = "BABA_YAGA/Invector/SnapBody/New Body Struct")] public class vBodyStruct : ScriptableObject { public List bones = new List(); diff --git a/Assets/Scripts/Player/Generic/Editor/InvectorDefineSymbolsManager.cs b/Assets/Scripts/Player/Generic/Editor/InvectorDefineSymbolsManager.cs index 3293dc09..1af83095 100644 --- a/Assets/Scripts/Player/Generic/Editor/InvectorDefineSymbolsManager.cs +++ b/Assets/Scripts/Player/Generic/Editor/InvectorDefineSymbolsManager.cs @@ -18,7 +18,7 @@ namespace Invector.DefineSymbolsManager public static void CreateDefinitions() { - string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); + string definesString = PlayerSettings.GetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup)); List allDefines = definesString.Split(';').ToList(); List allInvectorDefines = new List(); @@ -70,8 +70,8 @@ namespace Invector.DefineSymbolsManager } currentDefineSymbols.AddRange(targetDefineSymbols.Except(currentDefineSymbols)); if (needUpdate) - PlayerSettings.SetScriptingDefineSymbolsForGroup( - EditorUserBuildSettings.selectedBuildTargetGroup, + PlayerSettings.SetScriptingDefineSymbols( + UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(EditorUserBuildSettings.selectedBuildTargetGroup), string.Join(";", currentDefineSymbols.ToArray())); } diff --git a/Assets/Scripts/Player/Generic/Editor/vInspectorSearchTool.cs b/Assets/Scripts/Player/Generic/Editor/vInspectorSearchTool.cs index 7882b21f..7ddf280d 100644 --- a/Assets/Scripts/Player/Generic/Editor/vInspectorSearchTool.cs +++ b/Assets/Scripts/Player/Generic/Editor/vInspectorSearchTool.cs @@ -8,7 +8,7 @@ static class vInspectorSearchTool { // Editor.finishedDefaultHeaderGUI -= DrawInpectorSearchTool; // Editor.finishedDefaultHeaderGUI += DrawInpectorSearchTool; - Debug.LogWarning("vInspectorSearchTool: finishedDefaultHeaderGUI is deprecated/removed in Unity 6 and has been disabled."); + // Debug.LogWarning("vInspectorSearchTool: finishedDefaultHeaderGUI is deprecated/removed in Unity 6 and has been disabled."); } public static string search; diff --git a/Assets/Scripts/Player/Generic/Editor/vInvectorIcon.cs b/Assets/Scripts/Player/Generic/Editor/vInvectorIcon.cs index 43be519b..a4c65768 100644 --- a/Assets/Scripts/Player/Generic/Editor/vInvectorIcon.cs +++ b/Assets/Scripts/Player/Generic/Editor/vInvectorIcon.cs @@ -16,7 +16,7 @@ namespace Invector } static void ThirPersonCameraIcon(int instanceId, Rect selectionRect) { - GameObject go = EditorUtility.InstanceIDToObject(instanceId) as GameObject; + GameObject go = EditorUtility.EntityIdToObject(instanceId) as GameObject; if (go == null) return; var tpCamera = go.GetComponent(); @@ -25,7 +25,7 @@ namespace Invector static void ThirdPersonControllerIcon(int instanceId, Rect selectionRect) { - GameObject go = EditorUtility.InstanceIDToObject(instanceId) as GameObject; + GameObject go = EditorUtility.EntityIdToObject(instanceId) as GameObject; if (go == null) return; var controller = go.GetComponent(); diff --git a/Assets/Scripts/Player/Generic/Triggers/vChangeCameraAngleTrigger.cs b/Assets/Scripts/Player/Generic/Triggers/vChangeCameraAngleTrigger.cs index 92b4d102..da35c5b6 100644 --- a/Assets/Scripts/Player/Generic/Triggers/vChangeCameraAngleTrigger.cs +++ b/Assets/Scripts/Player/Generic/Triggers/vChangeCameraAngleTrigger.cs @@ -21,7 +21,7 @@ namespace Invector.vCamera } IEnumerator Start() { - tpCamera = FindObjectOfType(); + tpCamera = FindFirstObjectByType(); var collider = GetComponent(); if (collider) { diff --git a/Assets/Scripts/Player/Generic/Utils/vMousePositionHandler.cs b/Assets/Scripts/Player/Generic/Utils/vMousePositionHandler.cs index 3f4fae68..aa59553b 100644 --- a/Assets/Scripts/Player/Generic/Utils/vMousePositionHandler.cs +++ b/Assets/Scripts/Player/Generic/Utils/vMousePositionHandler.cs @@ -16,7 +16,7 @@ namespace Invector.vCharacterController { if (_instance == null) { - _instance = FindObjectOfType(); + _instance = FindFirstObjectByType(); } if (_instance == null) diff --git a/Assets/Scripts/Player/IK/vWeaponIKAdjust.cs b/Assets/Scripts/Player/IK/vWeaponIKAdjust.cs index 80ed7390..d84e884e 100644 --- a/Assets/Scripts/Player/IK/vWeaponIKAdjust.cs +++ b/Assets/Scripts/Player/IK/vWeaponIKAdjust.cs @@ -7,7 +7,7 @@ namespace Invector.vShooter using IK; using UnityEngine.Serialization; - [CreateAssetMenu(menuName = "Invector/Shooter/New Weapon IK Adjust")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Shooter/New Weapon IK Adjust")] public class vWeaponIKAdjust : ScriptableObject { public const string StandingState = "Standing"; diff --git a/Assets/Scripts/Player/IK/vWeaponIKAdjustList.cs b/Assets/Scripts/Player/IK/vWeaponIKAdjustList.cs index d86dcf65..f1caace3 100644 --- a/Assets/Scripts/Player/IK/vWeaponIKAdjustList.cs +++ b/Assets/Scripts/Player/IK/vWeaponIKAdjustList.cs @@ -3,7 +3,7 @@ using UnityEngine; namespace Invector.vShooter { - [CreateAssetMenu(menuName = "Invector/Shooter/New Weapon IK Adjust List")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Shooter/New Weapon IK Adjust List")] public class vWeaponIKAdjustList : ScriptableObject { [vSeparator("Global Offsets for all Weapons Hand IK target")] diff --git a/Assets/Scripts/Player/ImpactEffect/Scripts/CustomImpactEffects/vCustomImpactEffectSample.cs b/Assets/Scripts/Player/ImpactEffect/Scripts/CustomImpactEffects/vCustomImpactEffectSample.cs index 9ce874e3..45a5484a 100644 --- a/Assets/Scripts/Player/ImpactEffect/Scripts/CustomImpactEffects/vCustomImpactEffectSample.cs +++ b/Assets/Scripts/Player/ImpactEffect/Scripts/CustomImpactEffects/vCustomImpactEffectSample.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Invector.vShooter { - [CreateAssetMenu(menuName = "Invector/Shooter/Impact Effects/New Custom ImpactEffect", fileName = "CustomImpactEffect@")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Shooter/Impact Effects/New Custom ImpactEffect", fileName = "CustomImpactEffect@")] public class vCustomImpactEffectSample : vImpactEffectBase { public enum Align diff --git a/Assets/Scripts/Player/ImpactEffect/Scripts/vImpactEffect.cs b/Assets/Scripts/Player/ImpactEffect/Scripts/vImpactEffect.cs index f717f3e9..fbe0ceca 100644 --- a/Assets/Scripts/Player/ImpactEffect/Scripts/vImpactEffect.cs +++ b/Assets/Scripts/Player/ImpactEffect/Scripts/vImpactEffect.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Invector.vShooter { - [CreateAssetMenu(menuName = "Invector/Shooter/Impact Effects/New ImpactEffect", fileName = "ImpactEffect@")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Shooter/Impact Effects/New ImpactEffect", fileName = "ImpactEffect@")] public class vImpactEffect : vImpactEffectBase { public List decals; diff --git a/Assets/Scripts/Player/Shooter/Editor/vCreateShooterCharacterEditor.cs b/Assets/Scripts/Player/Shooter/Editor/vCreateShooterCharacterEditor.cs index 6d49f5a6..0309c883 100644 --- a/Assets/Scripts/Player/Shooter/Editor/vCreateShooterCharacterEditor.cs +++ b/Assets/Scripts/Player/Shooter/Editor/vCreateShooterCharacterEditor.cs @@ -265,7 +265,7 @@ namespace Invector.vShooter if (useGameController) { GameObject gC = null; - var gameController = FindObjectOfType(); + var gameController = FindFirstObjectByType(); if (gameController == null) { gC = new GameObject("vGameController_Example"); diff --git a/Assets/Scripts/Player/Shooter/vShooterManager.cs b/Assets/Scripts/Player/Shooter/vShooterManager.cs index 4b008177..52da38e2 100644 --- a/Assets/Scripts/Player/Shooter/vShooterManager.cs +++ b/Assets/Scripts/Player/Shooter/vShooterManager.cs @@ -217,7 +217,7 @@ namespace Invector.vShooter animator = GetComponent(); if (applyRecoilToCamera) { - tpCamera = FindObjectOfType(); + tpCamera = FindFirstObjectByType(); } ammoManager = GetComponent(); if (ammoManager != null) diff --git a/Assets/Scripts/Player/Shooter/vShooterMeleeInput.cs b/Assets/Scripts/Player/Shooter/vShooterMeleeInput.cs index eba7f4b8..1da72610 100644 --- a/Assets/Scripts/Player/Shooter/vShooterMeleeInput.cs +++ b/Assets/Scripts/Player/Shooter/vShooterMeleeInput.cs @@ -291,7 +291,7 @@ namespace Invector.vCharacterController { if (!_controlAimCanvas) { - _controlAimCanvas = FindObjectOfType(); + _controlAimCanvas = FindFirstObjectByType(); if (_controlAimCanvas) { _controlAimCanvas.Init(cc); @@ -1145,7 +1145,7 @@ namespace Invector.vCharacterController if (tpCamera == null) { - tpCamera = FindObjectOfType(); + tpCamera = FindFirstObjectByType(); if (tpCamera == null) { return; diff --git a/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowSettings.cs b/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowSettings.cs index 5d36f0a6..33f70c9b 100644 --- a/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowSettings.cs +++ b/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowSettings.cs @@ -1,7 +1,7 @@ using UnityEngine; namespace Invector.Throw { - [CreateAssetMenu(menuName = "Invector/Throw/New ThrowSettings")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Throw/New ThrowSettings")] public class vThrowSettings : ScriptableObject { [Tooltip("Align End point UP to surface normal direction")] diff --git a/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowVisualSettings.cs b/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowVisualSettings.cs index 02a08e03..59645eb9 100644 --- a/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowVisualSettings.cs +++ b/Assets/Scripts/Player/ThrowSystem/Scripts/vThrowVisualSettings.cs @@ -1,7 +1,7 @@ using UnityEngine; namespace Invector.Throw { - [CreateAssetMenu(menuName = "Invector/Throw/New ThrowVisualSettings")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Throw/New ThrowVisualSettings")] public class vThrowVisualSettings : ScriptableObject { [vSeparator("Visual Line")] diff --git a/Assets/Scripts/Player/Weapon/vBulletLifeSettings.cs b/Assets/Scripts/Player/Weapon/vBulletLifeSettings.cs index 6d7d0f60..dea9ea1c 100644 --- a/Assets/Scripts/Player/Weapon/vBulletLifeSettings.cs +++ b/Assets/Scripts/Player/Weapon/vBulletLifeSettings.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Invector.vShooter { - [CreateAssetMenu(menuName = "Invector/Shooter/New BulletLifeSettings")] + [CreateAssetMenu(menuName = "BABA_YAGA/Invector/Shooter/New BulletLifeSettings")] public class vBulletLifeSettings : ScriptableObject { public List bulletLostLifeList; diff --git a/Assets/Scripts/Player/Weapon/vShooterWeapon.cs b/Assets/Scripts/Player/Weapon/vShooterWeapon.cs index 191f3c24..ba871986 100644 --- a/Assets/Scripts/Player/Weapon/vShooterWeapon.cs +++ b/Assets/Scripts/Player/Weapon/vShooterWeapon.cs @@ -233,8 +233,9 @@ namespace Invector.vShooter onDisable.Invoke(); } - protected virtual void Start() + protected override void Start() { + base.Start(); if (!reloadSource) { reloadSource = source; diff --git a/Assets/Scripts/UI/FirebaseConfig.cs b/Assets/Scripts/UI/FirebaseConfig.cs index 5db4c07a..cc28cb16 100644 --- a/Assets/Scripts/UI/FirebaseConfig.cs +++ b/Assets/Scripts/UI/FirebaseConfig.cs @@ -2,7 +2,7 @@ using UnityEngine; namespace Hallucinate.UI { - [CreateAssetMenu(fileName = "FirebaseConfig", menuName = "Configs/FirebaseConfig")] + [CreateAssetMenu(fileName = "FirebaseConfig", menuName = "BABA_YAGA/Configs/FirebaseConfig")] public class FirebaseConfig : ScriptableObject { public string baseUrl; diff --git a/Assets/Scripts/UI/PauseMenuController.cs b/Assets/Scripts/UI/PauseMenuController.cs index a3563320..d44ffd01 100644 --- a/Assets/Scripts/UI/PauseMenuController.cs +++ b/Assets/Scripts/UI/PauseMenuController.cs @@ -62,13 +62,13 @@ namespace Hallucinate.UI { Show(); root.style.opacity = 0; - PrimeTween.Tween.Custom(0f, 1f, duration: 0.2f, onValueChange: val => root.style.opacity = val); + await PrimeTween.Tween.Custom(0f, 1f, duration: 0.2f, onValueChange: val => root.style.opacity = val); await Task.Delay(200); } public override async Task PlayTransitionOut() { - PrimeTween.Tween.Custom(1f, 0f, duration: 0.2f, onValueChange: val => root.style.opacity = val); + await PrimeTween.Tween.Custom(1f, 0f, duration: 0.2f, onValueChange: val => root.style.opacity = val); await Task.Delay(200); Hide(); } diff --git a/Assets/Third Parties/vFavorites.meta b/Assets/Third Parties/vFavorites.meta new file mode 100644 index 00000000..2f375665 --- /dev/null +++ b/Assets/Third Parties/vFavorites.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 61e90743ef9987d44832f60b572715ef +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/Manual.pdf b/Assets/Third Parties/vFavorites/Manual.pdf new file mode 100644 index 00000000..101f3fc8 Binary files /dev/null and b/Assets/Third Parties/vFavorites/Manual.pdf differ diff --git a/Assets/Third Parties/vFavorites/Manual.pdf.meta b/Assets/Third Parties/vFavorites/Manual.pdf.meta new file mode 100644 index 00000000..a486e69d --- /dev/null +++ b/Assets/Third Parties/vFavorites/Manual.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 842468ccf23564770b4b1cb9f7eca30e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavorites.asmdef b/Assets/Third Parties/vFavorites/VFavorites.asmdef new file mode 100644 index 00000000..8897fa16 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavorites.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VFavorites", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Third Parties/vFavorites/VFavorites.asmdef.meta b/Assets/Third Parties/vFavorites/VFavorites.asmdef.meta new file mode 100644 index 00000000..d92c7c02 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavorites.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d8266c7db84a045d3b706b23e60aa197 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavorites.cs b/Assets/Third Parties/vFavorites/VFavorites.cs new file mode 100644 index 00000000..226dc0da --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavorites.cs @@ -0,0 +1,1818 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using Type = System.Type; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; +using static VFavorites.VFavoritesData; + + +namespace VFavorites +{ + public static class VFavorites + { + static void WrappedOnGUI(object _) + { + if (wrappedBrowser?.GetType() != t_BrowserWindow) { originalBrowserGUI(); return; } + + + void createData() + { + if (data) return; + + data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(data, GetScriptPath("VFavorites").GetParentPath().CombinePath("vFavorites Data.asset")); + + + + // migrateDataFromV1 + + if (!EditorPrefs.HasKey("vFavorites-guids-" + GetProjectId())) return; + if (EditorPrefs.HasKey("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId())) return; + + EditorPrefs.SetBool("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId(), true); + + + if (!data.pages.Any()) + data.pages.Add(new Page("Page 1")); + + + var guidsFromV1 = EditorPrefs.GetString("vFavorites-guids-" + GetProjectId()).Split('-').Where(r => r != "").ToList(); + + foreach (var guid in guidsFromV1) + if (AssetDatabase.LoadAssetAtPath(guid.ToPath()) is Object obj) + data.pages.First().items.Add(new Item(obj)); + + + data.Dirty(); + data.Save(); + + } + + void background() + { + var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f); + + totalRect_groupSpace.Draw(color); + + } + void pages() + { + void page(Rect pageRect, Page page) + { + void findSelectedItem() + { + if (!curEvent.isLayout) return; + + foreach (var item in page.items) + item.isSelected = false; + + if (draggingItem) return; + if (mousePresesdOnItem) return; + if (page.lastItemDragTime_ticks > page.lastItemSelectTime_ticks) return; + + Item lastSelectedItem = null; + + foreach (var item in page.items) + { + if (!item.isLoadable) continue; + if (lastSelectedItem?.lastSelectTime_ticks > item.lastSelectTime_ticks) continue; + + + var isSelected = false; + + if (item.isFolder) + { + var targetBrowser = isWrappedBrowserLocked && isOneColumn ? allBrowsers.FirstOrDefault(r => !r.GetMemberValue("isLocked")) ?? wrappedBrowser + : wrappedBrowser; + + if (targetBrowser.GetFieldValue("m_ViewMode") == 1) + isSelected = targetBrowser.InvokeMethod("GetActiveFolderPath") == item.assetPath; + else + isSelected = Selection.activeObject == item.obj; + + } + + if (!item.isFolder) + isSelected = Selection.activeObject == item.obj; + + + if (isSelected) + lastSelectedItem = item; + + } + + if (lastSelectedItem != null) + lastSelectedItem.isSelected = true; + + } + + void rows() + { + void row(float y, Item item) + { + var rowRect = pageRect.SetHeight(rowHeight).SetY(y).SetX(0); + + var iconOffset = 6; + var iconSize = 25 * Mathf.Min(1, data.rowScale); + var nameOffset = 3; + var deletedOrNotLoadedLabelOffset = 1; + + float highlightAmount = 0f; + + + void set_highlightAmount() + { + if (animatingDroppedItem && item == droppedItem) + highlightAmount = droppedItemHighlightAmount; + + if (item.isSelected) + highlightAmount = 1; + + if (draggingItem && item == draggedItem) + highlightAmount = 1; + + if (mousePresesdOnItem && item == pressedItem) + highlightAmount = 1; + + } + + void shadow() + { + if (item != draggedItem && item != droppedItem) return; + + var amount = item == droppedItem ? droppedItemShadowAmount : 1; + + if (amount.Approx(0)) return; + + rowRect.AddWidthFromMid(30).DrawBlurred(Greyscale(0, .55f * amount), 22); + + } + void background() + { + var evenColor = isDarkTheme ? Greyscale(.249f) : Greyscale(.82f); + var oddColor = isDarkTheme ? Greyscale(.228f) : Greyscale(.85f); + var highlightedColor = isDarkTheme ? Greyscale(.335f) : Greyscale(.9f); + + var rowColor = Lerp(evenColor, oddColor, rowRect.y.PingPong(rowHeight) / rowHeight); + + Lerp(ref rowColor, highlightedColor, highlightAmount); + + rowRect.Draw(rowColor); + + } + void icon() + { + var iconRect = rowRect.MoveX(iconOffset).SetWidth(iconSize).SetHeightFromMid(iconSize); + + if (item.isSceneGameObject) + iconRect = iconRect.MoveX(1).Resize(.5f); + + void asset() + { + if (!item.isAsset) return; + + var iconTexture = item.isLoadable ? AssetPreview.GetAssetPreview(item.obj) ?? AssetPreview.GetMiniThumbnail(item.obj) : AssetPreview.GetMiniTypeThumbnail(item.type); + + GUI.DrawTexture(iconRect, iconTexture); + + } + void sceneGameObject() + { + if (!item.isSceneGameObject) return; + + void getIconNameFromAssetPreview() + { + if (!item.isLoadable) return; + + item.sceneGameObjectIconName = AssetPreview.GetMiniThumbnail(item.obj).name; + + } + void getIconNameFromVHierarchy() + { + if (!item.isLoadable) return; + if (!(item.obj is GameObject gameObject)) return; + if (mi_VHierarchy_GetIconName == null) return; + + var iconNameFromVHierarchy = (string)mi_VHierarchy_GetIconName.Invoke(null, new object[] { gameObject }); + + if (!iconNameFromVHierarchy.IsNullOrEmpty()) + item.sceneGameObjectIconName = iconNameFromVHierarchy; + + } + + getIconNameFromAssetPreview(); + getIconNameFromVHierarchy(); + + var iconTexture = EditorGUIUtility.IconContent(item.sceneGameObjectIconName.IsNullOrEmpty() ? "GameObject icon" : item.sceneGameObjectIconName).image; + + GUI.DrawTexture(iconRect, iconTexture); + + } + void folder() + { + if (!item.isFolder) return; + + iconRect = iconRect.Resize(-1.5f); + + void drawNormal() + { + if (isDarkTheme) + if (highlightAmount == 1) return; + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder icon").image); + + } + void drawHighlighted() + { + if (!isDarkTheme) return; + if (highlightAmount != 1) return; + + SetGUIColor(Greyscale(.84f)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder On icon").image); + + ResetGUIColor(); + + } + void drawViaVFolders() + { + mi_VFolders_DrawBigFolderIcon?.Invoke(null, new object[] { iconRect, item.globalId.guid }); + } + + drawNormal(); + drawHighlighted(); + drawViaVFolders(); + + } + + asset(); + sceneGameObject(); + folder(); + + } + void name() + { + var nameRect = rowRect.MoveX(iconOffset + iconSize + nameOffset).MoveY(-.5f).SetHeightFromMid(16); + + void normal() + { + if (isDarkTheme) + if (highlightAmount == 1) return; + + GUI.Label(nameRect, item.name); + + } + void highlighted() + { + if (!isDarkTheme) return; + if (highlightAmount != 1) return; + if (!curEvent.isRepaint) return; + + SetGUIColor(Greyscale(.91f)); + + GUI.skin.GetStyle("WhiteLabel").Draw(nameRect, item.name, false, false, false, false); + + ResetGUIColor(); + + } + + normal(); + highlighted(); + + } + void deletedOrNotLoaded() + { + var labelRect = rowRect.MoveX(iconOffset + iconSize + nameOffset + item.name.GetLabelWidth() + deletedOrNotLoadedLabelOffset).MoveY(.5f); + + SetGUIEnabled(false); + SetLabelFontSize(10); + + if (item.isDeleted) + GUI.Label(labelRect, "Deleted"); + + else if (!item.isLoadable) + GUI.Label(labelRect, "Not loaded"); + + ResetLabelStyle(); + ResetGUIEnabled(); + + } + void crossButton() + { + if (!rowRect.IsHovered()) return; + if (draggingItem) return; + // if (mousePresesdOnItem) return; // idk + + var buttonRect = rowRect.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize); + var iconRect = buttonRect.SetSizeFromMid(16); + + var normalColor = Greyscale(item.isSelected ? .48f : .4f); + var hoveredColor = isDarkTheme ? Greyscale(.8f) : normalColor; + var pressedColor = Greyscale(.6f); + + SetGUIColor(buttonRect.IsHovered() ? (mousePressed ? pressedColor : hoveredColor) : normalColor); + GUI.Label(iconRect, EditorGUIUtility.IconContent("CrossIcon")); + ResetGUIColor(); + + + + if (!mousePressedOnCrossButtonArea) return; + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + CancelRowAnimations(); + data.curPage.rowGaps[data.curPage.items.IndexOf(item)] = rowHeight; + + data.curPage.items.Remove(item); + + data.Dirty(); + data.Save(); + + curEvent.Use(); + + } + void click() + { + if (!rowRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + if (draggingItem) return; + if (mouseDragDistance > 2) return; + if (!item.isLoadable) return; + + SelectItem(item); + + } + void doubleclick() + { + if (!rowRect.IsHovered()) return; + if (!doubleclickUnhandled) return; + + OpenItem(item); + + doubleclickUnhandled = false; + + } + + + set_highlightAmount(); + + shadow(); + background(); + icon(); + name(); + deletedOrNotLoaded(); + crossButton(); + click(); + doubleclick(); + + } + + void normalRow(int i) + { + Space(page.rowGaps[i]); + Space(rowHeight); + + if (page.items[i] == droppedItem && animatingDroppedItem && page == data.curPage) return; + + row(lastRect.y, page.items[i]); + + } + void draggedRow() + { + if (!draggingItem) return; + if (page != data.curPage) return; + + + row(draggedItemY_rowsSpace, draggedItem); + + } + void droppedRow() + { + if (!animatingDroppedItem) return; + if (page != data.curPage) return; + + row(droppedItemY_rowsSpace, droppedItem); + + } + + + if (curEvent.isRepaint && skipNextRepaint) { skipNextRepaint = false; return; } + + + GUILayout.BeginArea(pageRect); + page.scrollPos = EditorGUILayout.BeginScrollView(new Vector2(0, page.scrollPos), GUIStyle.none, GUIStyle.none).y; + + for (int i = 0; i < page.items.Count; i++) + normalRow(i); + + Space(page.rowGaps.Last()); + + Space(60); + + draggedRow(); + droppedRow(); + + EditorGUILayout.EndScrollView(); + GUILayout.EndArea(); + + + } + void curtains() + { + var height = 25; + var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f); + + pageRect.SetHeight(height).DrawCurtainDown(color.SetAlpha((page.scrollPos / 20).Smoothstep())); + pageRect.SetHeightFromBottom(height).DrawCurtainUp(color); + + } + void tutor() + { + if (page.items.Any() || draggingItem) return; + + SetGUIEnabled(false); + SetLabelFontSize(11); + SetLabelAlignmentCenter(); + + GUI.Label(pageRect.MoveY(-13), "Drop folders, assets"); + GUI.Label(pageRect.MoveY(5), "or GameObjects"); + + ResetGUIEnabled(); + ResetLabelStyle(); + } + + + findSelectedItem(); + + rows(); + curtains(); + tutor(); + + } + + var spaceBetweenPages = 10; + + + if (pagesScrollPos == -1) + pagesScrollPos = data.curPageIndex; + + for (int i = pagesScrollPos.FloorToInt(); i <= pagesScrollPos.CeilToInt(); i++) + page(totalRect_groupSpace.SetX((totalRect_groupSpace.width + spaceBetweenPages) * (i - pagesScrollPos)), data.pages[i]); + + } + void widget() + { + var widthToAdd = 48; + var height = 24; + var distToBottom = 13; + + var color = isDarkTheme ? Greyscale(.1f) : Greyscale(.85f); + var shadowSize = 10; + var shadowAlpha = .23f; + + var chevronSize = 15; + var chevronOffset = 12; + var chevronBrightness = .5f; + + var textSize = 11; + var textBrightness = .65f; + + widgetRect_groupSpace = totalRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + widthToAdd).SetHeightFromBottom(height).MoveY(-distToBottom); + + + void shadow() + { + widgetRect_groupSpace.Resize(1).DrawBlurred(Greyscale(0f, shadowAlpha), shadowSize); + } + void background() + { + widgetRect_groupSpace.DrawWithRoundedCorners(color, 1223); + } + + void nameLabel() + { + if (renamingPage) return; + + + var buttonRect = widgetRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) - 2); + + + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = !buttonRect.IsHovered() ? textBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + SetLabelAlignmentCenter(); + SetLabelFontSize(textSize); + SetLabelBold(); + + GUI.Label(widgetRect_groupSpace, data.curPage.name); + + ResetGUIColor(); + ResetLabelStyle(); + + + if (!activated) return; + + renamingPage = true; + prevPageName = data.curPage.name; + + curEvent.Use(); + + } + void leftButton() + { + if (renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize); + var buttonRect = Rect.zero.SetX(0).SetY(widgetRect_groupSpace.y).SetXMax(iconRect.xMax + 4).SetYMax(totalRect_groupSpace.yMax); + + var active = data.curPageIndex > 0; + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = !buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronLeft@2x").image); + + ResetGUIColor(); + + + + if (!activated) return; + + curEvent.Use(); + + + + if (!active) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex--; + + + } + void rightButton() + { + if (renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize); + var buttonRect = Rect.zero.SetX(iconRect.x - 6).SetY(widgetRect_groupSpace.y).SetXMax(totalRect_groupSpace.xMax).SetYMax(totalRect_groupSpace.yMax); + + var active = true;// data.curPageIndex < data.pages.Count - 1 && data.curPage.items.Any(); + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = !buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronRight@2x").image); + + ResetGUIColor(); + + + + if (!activated) return; + + curEvent.Use(); + + + + if (!active) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex++; + + } + + void nameTextField() + { + if (!renamingPage) return; + + var textFieldRect = widgetRect_groupSpace.AddHeightFromMid(-2).SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + 4); + + var s = new GUIStyle(GUI.skin.textField); + s.alignment = TextAnchor.MiddleCenter; + s.fontSize = 11; + + EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor); + + GUI.SetNextControlName("asdasdasd"); + EditorGUI.FocusTextInControl("asdasdasd"); + + + data.curPage.name = EditorGUI.TextField(textFieldRect, data.curPage.name, s); + + + if (data.curPage.name.IsNullOrEmpty()) + data.curPage.name = "Page " + (data.curPageIndex + 1); + + EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor); + + + + } + void cancelRename_button() + { + if (!renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize - 2); + var buttonRect = iconRect.SetSizeFromMid(25, 25); + + + + var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("CrossIcon").image); + + ResetGUIColor(); + + + + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + data.curPage.name = prevPageName; + + renamingPage = false; + + } + void acceptRename_button() + { + if (!renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize - 0); + var buttonRect = iconRect.SetSizeFromMid(25, 25); + + + + var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("check").image); + + ResetGUIColor(); + + + + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + renamingPage = false; + + } + void acceptRename_enterKey() + { + if (!renamingPage) return; + if (curEvent.keyCode != KeyCode.Return) return; + + renamingPage = false; + + } + void cancelRename_escapeKey() + { + if (!renamingPage) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + data.curPage.name = prevPageName; + + renamingPage = false; + + } + + + shadow(); + background(); + + leftButton(); + rightButton(); + nameLabel(); + + nameTextField(); + + cancelRename_button(); + acceptRename_button(); + + acceptRename_enterKey(); + cancelRename_escapeKey(); + + } + void keys() + { + if (isWrappedBrowserLocked && !totalRect_browserSpace.IsHovered()) return; + + void prevPage() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.LeftArrow) return; + if (!VFavoritesMenu.changePagesWithArrowsEnabled) return; + if (data.curPageIndex == 0) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex--; + + curEvent.Use(); + + } + void nextPage() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.RightArrow) return; + if (!VFavoritesMenu.changePagesWithArrowsEnabled) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex++; + + curEvent.Use(); + + } + void selectPrev() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.UpArrow) return; + if (!VFavoritesMenu.changeSelectionWithArrowsEnabled) return; + + var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) - 1; + + if (iToSelect.IsInRangeOf(data.curPage.items)) + SelectItem(data.curPage.items[iToSelect]); + + curEvent.Use(); + + } + void selectNext() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.DownArrow) return; + if (!VFavoritesMenu.changeSelectionWithArrowsEnabled) return; + + var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) + 1; + + if (iToSelect.IsInRangeOf(data.curPage.items)) + SelectItem(data.curPage.items[iToSelect]); + + curEvent.Use(); + + } + void selectByNumberKey() + { + if (!curEvent.isKeyDown) return; + if (!VFavoritesMenu.setSelectionWithNumberKeysEnabled) return; + if (EditorGUIUtility.editingTextField) return; + + var i = ((int)curEvent.keyCode - 49); + + if (!i.IsInRange(0, 9)) return; + if (!i.IsInRangeOf(data.curPage.items)) return; + + if (i == lastNumberKeyPressedIndex && EditorApplication.timeSinceStartup - lastNumberKeyPressedTime_ticks < .3f) + OpenItem(data.curPage.items[i]); + else + SelectItem(data.curPage.items[i]); + + lastNumberKeyPressedIndex = i; + lastNumberKeyPressedTime_ticks = System.DateTime.UtcNow.Ticks; + + curEvent.Use(); + + } + + prevPage(); + nextPage(); + selectPrev(); + selectNext(); + selectByNumberKey(); + + } + + void onRepaint() + { + if (!curEvent.isRepaint) return; + + + originalBrowserGUI(); + + GUI.BeginGroup(totalRect_browserSpace); + GUI.color = GUI.color.SetAlpha(currentOpacity); + + background(); + pages(); + widget(); + + GUI.color = GUI.color.SetAlpha(1); + GUI.EndGroup(); + + } + void onOtherEvents() + { + if (curEvent.isRepaint) return; + + + keys(); + + GUI.BeginGroup(totalRect_browserSpace); + + widget(); + pages(); + + GUI.EndGroup(); + + if (totalRect_browserSpace.IsHovered()) + if (curEvent.isMouseUp || curEvent.isMouseDrag || curEvent.isScroll) // prevents these events from reaching original gui + curEvent.Use(); + + originalBrowserGUI(); + + } + + void originalBrowserGUI() + { + if (origBrowserOnGUIDelegate.GetMethodInfo().DeclaringType.Name.Contains("VTabs")) return; + + if (isOneColumn && currentOpacity.Approx(1)) // to optimize locked one-column browser functioning as a dedicated favorites window + if (originalGUICalledOnce) // needs to be called once to init stuff so pinging object won't throw exceptions + return; + + origBrowserOnGUIDelegate.GetMethodInfo().Invoke(wrappedBrowser, null); + + originalGUICalledOnce = true; + + } + + + createData(); + + UpdateMouseState(); + UpdateDragging(); + UpdateAnimations(); + + onRepaint(); + onOtherEvents(); + + + if (isWrappedBrowserLocked) + if (!totalRect_browserSpace.IsHovered() && !animatingDroppedItem && !animatingPageScroll) return; + + wrappedBrowser.Repaint(); + + } + + static void SelectItem(Item item) + { + void openFolder(EditorWindow browser, string path) + { + var folderAsset = AssetDatabase.LoadAssetAtPath(path); + + if (browser.GetFieldValue("m_ViewMode") == 1) + browser.InvokeMethod("SetFolderSelection", new[] { folderAsset.GetInstanceID() }, false); + else + { + Selection.activeObject = folderAsset; + + browser.GetMemberValue("m_AssetTree")?.GetPropertyValue("data")?.InvokeMethod("SetExpanded", folderAsset.GetInstanceID(), true); + + } + + } + + void selectSceneObject() + { + if (!item.isSceneGameObject) return; + + Selection.activeObject = item.obj; + + } + void selectAsset_twoColumns() + { + if (!item.isAsset) return; + if (wrappedBrowser.GetFieldValue("m_ViewMode") != 1) return; + + openFolder(wrappedBrowser, item.assetPath.GetParentPath()); + + Selection.activeObject = item.obj; + + } + void selectAsset_oneColumn() + { + if (!item.isAsset) return; + if (wrappedBrowser.GetFieldValue("m_ViewMode") != 0) return; + + Selection.activeObject = item.obj; + + } + void openFolder_unlocked() + { + if (!item.isFolder) return; + if (isWrappedBrowserLocked) return; + + openFolder(wrappedBrowser, item.assetPath); + + } + void openFolder_locked() + { + if (!item.isFolder) return; + if (!isWrappedBrowserLocked) return; + + var unlockedBrowser = allBrowsers.FirstOrDefault(r => !r.GetMemberValue("isLocked")); + var browserToUse = isOneColumn ? unlockedBrowser : lockedBrowser; + + if (!browserToUse) return; + + openFolder(browserToUse, item.assetPath); + + } + + selectSceneObject(); + selectAsset_twoColumns(); + selectAsset_oneColumn(); + openFolder_unlocked(); + openFolder_locked(); + + item.lastSelectTime_ticks = data.curPage.lastItemSelectTime_ticks = System.DateTime.UtcNow.Ticks; + + } + static void OpenItem(Item item) + { + void openText() + { + if (item.assetPath.GetExtension() != ".cs" + && item.assetPath.GetExtension() != ".shader" + && item.assetPath.GetExtension() != ".compute" + && item.assetPath.GetExtension() != ".cginc" + && item.assetPath.GetExtension() != ".json") return; + + + AssetDatabase.OpenAsset(item.globalId.guid.LoadGuid()); + + } + void openPrefab() + { + if (item.type != typeof(GameObject)) return; + if (!item.isLoadable) return; + if ((item.obj as GameObject).scene.rootCount != 0) return; + + AssetDatabase.OpenAsset(item.obj); + + } + void openScene() + { + if (item.type != typeof(SceneAsset)) return; + + EditorSceneManager.SaveOpenScenes(); + EditorSceneManager.OpenScene(item.assetPath); + + } + void openSceneForNotLoadedGameObject() + { + if (!item.isSceneGameObject) return; + if (item.isLoadable) return; + if (item.isDeleted) return; + + EditorSceneManager.SaveOpenScenes(); + EditorSceneManager.OpenScene(item.assetPath); + + Selection.activeObject = item.obj; + + } + + openText(); + openPrefab(); + openScene(); + openSceneForNotLoadedGameObject(); + + } + + static float rowHeight => 44 * data.rowScale; + + static float crossButtonOffsetFromRight = 23; + static float crossButtonSize = 16; + + static Rect totalRect_browserSpace => isOneColumn ? wrappedBrowser.position.SetPos(0, 0) : wrappedBrowser.GetFieldValue("m_TreeViewRect"); + static Rect totalRect_groupSpace => totalRect_browserSpace.SetPos(0, 0); + + static Rect widgetRect_browserSpace => widgetRect_groupSpace.MoveY(totalRect_browserSpace.y); + static Rect widgetRect_groupSpace; + + static bool renamingPage; + static string prevPageName; + + static int lastNumberKeyPressedIndex; + static long lastNumberKeyPressedTime_ticks; + + static bool isOneColumn => wrappedBrowser?.GetFieldValue("m_ViewMode") == 0; + + static bool skipNextRepaint; + static bool originalGUICalledOnce; + + + + + + + + static void UpdateMouseState() // called from WrappedOnGUI + { + if (!shortcutPressed && !isWrappedBrowserLocked) { setDefaultState(); return; } + if (!totalRect_browserSpace.IsHovered()) { setDefaultState(); return; } + + void setDefaultState() + { + mousePressed = false; + mousePressedOnCrossButtonArea = false; + mousePressedOnWidget = false; + pressedItem = null; + doubleclickUnhandled = false; + + } + + void position() + { + mousePosiion_browserSpace = curEvent.mousePosition; + } + void down() + { + if (!curEvent.isMouseDown) return; + + mousePressed = true; + mousePressedOnCrossButtonArea = totalRect_browserSpace.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize).IsHovered(); + mousePressedOnWidget = curEvent.mousePosition.y >= widgetRect_browserSpace.y; + + mouseDownPosiion_browserSpace = curEvent.mousePosition; + + var pressedItemIndex = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt(); + if (pressedItemIndex.IsInRangeOf(data.curPage.items) && !mousePressedOnCrossButtonArea && !mousePressedOnWidget) + pressedItem = data.curPage.items[pressedItemIndex]; + + doubleclickUnhandled = !mousePressedOnCrossButtonArea && curEvent.clickCount == 2; + + curEvent.Use(); + + } + void up() + { + if (!curEvent.isMouseUp) return; + + mousePressed = false; + doubleclickUnhandled = false; + pressedItem = null; + + } + + position(); + down(); + up(); + + } + + static bool mousePressed; + static bool mousePresesdOnItem => pressedItem != null; + static bool mousePressedOnCrossButtonArea; + static bool mousePressedOnWidget; + static bool doubleclickUnhandled; + + static float groupRectOffsetY => isOneColumn ? 0 : -20; + + static Vector2 mousePosiion_browserSpace; + static Vector2 mousePosition_groupSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY); + static Vector2 mousePosition_rowsSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos); + + static Vector2 mouseDownPosiion_browserSpace; + static Vector2 mouseDownPosition_groupSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY); + static Vector2 mouseDownPosition_rowsSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos); + + static float mouseDragDistance => (mousePosiion_browserSpace - mouseDownPosiion_browserSpace).magnitude; + + static Item pressedItem; + + + + + + + static void UpdateAnimations() // called from WrappedOnGUI + { + void calcDeltaTime() + { + if (!curEvent.isLayout) return; + + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void opacity() + { + if (!curEvent.isLayout) return; + if (!VFavoritesMenu.fadeAnimationsEnabled) { currentOpacity = targetOpacity; return; } + if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) { currentOpacity = targetOpacity; return; } + + SmoothDamp(ref currentOpacity, targetOpacity, 11, ref currentOpacityDerivative, deltaTime); + + if (targetOpacity == 0 && currentOpacity < .04f) + currentOpacity = 0; + + } + void pagesScroll() + { + if (!curEvent.isLayout) return; + if (!VFavoritesMenu.pageScrollAnimationEnabled) { pagesScrollPos = data.curPageIndex; return; } + + if (pagesScrollPos == -1) + pagesScrollPos = data.curPageIndex; + + SmoothDamp(ref pagesScrollPos, data.curPageIndex, 5, ref pagesScrollDerivative, deltaTime); + + if (pagesScrollPos.DistanceTo(data.curPageIndex) < .001f) + pagesScrollPos = data.curPageIndex; + + } + void rowGaps() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = 10; + + for (int i = 0; i < data.curPage.rowGaps.Count; i++) + data.curPage.rowGaps[i] = Lerp(data.curPage.rowGaps[i], draggingItem && i == insertDraggedItemAtIndex ? rowHeight : 0, lerpSpeed, deltaTime); + + } + void droppedItem() + { + if (!curEvent.isLayout) return; + if (!animatingDroppedItem) return; + + var yLerpSpeed = 8; + var shadowLerpSpeed = 8; + var highlightLerpSpeed = 10; + + SmoothDamp(ref droppedItemY_rowsSpace, data.curPage.items.IndexOf(VFavorites.droppedItem) * rowHeight, yLerpSpeed, ref droppedItemYDerivative, deltaTime); + Lerp(ref droppedItemShadowAmount, 0, shadowLerpSpeed, deltaTime); + Lerp(ref droppedItemHighlightAmount, 0, highlightLerpSpeed, deltaTime); + + if (droppedItemShadowAmount < .01f) + animatingDroppedItem = false; + + } + + calcDeltaTime(); + opacity(); + pagesScroll(); + rowGaps(); + droppedItem(); + + } + + static void CancelRowAnimations() + { + for (int i = 0; i < data.curPage.rowGaps.Count; i++) + data.curPage.rowGaps[i] = 0; + + animatingDroppedItem = false; + droppedItem = null; + + } + + static float deltaTime; + static double lastLayoutTime; + + static float pagesScrollPos = -1; + static float pagesScrollDerivative; + static bool animatingPageScroll => !pagesScrollPos.Approx(pagesScrollPos.Round()); + + static float droppedItemY_rowsSpace; + static float droppedItemYDerivative; + static float droppedItemShadowAmount; + static float droppedItemHighlightAmount; + static bool animatingDroppedItem; + + static float currentOpacity; + static float currentOpacityDerivative; + static float targetOpacity => curEvent.holdingAlt || renamingPage || draggingItemFromPageToOutside || isWrappedBrowserLocked ? 1 : 0; // holdingAlt instead of shortcutPressed to prevent unwrapping due to incorrect event modifiers on key down on mac + + + + + + + static void UpdateDragging() // called from WrappedOnGUI + { + void initFromOutside() + { + if (draggingItem) return; + if (!totalRect_browserSpace.IsHovered()) return; + if (!curEvent.isDragUpdate) return; + if (!DragAndDrop.objectReferences.FirstOrDefault()) return; + if (draggingItemFromPageToOutside) return; // to avoid duplication when dragging item from page to outside and back + + + animatingDroppedItem = false; + + draggingItem = true; + draggingItemFromPage = false; + + draggedItem = new Item(DragAndDrop.objectReferences.FirstOrDefault()); + draggedItemHoldOffset = 0; + + data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks; + + } + void initFromPage() + { + if (draggingItem) return; + if (!totalRect_browserSpace.IsHovered()) return; + if (!curEvent.isMouseDrag) return; + if (mouseDragDistance < 2) return; + + + var i = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt(); + + if (i >= data.curPage.items.Count) return; + if (i < 0) return; // somehow i = -1 with mouseDownPosition_rowsSpace.y = -20 when dragging in from outside quickly + + + animatingDroppedItem = false; + + draggingItem = true; + draggingItemFromPage = true; + draggingItemFromPageAtIndex = i; + + draggedItem = data.curPage.items[i]; + draggedItemHoldOffset = (i * rowHeight + rowHeight / 2) - mouseDownPosition_rowsSpace.y; + + data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks; + + data.curPage.items.Remove(draggedItem); + data.curPage.rowGaps[draggingItemFromPageAtIndex] = rowHeight; + + data.Dirty(); + data.Save(); + + } + + void acceptFromOutside() + { + if (!draggingItem) return; + if (!curEvent.isDragPerform) return; + + DragAndDrop.AcceptDrag(); + curEvent.Use(); + + AcceptDragging(); + + } + void acceptFromPage() + { + if (!draggingItem) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + AcceptDragging(); + } + + void cancelFromOutside() + { + if (!draggingItemFromOutside) return; + if (totalRect_browserSpace.IsHovered()) return; + + CancelDragging(); + + } + void cancelFromPageAndInitToOutside() + { + if (!curEvent.isMouseDrag) return; + if (!draggingItemFromPage) return; + if (totalRect_browserSpace.IsHovered()) return; + if (DragAndDrop.objectReferences.Any()) return; + + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { draggedItem.obj }; + DragAndDrop.StartDrag(draggedItem.name); + + CancelDragging(); + + draggingItemFromPageToOutside = true; + + } + + void setVisualMode() + { + if (!draggingItem) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + } + void setHotControl() + { + if (!draggingItem) return; + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + + void resetDraggingItemFromPageToOutside() // delayCall loop + { + if (!DragAndDrop.objectReferences.Any()) + draggingItemFromPageToOutside = false; + + EditorApplication.delayCall -= resetDraggingItemFromPageToOutside; + EditorApplication.delayCall += resetDraggingItemFromPageToOutside; + + // DragAndDrop.objectReferences is unreliable outside of delayCall + + } + + + initFromOutside(); + initFromPage(); + + acceptFromOutside(); + acceptFromPage(); + + cancelFromOutside(); + cancelFromPageAndInitToOutside(); + + setVisualMode(); + setHotControl(); + + EditorApplication.delayCall -= resetDraggingItemFromPageToOutside; + EditorApplication.delayCall += resetDraggingItemFromPageToOutside; + + } + + static void AcceptDragging() + { + draggingItem = false; + draggingItemFromPage = false; + mousePressed = false; + + data.curPage.items.AddAt(draggedItem, insertDraggedItemAtIndex); + + data.curPage.rowGaps[insertDraggedItemAtIndex] -= rowHeight; + data.curPage.rowGaps.AddAt(0, insertDraggedItemAtIndex); + + droppedItem = draggedItem; + + droppedItemY_rowsSpace = draggedItemY_groupSpace + data.curPage.scrollPos; + droppedItemYDerivative = 0; + droppedItemShadowAmount = droppedItemHighlightAmount = 1; + animatingDroppedItem = true; + + draggedItem = null; + + EditorGUIUtility.hotControl = 0; + + data.Dirty(); + data.Save(); + + } + static void CancelDragging() + { + if (!draggingItem) return; + + draggingItem = false; + mousePressed = false; + + + if (!draggingItemFromPage) { draggedItem = null; return; } + + data.curPage.items.AddAt(draggedItem, draggingItemFromPageAtIndex); + + data.curPage.rowGaps[draggingItemFromPageAtIndex] -= rowHeight; + droppedItem = draggedItem; + droppedItemY_rowsSpace = draggedItemY_groupSpace - data.curPage.scrollPos; + droppedItemShadowAmount = droppedItemHighlightAmount = 1; + animatingDroppedItem = true; + + draggingItemFromPage = false; + + draggedItem = null; + + EditorGUIUtility.hotControl = 0; + + data.Dirty(); + data.Save(); + + } + + static bool draggingItem; + static bool draggingItemFromPage; + static bool draggingItemFromPageToOutside; + static bool draggingItemFromOutside => draggingItem && !draggingItemFromPage; + static int draggingItemFromPageAtIndex; + static Item draggedItem; + static float draggedItemHoldOffset; + static float draggedItemY_groupSpace => (mousePosition_groupSpace.y - rowHeight / 2 + draggedItemHoldOffset).Clamp(0, 12321); + static float draggedItemY_rowsSpace => draggedItemY_groupSpace + data.curPage.scrollPos; + static int insertDraggedItemAtIndex => ((mousePosition_rowsSpace.y + draggedItemHoldOffset) / rowHeight).FloorToInt().Clamp(0, data.curPage.items.Count); + + static Item droppedItem; + + + + + + + + + + + + + + + static void UpdateGUIWrapping() // called from EditorApplicaton.update + { + void wrap() + { + if (wrappedBrowser) return; + if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) return; + if (!shortcutPressed) return; + if (EditorWindow.mouseOverWindow?.GetType() != t_BrowserWindow) return; + if (t_VTabs != null && !EditorPrefs.GetBool("vFavorites-pluginDisabled", false) && EditorWindow.mouseOverWindow.GetMemberValue("isLocked")) return; + + + WrapBrowserGUI(EditorWindow.mouseOverWindow); + + wrappedBrowser = EditorWindow.mouseOverWindow; + + wrappedBrowser.Focus(); + wrappedBrowser.Repaint(); + + t_BrowserWindow.SetFieldValue("s_LastInteractedProjectBrowser", wrappedBrowser); // so vTabs can copy its layout setting + + } + void unwrap() + { + if (!wrappedBrowser) return; + if (shortcutPressed && wrappedBrowser.hasFocus) return; + if (currentOpacity > 0 && wrappedBrowser.hasFocus) return; + + + CancelDragging(); + CancelRowAnimations(); + + UnwrapBrowserGUI(); + + wrappedBrowser.Repaint(); + + wrappedBrowser = null; + + } + + unwrap(); + wrap(); + + } + + static void WrapBrowserGUI(EditorWindow browser) + { + var hostView = fi_m_Parent.GetValue(browser); + var newDelegate = mi_OnGUIOverride.CreateDelegate(t_EditorWindowDelegate, hostView); + + origBrowserOnGUIDelegate = fi_m_OnGUI.GetValue(hostView) as System.Delegate; + fi_m_OnGUI.SetValue(hostView, newDelegate); + + } + static void UnwrapBrowserGUI() + { + if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue("m_OnGUI").Method.Name != nameof(WrappedOnGUI)) return; + + wrappedBrowser.GetFieldValue("m_Parent").SetFieldValue("m_OnGUI", origBrowserOnGUIDelegate); + + } + + static EditorWindow wrappedBrowser; + static System.Delegate origBrowserOnGUIDelegate; + + static bool shortcutPressed + { + get + { + if (VFavoritesMenu.activeOnAltEnabled) + return curEvent.holdingAlt; + + if (VFavoritesMenu.activeOnAltShiftEnabled) + return curEvent.modifiers == (EventModifiers.Alt | EventModifiers.Shift); + + if (VFavoritesMenu.activeOnCtrlAltEnabled) + if (Application.platform == RuntimePlatform.OSXEditor) + return curEvent.modifiers == (EventModifiers.Command | EventModifiers.Alt); + else + return curEvent.modifiers == (EventModifiers.Control | EventModifiers.Alt); + + return false; + } + } + + + + + + + static void UpdateLocking() // called from EditorApplicaton.update + { + void unsetWrappedBrowser() + { + if (!isWrappedBrowserLocked) return; + + if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue("m_OnGUI").Method.Name == nameof(WrappedOnGUI)) return; + + wrappedBrowser = null; + + // nulls wrappedBrowser if it was unwrapped externally + // ie when locked browser gets moved or maximized + + } + void markLockedBrowser() + { + if (!lockedBrowser) return; + + MarkAsLocked(lockedBrowser); + + // fixes marking getting reset on scene load + + } + void setMinWidthOnLockedBrowser() + { + if (!lockedBrowser) return; + if (lockedBrowser.minSize.x == 100) return; + + lockedBrowser.minSize = Vector2.one * 100; + + } + + void lock_() + { + if (lockedBrowser) return; + if (!wrappedBrowser) return; + if (!wrappedBrowser.GetMemberValue("isLocked")) return; + + lockedBrowser = wrappedBrowser; + + EditorPrefs.SetInt("vFavorites-lockedBrowserHash", lockedBrowser.GetHashCode()); + EditorPrefs.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", lockedBrowser.GetMemberValue("m_Parent").GetInstanceID()); + + curEvent.Use(); + + } + void unlock() + { + if (!lockedBrowser) return; + if (lockedBrowser.GetMemberValue("isLocked")) return; + + + lockedBrowser = null; + + EditorPrefs.SetInt("vFavorites-lockedBrowserHash", 0); + EditorPrefs.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", 0); + + curEvent.Use(); + + } + void wrap() + { + if (!lockedBrowser) return; + if (!lockedBrowser.hasFocus) return; + if (isWrappedBrowserLocked) return; + + WrapBrowserGUI(lockedBrowser); + + currentOpacity = 1; + + wrappedBrowser = lockedBrowser; + + } + + + unsetWrappedBrowser(); + markLockedBrowser(); + setMinWidthOnLockedBrowser(); + + lock_(); + unlock(); + wrap(); + + } + + static EditorWindow lockedBrowser + { + get + { + if (_lockedBrowser) return _lockedBrowser; + + + var lockedBrowserInstanceId = EditorPrefs.GetInt("vFavorites-lockedBrowserInstanceId", 0); + + if (lockedBrowserInstanceId == 0) return null; + + + + var window = Resources.InstanceIDToObject(lockedBrowserInstanceId) as EditorWindow; + + if (window && window.GetType() == t_BrowserWindow) // prevents iid collisions + _lockedBrowser = window; + + if (_lockedBrowser) return _lockedBrowser; + + + + _lockedBrowser = allBrowsers.FirstOrDefault(r => IsMarkedAsLocked(r)); + + if (_lockedBrowser) return _lockedBrowser; // todo set instanceid + + + + // EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", 0); // one attempt to find the locked browser isn't enough after unmaximize + + return null; + + } + set + { + if (_lockedBrowser) + MarkAsUnlocked(_lockedBrowser); + + if (value == null) + { + EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", 0); + + _lockedBrowser = null; + + } + else + { + EditorPrefs.SetInt("vFavorites-lockedBrowserInstanceId", value.GetInstanceID()); + + MarkAsLocked(value); + + _lockedBrowser = value; + + } + + } + } + static EditorWindow _lockedBrowser; + + static bool IsMarkedAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.GetMemberValue("m_OriginalText") == "asd"; + static void MarkAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", "asd"); + static void MarkAsUnlocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", ""); + + static bool isWrappedBrowserLocked => wrappedBrowser && wrappedBrowser == lockedBrowser; + + static bool CanBrowserBeWrapped_byVTabs(EditorWindow browser) => !IsMarkedAsLocked(browser); + + + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VFavoritesMenu.pluginDisabled) return; + + void subscribe() + { + EditorApplication.update -= UpdateGUIWrapping; + EditorApplication.update += UpdateGUIWrapping; + + EditorApplication.update -= UpdateLocking; + EditorApplication.update += UpdateLocking; + + EditorApplication.quitting -= VFavoritesState.Save; + EditorApplication.quitting += VFavoritesState.Save; + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString("vFavorites-lastKnownDataPath-" + GetProjectId())); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VFavoritesData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + EditorPrefs.SetString("vFavorites-lastKnownDataPath-" + GetProjectId(), data.GetPath()); + + } + void loadDataDelayed() + { + if (data) return; + + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + + subscribe(); + loadData(); + loadDataDelayed(); + + } + + public static VFavoritesData data; + + + + + + + + static void BeforeWindowCreated_byVTabs(object dockArea) + { + if (!wrappedBrowser) return; + if (wrappedBrowser.GetFieldValue("m_Parent") != dockArea) return; + + CancelDragging(); + CancelRowAnimations(); + + UnwrapBrowserGUI(); + + draggingItemFromPageToOutside = false; + + } + + + + + + + static IEnumerable allBrowsers => _allBrowsers ??= t_BrowserWindow.GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allBrowsers; + + static Type t_BrowserWindow = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView"); + static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags); + static FieldInfo fi_m_Parent = typeof(EditorWindow).GetField("m_Parent", maxBindingFlags); + static FieldInfo fi_m_OnGUI = t_HostView.GetField("m_OnGUI", maxBindingFlags); + static MethodInfo mi_OnGUIOverride = typeof(VFavorites).GetMethod(nameof(WrappedOnGUI), maxBindingFlags); + + + static Type t_VHierarchy = Type.GetType("VHierarchy.VHierarchy") ?? Type.GetType("VHierarchy.VHierarchy, VHierarchy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFolders = Type.GetType("VFolders.VFolders") ?? Type.GetType("VFolders.VFolders, VFolders, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VTabs = Type.GetType("VTabs.VTabs") ?? Type.GetType("VTabs.VTabs, VTabs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + static MethodInfo mi_VHierarchy_GetIconName = t_VHierarchy?.GetMethod("GetIconName_forVFavorites", maxBindingFlags); + static MethodInfo mi_VFolders_DrawBigFolderIcon = t_VFolders?.GetMethod("DrawBigFolderIcon_forVFavorites", maxBindingFlags); + + + + + + const string version = "2.0.6"; + + } +} +#endif diff --git a/Assets/Third Parties/vFavorites/VFavorites.cs.meta b/Assets/Third Parties/vFavorites/VFavorites.cs.meta new file mode 100644 index 00000000..64e3f6a4 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavorites.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6651039474d594cdd91489768cf293f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavoritesData.cs b/Assets/Third Parties/vFavorites/VFavoritesData.cs new file mode 100644 index 00000000..c2dbe74c --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesData.cs @@ -0,0 +1,225 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFavorites.VFavoritesState; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + + +namespace VFavorites +{ + public class VFavoritesData : ScriptableObject + { + public List pages = new List(); + + public Page curPage + { + get + { + while (curPageIndex >= pages.Count - 1) + pages.Add(new Page("Page " + (pages.Count + 1))); + + return pages[curPageIndex]; + + } + } + + public int curPageIndex { get => VFavoritesState.instance.curPageIndex; set => VFavoritesState.instance.curPageIndex = value; } + + public float rowScale = 1; + + + [System.Serializable] + public class Page + { + public List items = new List(); + + public string name = ""; + + public Page(string name) => this.name = name; + + + + + [System.NonSerialized] + public List _rowGaps = new List(); + public List rowGaps + { + get + { + if (_rowGaps == null) + _rowGaps = new List(); + + while (_rowGaps.Count < items.Count + 1) _rowGaps.Add(0); + while (_rowGaps.Count > items.Count + 1) _rowGaps.RemoveLast(); + + return _rowGaps; + + } + } + + + public float scrollPos { get => state.scrollPos; set => state.scrollPos = value; } + + public long lastItemSelectTime_ticks { get => state.lastItemSelectTime_ticks; set => state.lastItemSelectTime_ticks = value; } + public long lastItemDragTime_ticks { get => state.lastItemDragTime_ticks; set => state.lastItemDragTime_ticks = value; } + + + public PageState state + { + get + { + if (!VFavoritesState.instance.pageStates_byPageId.ContainsKey(id)) + VFavoritesState.instance.pageStates_byPageId[id] = new PageState(); + + return VFavoritesState.instance.pageStates_byPageId[id]; + + } + } + + public int id + { + get + { + if (_id == 0) + _id = Random.value.GetHashCode(); + + return _id; + + } + } + public int _id = 0; + + } + + [System.Serializable] + public class Item + { + public GlobalID globalId; + + + public Type type => Type.GetType(_typeString) ?? typeof(DefaultAsset); + public string _typeString; + + public Object obj => _obj != null ? _obj : (_obj = globalId.GetObject()); + public Object _obj; + + + public bool isSceneGameObject; + public bool isFolder; + public bool isAsset; + + + public bool isLoadable => obj != null; + + public bool isDeleted + { + get + { + if (!isSceneGameObject) + return !isLoadable; + + if (isLoadable) + return false; + + if (!AssetDatabase.LoadAssetAtPath(globalId.guid.ToPath())) + return true; + + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).path == globalId.guid.ToPath()) + return true; + + return false; + + } + } + + public string assetPath => globalId.guid.ToPath(); + + + public Item(Object o) + { + globalId = o.GetGlobalID(); + + + isSceneGameObject = o is GameObject go && go.scene.rootCount != 0; + isFolder = AssetDatabase.IsValidFolder(o.GetPath()); + isAsset = !isSceneGameObject && !isFolder; + + _typeString = o.GetType().AssemblyQualifiedName; + + _name = o.name; + + } + + + + + + public string name + { + get + { + if (!isLoadable) return _name; + + if (assetPath.GetExtension() == ".cs") + _name = obj.name.Decamelcase(); + else + _name = obj.name; + + return _name; + + } + } + public string _name { get => state._name; set => state._name = value; } + + public string sceneGameObjectIconName { get => state.sceneGameObjectIconName; set => state.sceneGameObjectIconName = value; } + + public long lastSelectTime_ticks { get => state.lastSelectTime_ticks; set => state.lastSelectTime_ticks = value; } + public bool isSelected { get => state.isSelected; set => state.isSelected = value; } + + + + public ItemState state + { + get + { + if (!VFavoritesState.instance.itemStates_byItemId.ContainsKey(id)) + VFavoritesState.instance.itemStates_byItemId[id] = new ItemState(); + + return VFavoritesState.instance.itemStates_byItemId[id]; + + } + } + + public int id + { + get + { + if (_id == 0) + _id = Random.value.GetHashCode(); + + return _id; + + } + } + public int _id = 0; + + + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFavorites/VFavoritesData.cs.meta b/Assets/Third Parties/vFavorites/VFavoritesData.cs.meta new file mode 100644 index 00000000..92b497a0 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 066cf82f8f80d408c856e48fc8f1127b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavoritesLibs.cs b/Assets/Third Parties/vFavorites/VFavoritesLibs.cs new file mode 100644 index 00000000..da67d641 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesLibs.cs @@ -0,0 +1,1740 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using UnityEngine.UIElements; +using Type = System.Type; +using static VFavorites.Libs.VUtils; + + +namespace VFavorites.Libs +{ + public static class VUtils + { + #region Text + + + public static string Decamelcase(this string str) => Regex.Replace(Regex.Replace(str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2"); + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + + + + + #endregion + + #region IEnumerables + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + public static int LastIndex(this List l) => l.Count - 1; // toremove + + // public static T GetAtWrapped(this List list, int i) // toremove + // { + // while (i < 0) i += list.Count; + // while (i >= list.Count) i -= list.Count; + + // return list[i]; + // } + + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + public static bool None(this IEnumerable ie, System.Func f) => !ie.Any(f); + public static bool None(this IEnumerable ie) => !ie.Any(); + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + + public static void ForEach(this IEnumerable sequence, System.Action action) { foreach (T item in sequence) action(item); } + + + + + + #endregion + + #region Reflection + + public static object GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + + public static void SetFieldValue(this object o, string fieldName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) // todo handle null params (can't get their type) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in type '{type.Name}', its parent types and interfaces"); + + } + + + + static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + static Dictionary> fieldInfoCache = new Dictionary>(); + + static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + static Dictionary> propertyInfoCache = new Dictionary>(); + + static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + static Dictionary> methodInfoCache = new Dictionary>(); + + + + public static T GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) => (T)o.GetFieldValue(fieldName, exceptionIfNotFound); + public static T GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) => (T)o.GetPropertyValue(propertyName, exceptionIfNotFound); + public static T GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) => (T)o.GetMemberValue(memberName, exceptionIfNotFound); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + #endregion + + #region Math + + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + public static float DistanceTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float DistanceTo(this Vector2 f1, Vector2 f2) => (f1 - f2).magnitude; + public static float DistanceTo(this Vector3 f1, Vector3 f2) => (f1 - f2).magnitude; + public static float Dist(float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Avg(float f1, float f2) => (f1 + f2) / 2; + public static float Abs(this float f) => Mathf.Abs(f); + public static int Abs(this int f) => Mathf.Abs(f); + public static float Sign(this float f) => Mathf.Sign(f); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new Vector2(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new Vector3(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + public static float ToFloat(this double f) => (float)f; + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + + + public static Color Greyscale(float brightness, float alpha = 1) => new Color(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Vectors + + + public static Vector2 AddX(this Vector2 v, float f) => new Vector2(v.x + f, v.y + 0); + public static Vector2 AddY(this Vector2 v, float f) => new Vector2(v.x + 0, v.y + f); + + public static Vector3 AddX(this Vector3 v, float f) => new Vector3(v.x + f, v.y + 0, v.z + 0); + public static Vector3 AddY(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + f, v.z + 0); + public static Vector3 AddZ(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + 0, v.z + f); + + public static Vector2 xx(this Vector3 v) { return new Vector2(v.x, v.x); } + public static Vector2 xy(this Vector3 v) { return new Vector2(v.x, v.y); } + public static Vector2 xz(this Vector3 v) { return new Vector2(v.x, v.z); } + public static Vector2 yx(this Vector3 v) { return new Vector2(v.y, v.x); } + public static Vector2 yy(this Vector3 v) { return new Vector2(v.y, v.y); } + public static Vector2 yz(this Vector3 v) { return new Vector2(v.y, v.z); } + public static Vector2 zx(this Vector3 v) { return new Vector2(v.z, v.x); } + public static Vector2 zy(this Vector3 v) { return new Vector2(v.z, v.y); } + public static Vector2 zz(this Vector3 v) { return new Vector2(v.z, v.z); } + + + + + + #endregion + + #region Compute + + + [System.Serializable] + public class GaussianKernel + { + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + } + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && globalObjectIdString != null && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + } + + public static GlobalID GetGlobalID(this Object o) => new GlobalID(o); + + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + } + + +#endif + + + + + #endregion + + #region Paths + + + public static string GetParentPath(this string path) => path.Substring(0, path.LastIndexOf('/')); + public static bool HasParentPath(this string path) => path.Contains('/') && path.GetParentPath() != ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + public static string CombinePath(this string p, string p2) => Path.Combine(p, p2); + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + public static bool IsValidGuid(this string guid) => AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(guid), AssetPathToGUIDOptions.OnlyExistingAssets) != ""; + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + +#endif + + + + + + #endregion + + #region Serialization + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + [SerializeField] List keys = new List(); + [SerializeField] List values = new List(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + public static void ToggleDefineDisabledInScript(Type scriptType) + { + var path = GetScriptPath(scriptType.Name); + + var lines = File.ReadAllLines(path); + if (lines.First().StartsWith("#define DISABLED")) + File.WriteAllLines(path, lines.Skip(1)); + else + File.WriteAllLines(path, lines.Prepend("#define DISABLED // this line was added by VUtils.ToggleDefineDisabledInScript")); + + AssetDatabase.ImportAsset(path); + } + public static bool ScriptHasDefineDisabled(Type scriptType) => File.ReadLines(GetScriptPath(scriptType.Name)).First().StartsWith("#define DISABLED"); + public static void SetDefineDisabledInScript(Type scriptType, bool defineDisabled) + { + if (ScriptHasDefineDisabled(scriptType) != defineDisabled) + ToggleDefineDisabledInScript(scriptType); + + } + + public static int GetProjectId() => Application.dataPath.GetHashCode(); + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); + public static void RecordUndo(this Object o) => Undo.RecordObject(o, ""); + + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + + public static void RemoveEditorErrors() => removeEditorErrorsMethod.Invoke(null, new object[] { 1 }); + static MethodInfo removeEditorErrorsMethod = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(r => r.GetName().ToString().Contains("UnityEditor.CoreModule")).GetTypes().First(r => r.Name.Contains("LogEntry")).GetMethod("RemoveLogEntriesByMode", BindingFlags.Static | BindingFlags.NonPublic); + + +#endif + + + + + + #endregion + + } + + public static partial class VGUI + { + #region Colors + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + #endregion + + #region Shortcuts + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fotSize) + { + SetLabelFontSize(fotSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + + public static void SetGUIColor(Color c) + { + if (!_guiColorModified) + _defaultGuiColor = GUI.color; + + _guiColorModified = true; + + GUI.color = _defaultGuiColor * c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorModified ? _defaultGuiColor : Color.white; + + _guiColorModified = false; + + } + static bool _guiColorModified; + static Color _defaultGuiColor; + + + + #endregion + + #region Events + + public struct WrappedEvent + { + public Event e; + + public bool isNull => e == null; + public bool isRepaint => isNull ? default : e.type == EventType.Repaint; + public bool isLayout => isNull ? default : e.type == EventType.Layout; + public bool isUsed => isNull ? default : e.type == EventType.Used; + public bool isMouseLeaveWindow => isNull ? default : e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => isNull ? default : e.type == EventType.MouseEnterWindow; + public bool isContextClick => isNull ? default : e.type == EventType.ContextClick; + + public bool isKeyDown => isNull ? default : e.type == EventType.KeyDown; + public bool isKeyUp => isNull ? default : e.type == EventType.KeyUp; + public KeyCode keyCode => isNull ? default : e.keyCode; + public char characted => isNull ? default : e.character; + + public bool isExecuteCommand => isNull ? default : e.type == EventType.ExecuteCommand; + public string commandName => isNull ? default : e.commandName; + + public bool isMouse => isNull ? default : e.isMouse; + public bool isMouseDown => isNull ? default : e.type == EventType.MouseDown; + public bool isMouseUp => isNull ? default : e.type == EventType.MouseUp; + public bool isMouseDrag => isNull ? default : e.type == EventType.MouseDrag; + public bool isMouseMove => isNull ? default : e.type == EventType.MouseMove; + public bool isScroll => isNull ? default : e.type == EventType.ScrollWheel; + public int mouseButton => isNull ? default : e.button; + public int clickCount => isNull ? default : e.clickCount; + public Vector2 mousePosition => isNull ? default : e.mousePosition; + public Vector2 mousePosition_screenSpace => isNull ? default : GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => isNull ? default : e.delta; + + public bool isDragUpdate => isNull ? default : e.type == EventType.DragUpdated; + public bool isDragPerform => isNull ? default : e.type == EventType.DragPerform; + public bool isDragExit => isNull ? default : e.type == EventType.DragExited; + + public EventModifiers modifiers => isNull ? default : e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => isNull ? default : e.alt; + public bool holdingShift => isNull ? default : e.shift; + public bool holdingCtrl => isNull ? default : e.control; + public bool holdingCmd => isNull ? default : e.command; + public bool holdingCmdOrCtrl => isNull ? default : e.command || e.control; + + public bool holdingAltOnly => isNull ? default : e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => isNull ? default : e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => isNull ? default : e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => isNull ? default : e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => isNull ? default : (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new WrappedEvent(e); + public static WrappedEvent curEvent => (Event.current ?? _fi_s_Current.GetValue(null) as Event).Wrap(); + static FieldInfo _fi_s_Current = typeof(Event).GetField("s_Current", maxBindingFlags); + + + + + + #endregion + + #region Layout + + + + public static void BeginPanel(string title, float height, System.Action onClose = null, System.Action onApply = null) + { + + void bg() + { + GUI.enabled = false; + SetGUIColor(Greyscale(.75f)); + var r = ExpandWidthLabelRect(0).SetHeight(height); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + ResetGUIColor(); + GUI.enabled = true; + } + + void layout() + { + + GUILayout.BeginHorizontal(); + Space(7); + EditorGUIUtility.labelWidth -= 7; + GUILayout.BeginVertical(); + + } + + void title_() + { + Space(5); + EditorGUI.PrefixLabel(ExpandWidthLabelRect(), new GUIContent(title)); + // GUI.skin.label.fontStyle = FontStyle.Bold; + // GUILayout.Label(title); + + } + + void buttons() + { + var rClose = lastRect.SetWidthFromRight(16).MoveX(1).MoveY(-1); + + // if() + GUI.color = Greyscale(rClose.IsHovered() ? .9f : .6f); + GUI.Label(rClose, EditorGUIUtility.IconContent("CrossIcon")); + + GUI.color = Color.clear; + if (GUI.Button(rClose, "")) + onClose?.Invoke(); + + + var rApply = rClose.MoveX(-19).Resize(-1); + + GUI.color = Greyscale(rApply.IsHovered() ? .9f : .6f); + GUI.Label(rApply, EditorGUIUtility.IconContent("check")); + + GUI.color = Color.clear; + if (GUI.Button(rApply, "")) + onApply?.Invoke(); + + + GUI.color = Color.white; + + } + + + + bg(); + layout(); + title_(); + buttons(); + + Space(5); + + } + + public static void EndPanel() + { + GUILayout.EndVertical(); + Space(7); + EditorGUIUtility.labelWidth = 0; + GUILayout.EndHorizontal(); + } + + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new Stack(); + + + + + #endregion + + #region Drawing + + public static Rect Draw(this Rect r) { EditorGUI.DrawRect(r, Color.black); return r; } + public static Rect Draw(this Rect r, Color c) { EditorGUI.DrawRect(r, c); return r; } + + + + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, float cornerRadius) => rect.DrawWithRoundedCorners(color, cornerRadius.RoundToInt()); + static Dictionary _roundedStylesByCornerRadius = new Dictionary(); + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = new GaussianKernel(false, blurRadiusScaled).Array2d(); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new Dictionary<(int, int), GUIStyle>(); + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + static Texture2D[] _gradientTextures; + + + + public static bool IsHovered(this Rect r) => !curEvent.isNull && r.Contains(curEvent.mousePosition); + + #endregion + + #region Spacing + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static void Divider(float space = 15, float yOffset = 0) + { + GUILayout.Label("", GUILayout.Height(space), GUILayout.ExpandWidth(true)); + lastRect.SetHeightFromMid(1).SetWidthFromMid(lastRect.width - 16).MoveY(yOffset).Draw(isDarkTheme ? Color.white * .42f : Color.white * .72f); + } + + public static Rect ExpandSpace() { GUILayout.Label("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); return lastRect; } + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + #endregion + + #region Icons + + public static class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath) + { + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult)) return cachedResult; + + var icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + return icons_byName[iconNameOrPath] = icon; + + } + + static Dictionary icons_byName = new Dictionary(); + } + + + + + // toremove: + + public static void DrawIcon(Rect rect, string icon, Color? col = null) + { + if (icon == "") return; + Texture2D tex = (Texture2D)EditorGUIUtility.FindTexture(icon); + if (!tex) tex = (Texture2D)EditorGUIUtility.Load(icon); + if (!tex) tex = (Texture2D)Resources.Load(icon); + DrawIcon(rect, tex, col); + } + public static void DrawIcon(Rect rect, string icon, bool greyedOut) => DrawIcon(rect, icon, greyedOut ? GUIColors.greyedOutTint : (Color?)null); + + public static void DrawIcon(Rect rect, Texture2D tex, Color? col = null) + { + var color = Color.white; + if (col != null) color = col.GetValueOrDefault(); + + GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, color, 0, 0); + } + public static void DrawIcon(Rect rect, Texture2D tex, bool greyedOut) => DrawIcon(rect, tex, greyedOut ? GUIColors.greyedOutTint : (Color?)null); + + public static void DrawIconForGuid(Rect rect, string guid, bool greyedOut = false) + { + if (!guid.IsValidGuid()) return; + + var type = AssetDatabase.GetMainAssetTypeAtPath(guid.ToPath()); + + if (AssetDatabase.IsValidFolder(guid.ToPath())) + DrawIcon(rect, "Folder Icon", greyedOut); + + else if (guid.ToPath().GetExtension() == ".cs") + DrawIcon(rect, "cs Script Icon", greyedOut); + + else DrawIcon(rect, AssetPreview.GetMiniTypeThumbnail(type), greyedOut); + } + + // public static void DrawIcon(string icon, bool greyedOut = false, bool pressed = false) => DrawIcon(lastRect, icon, greyedOut, pressed); + // public static void DrawIcon(Rect rect, string icon, bool greyedOut = false, bool pressed = false) + // { + // if (icon == "") return; + // Texture2D tex = (Texture2D)EditorGUIUtility.FindTexture(icon); + // if (!tex) tex = (Texture2D)EditorGUIUtility.Load(icon); + // if (!tex) tex = (Texture2D)Resources.Load(icon); + // DrawIcon(rect, tex, greyedOut, pressed); + // } + // public static void DrawIcon(Rect rect, GUIContent tex, bool greyedOut = false) //some icons get fucked up if drawn differently + // { + // var r = GUI.enabled; + // if (greyedOut) GUI.enabled = false; + // var s = new GUIStyle(); + // GUI.Label(rect.Resize(0), tex, s); + // GUI.enabled = r; + // } + // public static void DrawIcon(Rect rect, Texture2D tex, bool greyedOut = false, bool pressed = false) + // { + // var col = Color.white; + // if (greyedOut) col *= greyedOutColor; + // if (pressed) col *= pressedCol; + // GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, col, 0, 0); + // } + // public static void DrawIcon(Rect rect, string icon, Color col) => DrawIcon(lastRect, EditorGUIUtility.FindTexture(icon), col); + // public static void DrawIcon(Rect rect, Texture2D tex, Color col) => GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, col, 0, 0); + + + + #endregion + + #region Other + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + #endregion + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFavorites/VFavoritesLibs.cs.meta b/Assets/Third Parties/vFavorites/VFavoritesLibs.cs.meta new file mode 100644 index 00000000..a0d75079 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38092f82a4c054086826261d88277942 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavoritesMenu.cs b/Assets/Third Parties/vFavorites/VFavoritesMenu.cs new file mode 100644 index 00000000..03dc2dc7 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesMenu.cs @@ -0,0 +1,127 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + + +namespace VFavorites +{ + public class VFavoritesMenu + { + + public static bool changePagesWithArrowsEnabled { get => EditorPrefs.GetBool("vFavorites-changePagesWithArrowsEnabled", true); set => EditorPrefs.SetBool("vFavorites-changePagesWithArrowsEnabled", value); } + public static bool changeSelectionWithArrowsEnabled { get => EditorPrefs.GetBool("vFavorites-changeSelectionWithArrowsEnabled", true); set => EditorPrefs.SetBool("vFavorites-changeSelectionWithArrowsEnabled", value); } + public static bool setSelectionWithNumberKeysEnabled { get => EditorPrefs.GetBool("vFavorites-setSelectionWithNumberKeysEnabled", true); set => EditorPrefs.SetBool("vFavorites-setSelectionWithNumberKeysEnabled", value); } + + public static bool fadeAnimationsEnabled { get => EditorPrefs.GetBool("vFavorites-fadeAnimationsEnabled", true); set => EditorPrefs.SetBool("vFavorites-fadeAnimationsEnabled", value); } + public static bool pageScrollAnimationEnabled { get => EditorPrefs.GetBool("vFavorites-pageScrollAnimationEnabled", true); set => EditorPrefs.SetBool("vFavorites-pageScrollAnimationEnabled", value); } + + public static int activeOnKeyCombination { get => EditorPrefs.GetInt("vFavorites-activeOnKeyCombination", 0); set => EditorPrefs.SetInt("vFavorites-activeOnKeyCombination", value); } + public static bool activeOnAltEnabled { get => activeOnKeyCombination == 0; set => activeOnKeyCombination = 0; } + public static bool activeOnAltShiftEnabled { get => activeOnKeyCombination == 1; set => activeOnKeyCombination = 1; } + public static bool activeOnCtrlAltEnabled { get => activeOnKeyCombination == 2; set => activeOnKeyCombination = 2; } + + public static bool pluginDisabled { get => EditorPrefs.GetBool("vFavorites-pluginDisabled", false); set => EditorPrefs.SetBool("vFavorites-pluginDisabled", value); } + + + + + const string dir = "Tools/vFavorites/"; + + const string changePagesWithArrows = dir + "Change pages with Left or Right arrow keys"; + const string changeSelectionWithArrows = dir + "Change selection with Up or Down arrow keys"; + const string setSelectionWithNumberKeys = dir + "Set selection with Number keys"; + + const string fadeAnimations = dir + "Fade animations"; + const string pageScrollAnimation = dir + "Page scroll animation"; + + const string activeOnAlt = dir + "Holding Alt"; + const string activeOnAltShift = dir + "Holding Alt and Shift"; +#if UNITY_EDITOR_OSX + const string activeOnCtrlAlt = dir + "Holding Cmd and Alt"; +#else + const string activeOnCtrlAlt = dir + "Holding Ctrl and Alt"; + +#endif + + const string disablePlugin = dir + "Disable vFavorites"; + + + + + + + [MenuItem(dir + "Features", false, 1)] static void dadsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsas123() => false; + + [MenuItem(changePagesWithArrows, false, 2)] static void dadsadadsas() => changePagesWithArrowsEnabled = !changePagesWithArrowsEnabled; + [MenuItem(changePagesWithArrows, true, 2)] static bool dadsaddasadsas() { Menu.SetChecked(changePagesWithArrows, changePagesWithArrowsEnabled); return !pluginDisabled; } + + [MenuItem(changeSelectionWithArrows, false, 3)] static void dadsadaddassas() => changeSelectionWithArrowsEnabled = !changeSelectionWithArrowsEnabled; + [MenuItem(changeSelectionWithArrows, true, 2)] static bool dadadssaddasadsas() { Menu.SetChecked(changeSelectionWithArrows, changeSelectionWithArrowsEnabled); return !pluginDisabled; } + + [MenuItem(setSelectionWithNumberKeys, false, 4)] static void dadsadasdadsas() => setSelectionWithNumberKeysEnabled = !setSelectionWithNumberKeysEnabled; + [MenuItem(setSelectionWithNumberKeys, true, 4)] static bool dadsadadsdasadsas() { Menu.SetChecked(setSelectionWithNumberKeys, setSelectionWithNumberKeysEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Animations", false, 101)] static void dadsadsas() { } + [MenuItem(dir + "Animations", true, 101)] static bool dadadssas123() => false; + + [MenuItem(fadeAnimations, false, 102)] static void dadsdasadadsas() => fadeAnimationsEnabled = !fadeAnimationsEnabled; + [MenuItem(fadeAnimations, true, 102)] static bool dadsadadsadsdasadsas() { Menu.SetChecked(fadeAnimations, fadeAnimationsEnabled); return !pluginDisabled; } + + [MenuItem(pageScrollAnimation, false, 103)] static void dadsdasdasadadsas() => pageScrollAnimationEnabled = !pageScrollAnimationEnabled; + [MenuItem(pageScrollAnimation, true, 103)] static bool dadsadaddassadsdasadsas() { Menu.SetChecked(pageScrollAnimation, pageScrollAnimationEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Open when", false, 1001)] static void dadsaddssas() { } + [MenuItem(dir + "Open when", true, 1001)] static bool dadadsssas123() => false; + + [MenuItem(activeOnAlt, false, 1002)] static void dadsdasasdadsas() => activeOnAltEnabled = !activeOnAltEnabled; + [MenuItem(activeOnAlt, true, 1002)] static bool dadsadadssdadsdasadsas() { Menu.SetChecked(activeOnAlt, activeOnAltEnabled); return !pluginDisabled; } + + [MenuItem(activeOnAltShift, false, 1003)] static void dadsdasasdadsadsas() => activeOnAltShiftEnabled = !activeOnAltShiftEnabled; + [MenuItem(activeOnAltShift, true, 1003)] static bool dadsadadssdasdadsdasadsas() { Menu.SetChecked(activeOnAltShift, activeOnAltShiftEnabled); return !pluginDisabled; } + + [MenuItem(activeOnCtrlAlt, false, 1004)] static void dadsdasadasadssdadsas() => activeOnCtrlAltEnabled = !activeOnCtrlAltEnabled; + [MenuItem(activeOnCtrlAlt, true, 1004)] static bool dadsadadsadssdadsdasadsas() { Menu.SetChecked(activeOnCtrlAlt, activeOnCtrlAltEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "More", false, 10001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 10001)] static bool dadsadsdasas123() => false; + + [MenuItem(dir + "Join our Discord", false, 10002)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/4dG9KsbspG"); + + [MenuItem(dir + "Get more Editor Enhancers/Get vHierarchy 2", false, 10003)] + static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/251320?aid=1100lGLBn&pubref=menucheckout"); + + [MenuItem(dir + "Get more Editor Enhancers/Get vFolders 2", false, 10004)] + static void dadadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263644?aid=1100lGLBn&pubref=menucheckout"); + + [MenuItem(dir + "Get more Editor Enhancers/Get vTabs 2", false, 10005)] + static void dadadsadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263645?aid=1100lGLBn&pubref=menucheckout"); + + + + + + + + + [MenuItem(disablePlugin, false, 100001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 100001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFavorites/VFavoritesMenu.cs.meta b/Assets/Third Parties/vFavorites/VFavoritesMenu.cs.meta new file mode 100644 index 00000000..c4ab387d --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65f8f6586ea4740238f667f8337cf6fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs b/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs.meta b/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs.meta new file mode 100644 index 00000000..cef62601 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1b1b30cd63e14da9a295742be768842 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/VFavoritesState.cs b/Assets/Third Parties/vFavorites/VFavoritesState.cs new file mode 100644 index 00000000..c4d1a52d --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesState.cs @@ -0,0 +1,57 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + +namespace VFavorites +{ + [FilePath("Library/vFavorites State.asset", FilePathAttribute.Location.ProjectFolder)] + public class VFavoritesState : ScriptableSingleton + { + public int curPageIndex; + + public SerializableDictionary pageStates_byPageId = new SerializableDictionary(); + + public SerializableDictionary itemStates_byItemId = new SerializableDictionary(); + + + [System.Serializable] + public class PageState + { + public long lastItemSelectTime_ticks; + public long lastItemDragTime_ticks; + + public float scrollPos; + + } + + [System.Serializable] + public class ItemState + { + public string _name; + + public string sceneGameObjectIconName; + + public long lastSelectTime_ticks; + public bool isSelected; + + } + + + public static void Save() => instance.Save(true); + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFavorites/VFavoritesState.cs.meta b/Assets/Third Parties/vFavorites/VFavoritesState.cs.meta new file mode 100644 index 00000000..a3261501 --- /dev/null +++ b/Assets/Third Parties/vFavorites/VFavoritesState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 362e220c378db4e97ae41fa35a6fb58f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFavorites/vFavorites Data.asset b/Assets/Third Parties/vFavorites/vFavorites Data.asset new file mode 100644 index 00000000..bc1b79ff --- /dev/null +++ b/Assets/Third Parties/vFavorites/vFavorites Data.asset @@ -0,0 +1,16 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 066cf82f8f80d408c856e48fc8f1127b, type: 3} + m_Name: vFavorites Data + m_EditorClassIdentifier: VFavorites::VFavorites.VFavoritesData + pages: [] + rowScale: 1 diff --git a/Assets/Third Parties/vFavorites/vFavorites Data.asset.meta b/Assets/Third Parties/vFavorites/vFavorites Data.asset.meta new file mode 100644 index 00000000..ceeba770 --- /dev/null +++ b/Assets/Third Parties/vFavorites/vFavorites Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 86aa99958f6df3c46929bd75bbe78ae1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders.meta b/Assets/Third Parties/vFolders.meta new file mode 100644 index 00000000..bea38d23 --- /dev/null +++ b/Assets/Third Parties/vFolders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c7dca8ae168f8ba41873023d2bbcfdb6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/Manual.pdf b/Assets/Third Parties/vFolders/Manual.pdf new file mode 100644 index 00000000..a590e288 Binary files /dev/null and b/Assets/Third Parties/vFolders/Manual.pdf differ diff --git a/Assets/Third Parties/vFolders/Manual.pdf.meta b/Assets/Third Parties/vFolders/Manual.pdf.meta new file mode 100644 index 00000000..6f4911a3 --- /dev/null +++ b/Assets/Third Parties/vFolders/Manual.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 443dd991dbef94630a18f43d266b5e33 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFolders.asmdef b/Assets/Third Parties/vFolders/VFolders.asmdef new file mode 100644 index 00000000..d3d34ab3 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFolders.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VFolders", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFolders.asmdef.meta b/Assets/Third Parties/vFolders/VFolders.asmdef.meta new file mode 100644 index 00000000..a4528fa0 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFolders.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3331bc437f3a24586b7e86e7bdd62b5e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFolders.cs b/Assets/Third Parties/vFolders/VFolders.cs new file mode 100644 index 00000000..7058777b --- /dev/null +++ b/Assets/Third Parties/vFolders/VFolders.cs @@ -0,0 +1,2007 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using System.IO; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using UnityEditorInternal; +using Type = System.Type; +using static VFolders.VFoldersData; +using static VFolders.VFoldersCache; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + + +namespace VFolders +{ + public static class VFolders + { + static void RowGUI(string guid, Rect rowRect) + { + var fullRowRect = rowRect.AddWidthFromRight(rowRect.x); + + var isRowHovered = fullRowRect.IsHovered(); + + var isListArea = rowRect.x == 14; + + var isFolder = AssetDatabase.IsValidFolder(guid.ToPath()); + var isAsset = !isFolder && !guid.IsNullOrEmpty(); + var isFavorite = !isFolder && !isAsset && rowRect.x != 16; + + var isFavoritesRoot = rowRect.x == 16 && !isFolder && rowRect.y == 0; + var isAssetsRoot = rowRect.x == 16 && isFolder && guid.ToPath() == "Assets"; + var isPackagesRoot = rowRect.x == 16 && !isFavoritesRoot && !isAssetsRoot && guid.IsNullOrEmpty(); + + var useClearerRows = VFoldersMenu.clearerRowsEnabled && !isListArea; + var useMinimalMode = VFoldersMenu.minimalModeEnabled && !isListArea; + var useContentMinimap = VFoldersMenu.contentMinimapEnabled && !isListArea; + var useHierarchyLines = VFoldersMenu.hierarchyLinesEnabled && !isListArea; + var useZebraStriping = VFoldersMenu.zebraStripingEnabled && !isListArea; + + + + EditorWindow browser = null; + Tree tree = null; + TreeViewItem treeItem = null; + + var isTreeFocused = false; + var isRowSelected = false; + var isRowBeingRenamed = false; + + void setObjects() + { + if (isListArea) return; + + if (!curEvent.isRepaint) // only needed for drawing, + if (!curEvent.isMouseUp) // altClick and + if (!isRowHovered) // setting last hovered tree item + return; + + + void set_browser() + { + var pointInsideWindow = EditorGUIUtility.GUIToScreenPoint(rowRect.center); + + browser = allProjectBrowsers.FirstOrDefault(r => r.position.AddHeight(30).Contains(pointInsideWindow) && r.hasFocus); + + } + void set_tree() + { + if (browser == null) return; + + tree = GetTree(browser); + + } + void set_treeItem_byRect() + { + if (tree == null) return; + if (tree.animatingExpansion) return; + + var offest = tree.isTwoColumns ? -15 : -4; + + + if ((rowRect.y + offest) % 16 != 0) return; + + var rowIndex = ((rowRect.y + offest) / 16).ToInt(); + + + if (rowIndex < 0) return; + if (tree.data == null) return; + + treeItem = (TreeViewItem)Tree.mi_data_GetItem.Invoke(tree.data, new object[] { rowIndex }); + + } + void set_treeItem_byInstanceId() + { + if (tree == null) return; + if (treeItem != null) return; + if (isFavorite || isFavoritesRoot) return; + + var instanceId = typeof(AssetDatabase).InvokeMethod("GetMainAssetOrInProgressProxyInstanceID", guid.ToPath()); + + treeItem = tree.treeViewController.InvokeMethod("FindItem", instanceId); + + } + + set_browser(); + set_tree(); + set_treeItem_byRect(); + set_treeItem_byInstanceId(); + + } + void setState() + { + void set_isTreeFocused() + { + if (!curEvent.isRepaint) return; + if (tree?.treeViewController == null) return; + if (isListArea) return; + + isTreeFocused = EditorWindow.focusedWindow == browser && + GUIUtility.keyboardControl == tree.treeViewController.GetFieldValue("m_KeyboardControlID"); + + } + void set_isRowSelected_oneColumn() + { + if (!curEvent.isRepaint) return; + if (isListArea) return; + if (tree == null) return; + if (treeItem == null) return; + if (tree.isTwoColumns) return; + +#if UNITY_2021_1_OR_NEWER + var dragSelectionList = tree.treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List"); +#else + var dragSelectionList = tree.treeViewController?.GetFieldValue>("m_DragSelection"); +#endif + + var dragging = dragSelectionList != null && dragSelectionList.Any(); + + isRowSelected = dragging ? (dragSelectionList.Contains(treeItem.id)) : Selection.Contains(treeItem.id); + + } + void set_isRowSelected_TwoColumns() + { + if (!curEvent.isRepaint) return; + if (isListArea) return; + if (tree == null) return; + if (treeItem == null) return; + if (!tree.isTwoColumns) return; + +#if UNITY_2021_1_OR_NEWER + var dragSelectionList = tree.treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List"); + var normalSelectionList = tree.treeViewController?.GetFieldValue("m_CachedSelection")?.GetFieldValue>("m_List"); +#else + var dragSelectionList = tree.treeViewController?.GetFieldValue>("m_DragSelection"); + var normalSelectionList = tree.treeViewController?.GetMemberValue("state").GetMemberValue>("selectedIDs"); +#endif + + var dragging = dragSelectionList != null && dragSelectionList.Any(); + + isRowSelected = dragging ? (dragSelectionList.Contains(treeItem.id)) : normalSelectionList != null && normalSelectionList.Contains(treeItem.id); + + } + void set_isRowBeingRenamed() + { + if (!curEvent.isRepaint) return; + if (isListArea) return; + + isRowBeingRenamed = EditorGUIUtility.editingTextField && + isRowSelected && + tree?.treeViewController?.GetMemberValue("state")?.GetMemberValue("renameOverlay")?.InvokeMethod("IsRenaming") == true; + + } + void set_lastHovered() + { + if (isListArea) return; + if (!curEvent.isRepaint) return; + if (!isRowHovered) return; + + lastHoveredRowRect_screenSpace = EditorGUIUtility.GUIToScreenRect(fullRowRect); + lastHoveredTreeItem = treeItem; + + } + + set_isTreeFocused(); + set_isRowSelected_oneColumn(); + set_isRowSelected_TwoColumns(); + set_isRowBeingRenamed(); + set_lastHovered(); + + lastKnownMousePosition_screenSpace = curEvent.mousePosition_screenSpace; + + } + + + void drawing() + { + if (!curEvent.isRepaint) { hierarchyLines_isFirstRowDrawn = false; return; } + + var folderInfo = GetFolderInfo(guid, createDataIfDoesntExist: false); + + var showBackgroundColor = folderInfo.hasColor && useClearerRows;// && !(isRowSelected && isTreeFocused); + var showCustomIcon = useClearerRows ? folderInfo.hasIcon : (folderInfo.hasIcon || folderInfo.hasColor); + var showDefaultIcon = !showCustomIcon && (!useMinimalMode || isRowBeingRenamed); + + var makeTriangleBrighter = showBackgroundColor && isDarkTheme; + var makeNameBrighter = showBackgroundColor && isDarkTheme; + var makeIconBrighter = showBackgroundColor; + + var hideProperly = showBackgroundColor || (!showCustomIcon && !showDefaultIcon); + + Color defaultBackground = default; + + + void calcDefaultBackground() + { + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + if (!hideProperly) return; + + var selectedFocused = GUIColors.selectedBackground; + var selectedUnfocused = isDarkTheme ? Greyscale(.3f) : Greyscale(.68f); + var normal = isListArea ? Greyscale(.2f) : GUIColors.windowBackground; + + if (isRowSelected && !isRowBeingRenamed) + defaultBackground = isTreeFocused ? selectedFocused : selectedUnfocused; + + else + defaultBackground = normal; + + } + void hideName_proper() + { + if (!showBackgroundColor && (showCustomIcon || showDefaultIcon)) return; + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + if (!hideProperly) return; + + var name = isListArea ? guid.ToPath().GetFilename() : treeItem != null ? treeItem.displayName : "Favorites"; + + var nameRect = rowRect.SetWidth(name.GetLabelWidth() + 3).MoveX(16).MoveX(isListArea ? 3 : 0); + + nameRect.Draw(defaultBackground); + + } + void hideDefaultIcon_proper() + { + if (showDefaultIcon) return; + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + if (!hideProperly) return; + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + iconRect.Draw(defaultBackground); + + + + } + void hideDefaultIcon_fast() + { + if (showDefaultIcon) return; + if (hideProperly) return; + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + SetGUIColor(EditorGUIUtility.isProSkin ? Greyscale(.28f) : Color.white); + + GUI.DrawTexture(iconRect, EditorGUIUtility.FindTexture("d_Folder Icon")); + GUI.DrawTexture(iconRect, EditorGUIUtility.FindTexture("d_FolderOpened Icon")); + + ResetGUIColor(); + + } + + void backgroundColor() + { + if (!showBackgroundColor) return; + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + + + var hasLeftGradient = rowRect.x > 32; + + + + var colorRect = rowRect.AddWidthFromRight(30).AddWidth(16); + + if (!isRowSelected) + colorRect = colorRect.AddHeightFromMid(EditorGUIUtility.pixelsPerPoint >= 2 ? -.5f : -1); + + if (hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(2); + + + + + var leftGradientWith = hasLeftGradient ? 22 : 0; + var rightGradientWidth = (fullRowRect.width * .77f).Min(colorRect.width); + + var leftGradientRect = colorRect.SetWidth(leftGradientWith); + var rightGradientRect = colorRect.SetWidthFromRight(rightGradientWidth - leftGradientWith); + + var flatColorRect = colorRect.SetX(leftGradientRect.xMax).SetXMax(rightGradientRect.x); + + + + + var colorWithFlatness = folderInfo.color; + + var flatness = colorWithFlatness.a; + + var color = (isDarkTheme ? colorWithFlatness * .64f : Color.Lerp(colorWithFlatness, Greyscale(.8f), .5f)).SetAlpha(1); + + if (isRowHovered) + color *= 1.1f; + + if (isRowSelected) + color *= 1.2f; + + + + + leftGradientRect.Draw(color.SetAlpha((flatness - .1f) / .9f)); + leftGradientRect.DrawCurtainLeft(color); + + flatColorRect.AddWidth(1).Draw(color); + + rightGradientRect.Draw(color.MultiplyAlpha(flatness)); + rightGradientRect.DrawCurtainRight(color); + + + } + void triangle() + { + if (!showBackgroundColor) return; + if (treeItem == null) return; + if (!treeItem.hasChildren) return; + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + + + var triangleRect = rowRect.MoveX(-15).SetWidth(16).Resize(-1); + + GUI.Label(triangleRect, EditorGUIUtility.IconContent(tree.IsExpanded(treeItem) ? "IN_foldout_on" : "IN_foldout")); + + + if (!makeTriangleBrighter) return; + + GUI.Label(triangleRect, EditorGUIUtility.IconContent(tree.IsExpanded(treeItem) ? "IN_foldout_on" : "IN_foldout")); + + } + void name() + { + if (!showBackgroundColor && (showCustomIcon || showDefaultIcon)) return; + if (!isFolder && !isFavoritesRoot && !isPackagesRoot) return; + if (isRowBeingRenamed) return; + + + var nameRect = rowRect.MoveX(18); + + if (isListArea) + nameRect = nameRect.MoveX(3); + + if (useMinimalMode && !showCustomIcon && !showDefaultIcon) + nameRect = nameRect.MoveX(-17); + + if (makeNameBrighter) + nameRect = nameRect.MoveX(-2).MoveY(-.5f); + + + + var styleName = makeNameBrighter ? "WhiteLabel" : "TV Line"; + + if (isFavoritesRoot || isAssetsRoot || isPackagesRoot) + styleName = "BoldLabel"; + + + + var name = isFavoritesRoot ? "Favorites" : + isPackagesRoot ? "Packages" : + isListArea || treeItem == null ? guid.ToPath().GetFilename() : + treeItem.displayName; + + + + if (makeNameBrighter) + SetGUIColor(Greyscale(isRowSelected ? 1 : .93f)); + + GUI.skin.GetStyle(styleName).Draw(nameRect, name, false, false, isRowSelected, isTreeFocused && styleName != "BoldLabel"); + + if (makeNameBrighter) + ResetGUIColor(); + + } + void defaultIcon() + { + if (!isFolder) return; + if (!showBackgroundColor) return; + if (!showDefaultIcon) return; + + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + + SetLabelAlignmentCenter(); + + if (makeNameBrighter) + SetGUIColor(Greyscale(.88f)); + + if (makeNameBrighter) + GUI.Label(iconRect.Resize(-2), EditorGUIUtility.IconContent(folderInfo.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon")); + else + GUI.Label(iconRect.Resize(-2), EditorGUIUtility.IconContent(folderInfo.isEmpty ? "FolderEmpty Icon" : "Folder Icon")); + + if (makeNameBrighter) + ResetGUIColor(); + + ResetLabelStyle(); + + } + void customIcon() + { + if (!isFolder) return; + if (!showCustomIcon) return; + + + var icon = useClearerRows ? EditorIcons.GetIcon(folderInfo.iconNameOrPath) + : GetSmallFolderIcon(folderInfo); + + if (!icon) return; + + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + iconRect = iconRect.SetWidth(iconRect.height / icon.height * icon.width); + + + GUI.DrawTexture(iconRect, icon); + + } + void hierarchyLines() + { + if (!useHierarchyLines) return; + if (isListArea) return; + if (treeItem == null) return; + + + var lineThickness = 1f; + var lineContrast = isDarkTheme ? .35f : .55f; + + if (isRowSelected) + if (isTreeFocused) + lineContrast += isDarkTheme ? .1f : -.25f; + else + lineContrast += isDarkTheme ? .05f : -.05f; + + + var depth = ((rowRect.x - 16) / 14).RoundToInt(); + + bool isLastChild(TreeViewItem item) => item.parent?.children?.LastOrDefault() == item; + bool hasChilren(TreeViewItem item) => item.children != null && item.children.Count > 0; + + void calcVerticalGaps_beforeFirstRowDrawn() + { + if (hierarchyLines_isFirstRowDrawn) return; + + hierarchyLines_verticalGaps.Clear(); + + var curItem = treeItem.parent; + var curDepth = depth - 1; + + while (curItem != null && curItem.parent != null) + { + if (isLastChild(curItem)) + hierarchyLines_verticalGaps.Add(curDepth - 1); + + curItem = curItem.parent; + curDepth--; + } + + } + void updateVerticalGaps_beforeNextRowDrawn() + { + if (isLastChild(treeItem)) + hierarchyLines_verticalGaps.Add(depth - 1); + + if (depth < hierarchyLines_prevRowDepth) + hierarchyLines_verticalGaps.RemoveAll(r => r >= depth); + + } + + void drawVerticals() + { + for (int i = 1; i < depth; i++) + if (!hierarchyLines_verticalGaps.Contains(i)) + rowRect.SetX(9 + i * 14 - lineThickness / 2) + .SetWidth(lineThickness) + .SetHeight(isLastChild(treeItem) && i == depth - 1 ? 8 + lineThickness / 2 : 16) + .Draw(Greyscale(lineContrast)); + + } + void drawHorizontals() + { + if (depth == 0) return; + if (depth == 1) return; + + rowRect.MoveX(-21) + .SetHeightFromMid(lineThickness) + .SetWidth(hasChilren(treeItem) ? 7 : 17) + .Draw(Greyscale(lineContrast)); + + } + + + + calcVerticalGaps_beforeFirstRowDrawn(); + + drawVerticals(); + drawHorizontals(); + + updateVerticalGaps_beforeNextRowDrawn(); + + hierarchyLines_prevRowDepth = depth; + hierarchyLines_isFirstRowDrawn = true; + + } + void zebraStriping() + { + if (!useZebraStriping) return; + + var contrast = isDarkTheme ? .033f : .05f; + + var t = 1 - rowRect.y.PingPong(16f) / 16f; + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, contrast * t)); + + + } + void hoveredRow() + { + if (!isRowHovered) return; + if (isListArea) return; + if (EditorWindow.mouseOverWindow == VFoldersPaletteWindow.instance) return; + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, .06f)); + + } + + void contentMinimap() + { + if (!isFolder) return; + if (!useContentMinimap) return; + if (guid.IsNullOrEmpty()) return; + + void icon(Rect rect, string name) + { + var icon = EditorIcons.GetIcon(name); + + if (!icon) return; + + + SetGUIColor(Greyscale(1, isDarkTheme ? .49f : .7f)); + + GUI.DrawTexture(rect, icon); + + ResetGUIColor(); + + } + + + var iconDistance = 13; + var minButtonX = rowRect.x + guid.ToPath().GetFilename().GetLabelWidth() + iconDistance + 2; + var iconRect = fullRowRect.SetWidthFromRight(iconDistance).SetSizeFromMid(12, 12).MoveX(-1.5f); + + foreach (var iconName in folderInfo.folderState.contentMinimapIconNames) + { + if (iconRect.x < minButtonX) continue; + + icon(iconRect, iconName); + + iconRect = iconRect.MoveX(-iconDistance); + + } + + } + + + fullRowRect.MarkInteractive(); + + calcDefaultBackground(); + hideName_proper(); + hideDefaultIcon_proper(); + hideDefaultIcon_fast(); + + hierarchyLines(); + backgroundColor(); + triangle(); + defaultIcon(); + customIcon(); + name(); + zebraStriping(); + hoveredRow(); + + contentMinimap(); + + } + + void altDrag() + { + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!isRowHovered) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!rowRect.Contains(mouseDownPos)) return; + if (!rowRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath(guid.ToPath()) }; + DragAndDrop.StartDrag(guid.ToPath().GetFilename()); + + } + + mouseDown(); + mouseDrag(); + + // altdrag has to be set up manually before altClick because altClick will use() mouseDown event to prevent selection change + } + void altClick() + { + if (!isRowHovered) return; + if (!curEvent.holdingAlt) return; + if (!isFolder) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var selectedGuids = isListArea + ? + Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid()) + : +#if UNITY_2021_1_OR_NEWER + tree.treeViewController.GetFieldValue("m_CachedSelection").GetFieldValue>("m_List") +#else + tree.treeViewController?.GetMemberValue("state").GetMemberValue>("selectedIDs") +#endif + .Select(id => tree.treeViewController.InvokeMethod("FindItem", id) // todo to somethinf else (finditem reveals selected item for some reason) + .GetPropertyValue("Guid", exceptionIfNotFound: false)) + .Where(r => r != null); + + + var editMultiSelection = selectedGuids.Count() > 1 && selectedGuids.Contains(guid); + + var guidsToEdit = (editMultiSelection ? selectedGuids.Where(r => AssetDatabase.IsValidFolder(r.ToPath())) : new[] { guid }).ToList(); + + + if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; } + + var openNearRect = rowRect;// editMultiSelection ? lastVisibleSelectedCellRect : cellRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(curEvent.mousePosition.x + 20, openNearRect.y - 13)); + + if (!VFoldersPaletteWindow.instance) + VFoldersPaletteWindow.CreateInstance(position); + + VFoldersPaletteWindow.instance.Init(guidsToEdit); + VFoldersPaletteWindow.instance.Focus(); + + VFoldersPaletteWindow.instance.targetPosition = position; + + if (editMultiSelection) + Selection.objects = null; + } + + mouseDown(); + mouseUp(); + + } + + + + setObjects(); + setState(); + + drawing(); + + altDrag(); + altClick(); + + } + + static List hierarchyLines_verticalGaps = new List(); + static bool hierarchyLines_isFirstRowDrawn; + static int hierarchyLines_prevRowDepth; + + static Vector2 lastKnownMousePosition_screenSpace; + static Rect lastHoveredRowRect_screenSpace; + static TreeViewItem lastHoveredTreeItem; + + + + + static void CellGUI(string guid, Rect cellRect) + { + if (!AssetDatabase.IsValidFolder(guid.ToPath())) return; + + var folderInfo = GetFolderInfo(guid, createDataIfDoesntExist: false); + + + void setLastVisibleSelectedForAltClick() + { + if (!curEvent.isRepaint) return; + if (!Selection.objects.Contains(AssetDatabase.LoadAssetAtPath(guid.ToPath()))) return; + + lastVisibleSelectedCellRect = cellRect; + + } + + void hideIcon() + { + if (!curEvent.isRepaint) return; + if (!folderInfo.hasColor) return; + + cellRect.SetHeight(cellRect.width).Resize(4).Draw(EditorGUIUtility.isProSkin ? Greyscale(.2f) : Greyscale(.75f)); + + } + void icon() + { + if (!curEvent.isRepaint) return; + if (!folderInfo.hasColor && !folderInfo.hasIcon) return; + + DrawBigFolderIcon(cellRect, folderInfo); + + } + + void altDrag() + { + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!cellRect.IsHovered()) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!cellRect.Contains(mouseDownPos)) return; + if (!cellRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath(guid.ToPath()) }; + DragAndDrop.StartDrag(guid.ToPath().GetFilename()); + + } + + mouseDown(); + mouseDrag(); + + } + void altClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var selectedFoldersGuids = Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid()); + + var editMultiSelection = selectedFoldersGuids.Count() > 1 && selectedFoldersGuids.Contains(guid); + + var guidsToEdit = (editMultiSelection ? selectedFoldersGuids : new[] { guid }).ToList(); + + + if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; } + + var openNearRect = editMultiSelection ? lastVisibleSelectedCellRect : cellRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(openNearRect.xMax + 8, openNearRect.y - 5)); + + if (!VFoldersPaletteWindow.instance) + VFoldersPaletteWindow.CreateInstance(position); + + VFoldersPaletteWindow.instance.Init(guidsToEdit); + VFoldersPaletteWindow.instance.Focus(); + + VFoldersPaletteWindow.instance.targetPosition = position; + + if (editMultiSelection) + Selection.objects = null; + } + + mouseDown(); + mouseUp(); + + } + + + setLastVisibleSelectedForAltClick(); + + hideIcon(); + icon(); + + altDrag(); + altClick(); + + } + + static Rect lastVisibleSelectedCellRect; + + + + static void ProjectBrowserItemGUI(string guid, Rect itemRect) + { + void callRowGUI() + { + if (itemRect.height != 16) return; + + RowGUI(guid, itemRect); + + } + void callCellGUI() + { + if (itemRect.height == 16) return; + + CellGUI(guid, itemRect); + + } + void updateExpandQueues() + { + if (!curEvent.isLayout) return; + + foreach (var tree in trees_byBrowser.Values) + tree.UpdateExpandQueue(); + + } + void createFoldersFirstUpdater() + { + if (!curEvent.isLayout) return; + if (!VFoldersMenu.foldersFirstEnabled) return; + + var pointInsideWindow = EditorGUIUtility.GUIToScreenPoint(itemRect.center); + + var browser = allProjectBrowsers.FirstOrDefault(r => r.position.AddHeight(30).Contains(pointInsideWindow) && r.hasFocus); + + if (browser == null) return; + if (foldersFirstUpdater_byBrowser.ContainsKey(browser)) return; + + var updater = new FoldersFirstUpdater(browser); + + foldersFirstUpdater_byBrowser[browser] = updater; + + updater.Update(); + + } + + callRowGUI(); + callCellGUI(); + updateExpandQueues(); + createFoldersFirstUpdater(); + + } + + static Vector2 mouseDownPos; + + + + + + + + + + + public static Texture2D GetSmallFolderIcon(FolderInfo folderInfo) + { + var key = new object[] { folderInfo.iconNameOrPath, folderInfo.color, folderInfo.isEmpty, isDarkTheme }.Aggregate(0, (hash, r) => (hash * 2) ^ r.GetHashCode()); + + Texture2D icon = null; + + void getCached() + { + if (!cache.HasIcon(key)) return; + + icon = cache.GetIcon(key); + + } + void generateAndCache() + { + if (icon != null) return; + if (Event.current != null) return; // interactions with gpu in OnGUI may interfere with gui rendering + + var iconSizeX = folderInfo.hasIcon ? 36 : 32; + var iconSizeY = 32; + + var assetIconSize = 20; // 20 21 + var assetIconOffsetX = 16; + var assetIconOffsetY = -2; // -2 -3 + + var folderIconSize = iconSizeY; + var folderIconOffsetY_ifHasAssetIcon = 1; + + Color[] iconPixels; + + Texture2D folderIcon; + Color[] folderIconPixels; + + + void createIcon() + { + icon = new Texture2D(iconSizeX, iconSizeY, TextureFormat.RGBA32, 1, false); + icon.hideFlags = HideFlags.DontSave; + icon.SetPropertyValue("pixelsPerPoint", 2); + + iconPixels = new Color[iconSizeX * iconSizeY]; + + } + void createFolderIcon() + { + // var folderIconName = folderInfo.isEmpty ? "FolderEmpty Icon" : "Project@2x"; + var folderIconName = folderInfo.hasColor ? (folderInfo.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon") : + (folderInfo.isEmpty ? "FolderEmpty Icon" : "Folder Icon"); + + + folderIcon = EditorGUIUtility.FindTexture(folderIconName); + + if (folderIcon.width != folderIconSize) + folderIcon = folderIcon.CreateResizedCopy(folderIconSize, folderIconSize); + else + folderIcon = folderIcon.CreateCopy(); + + folderIconPixels = folderIcon.GetPixels(0); + + } + void copyFolderIcon() + { + if (!folderInfo.hasIcon) { iconPixels = folderIconPixels; return; } + + for (int x = 0; x < folderIcon.width; x++) + for (int y = 0; y < folderIcon.height - folderIconOffsetY_ifHasAssetIcon; y++) + iconPixels[x + (y + folderIconOffsetY_ifHasAssetIcon) * icon.width] = folderIconPixels[x + y * folderIcon.width]; + + } + void applyColor() + { + if (!folderInfo.hasColor) return; + + for (int i = 0; i < iconPixels.Length; i++) + iconPixels[i] *= folderInfo.color.SetAlpha(1); + + } + void insertAssetIcon() + { + if (!folderInfo.hasIcon) return; + + + var assetIconOriginal = EditorIcons.GetIcon(folderInfo.iconNameOrPath); + + if (!assetIconOriginal) return; + + + var prevFilter = assetIconOriginal.filterMode; + + assetIconOriginal.filterMode = FilterMode.Bilinear; + var assetIconPixels_bilinear = assetIconOriginal.CreateResizedCopy(assetIconSize, assetIconSize).GetPixels(); + + assetIconOriginal.filterMode = FilterMode.Point; + var assetIconPixels_point = assetIconOriginal.CreateResizedCopy(assetIconSize, assetIconSize).GetPixels(); + + + assetIconOriginal.filterMode = prevFilter; + + + for (int x = 0; x < iconSizeX; x++) + for (int y = 0; y < iconSizeY; y++) + { + var xAssetIcon = x - assetIconOffsetX; + var yAssetIcon = y - assetIconOffsetY - folderIconOffsetY_ifHasAssetIcon; + + if (!xAssetIcon.IsInRange(0, assetIconSize - 1)) continue; + if (!yAssetIcon.IsInRange(0, assetIconSize - 1)) continue; + + + var innerRadius = (folderInfo.iconNameOrPath == "AudioClip Icon" ? .2f : .4f); + var isInnerPixel = (new Vector2(xAssetIcon, yAssetIcon) / (assetIconSize - 1) - Vector2.one * .5f).magnitude < innerRadius; + + var isOutlinePixel = false; + var outlineRadius = isInnerPixel ? 2 : 1; + for (int xx = xAssetIcon - outlineRadius; xx <= xAssetIcon + outlineRadius; xx++) + if (!isOutlinePixel) + for (int yy = yAssetIcon - outlineRadius; yy <= yAssetIcon + outlineRadius; yy++) + if (!isOutlinePixel) + if (xx.IsInRange(0, assetIconSize - 1) && yy.IsInRange(0, assetIconSize - 1)) + if (assetIconPixels_bilinear[xx + yy * assetIconSize].a > .2f) + isOutlinePixel = true; + + + var pxBilinear = assetIconPixels_bilinear[xAssetIcon + yAssetIcon * assetIconSize]; + var pxPoint = assetIconPixels_point[xAssetIcon + yAssetIcon * assetIconSize]; + var pxCombined = new Color(pxPoint.r, pxPoint.g, pxPoint.b, pxBilinear.a); + + if (pxCombined.a == 0 && !isOutlinePixel) continue; + + iconPixels[x + y * iconSizeX] = pxCombined; + + } + + } + + + createIcon(); + createFolderIcon(); + copyFolderIcon(); + applyColor(); + insertAssetIcon(); + + icon.SetPixels(iconPixels); + icon.Apply(); + + cache.AddIcon(key, icon); + + } + void queueGeneration() + { + if (icon != null) return; + + toCallInUpdate.Add(generateAndCache); + + } + + getCached(); + generateAndCache(); + queueGeneration(); + + return icon ?? EditorGUIUtility.FindTexture(folderInfo.isEmpty ? "FolderEmpty Icon" : "Project@2x"); + + } + public static Texture2D GetSmallFolderIcon_forVTabs(string folderGuid) + { + var folderInfo = GetFolderInfo(folderGuid, false); + + if (folderInfo.hasColor || folderInfo.hasIcon) + return GetSmallFolderIcon(folderInfo); + + return null; + + } + + static void Update() + { + foreach (var r in toCallInUpdate) + r.Invoke(); + + toCallInUpdate.Clear(); + + } + + static List toCallInUpdate = new List(); + + + + + + public static void DrawBigFolderIcon(Rect rect, FolderInfo folderInfo) + { + Rect folderIconRect; + Rect assetIconRect; + + + void calcRects() + { + folderIconRect = rect.SetHeight(rect.width); + +#if UNITY_2022_3_OR_NEWER +#else + if (Application.platform == RuntimePlatform.OSXEditor) + if (folderIconRect.width > 64) + folderIconRect = folderIconRect.SetSizeFromMid(64, 64); +#endif + + + var assetIconOffsetMin = new Vector2(4.5f, 3.5f); + var assetIconSizeMin = 10; + + var assetIconOffsetMax = new Vector2(19, 15); + var assetIconSizeMax = 24.5f; // 25 + + var t = ((folderIconRect.width - 16) / (64 - 16)); + +#if UNITY_2022_3_OR_NEWER +#else + if (Application.platform == RuntimePlatform.OSXEditor) + t = t.Clamp01(); +#endif + + var assetIconOffset = Lerp(assetIconOffsetMin, assetIconOffsetMax, t); + var assetIconSize = Lerp(assetIconSizeMin, assetIconSizeMax, t); + + assetIconRect = folderIconRect.Move(assetIconOffset).SetSizeFromMid(assetIconSize, assetIconSize).AlignToPixelGrid(); + + } + + void color() + { + if (!folderInfo.hasColor) return; + + + SetGUIColor(folderInfo.color.SetAlpha(1)); + + GUI.DrawTexture(folderIconRect, EditorGUIUtility.FindTexture(folderInfo.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon")); + + ResetGUIColor(); + + } + void assetIcon() + { + if (!folderInfo.hasIcon) return; + + var texture = EditorIcons.GetIcon(folderInfo.iconNameOrPath); + + if (!texture) return; + + void material() + { + if (outlineMaterial) return; + + outlineMaterial = new Material(Shader.Find("Hidden/VFoldersOutline")); + + outlineMaterial.SetColor("_Color", EditorGUIUtility.isProSkin ? Greyscale(.2f, .6f) : Greyscale(.75f)); + + } + + void shadow() + { + var contrast = isDarkTheme ? .6f : .2f; // was .65 then .6 + + assetIconRect.SetSizeFromMid(assetIconRect.width * .8f).DrawBlurred(Greyscale(.2f, contrast), assetIconRect.width * .55f); + + } + void outline() + { + var outlineRect = assetIconRect.Resize(rect.height >= 70 && EditorGUIUtility.pixelsPerPoint >= 2 ? -1f / EditorGUIUtility.pixelsPerPoint : 0).AlignToPixelGrid(); + + EditorGUI.DrawPreviewTexture(outlineRect.Move(-1, -1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(-1, 1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(1, 1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(1, -1), texture, outlineMaterial); + + } + void background() + { + for (int i = 0; i < assetIconRect.size.x; i++) + EditorGUI.DrawPreviewTexture(assetIconRect.Resize(i * .5f + 1), texture, outlineMaterial); + + } + void icon() + { + GUI.DrawTexture(assetIconRect, texture); + } + + material(); + + shadow(); + outline(); + background(); + icon(); + + } + + + calcRects(); + + color(); + assetIcon(); + + } + public static void DrawBigFolderIcon_forVFavorites(Rect rect, string folderGuid) + { + DrawBigFolderIcon(rect, GetFolderInfo(folderGuid, false)); + } + + static Material outlineMaterial; + + + + + + + + + + + + static void CheckShortcuts() // called from globalEventHandler + { + if (!hoveredProjectBrowser) return; + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode == KeyCode.None) return; + if (EditorWindow.mouseOverWindow.GetType() != t_ProjectBrowser) return; + + void toggleExpanded() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.holdingAnyModifierKey) return; + if (!VFoldersMenu.toggleExpandedEnabled) return; + + if (lastHoveredTreeItem == null) return; + if (!lastHoveredRowRect_screenSpace.Contains(lastKnownMousePosition_screenSpace)) return; + + curEvent.Use(); + + if (lastHoveredTreeItem.children == null) return; + if (lastHoveredTreeItem.children.Count == 0) return; + + var tree = GetTree(hoveredProjectBrowser); + + tree.SetExpandedWithAnimation(lastHoveredTreeItem.id, !tree.expandedIds.Contains(lastHoveredTreeItem.id)); + + EditorApplication.RepaintProjectWindow(); + + } + void collapseEverything() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Command) && curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Control)) return; + if (!VFoldersMenu.collapseEverythingEnabled) return; + + curEvent.Use(); + + var tree = GetTree(hoveredProjectBrowser); + + + var idsToCollapse_roots = tree.expandedIds.Where(id => EditorUtility.InstanceIDToObject(id).GetPath() is string path && + path.HasParentPath() && + (path.GetParentPath() == "Assets" || path.GetParentPath() == "Packages")); + + + var idsToCollapse_children = tree.expandedIds.Where(id => EditorUtility.InstanceIDToObject(id).GetPath() is string path && + !path.IsNullOrEmpty() && + path != "Assets" && + path != "Packages" && + !idsToCollapse_roots.Contains(id)); + + + tree.expandQueue_toCollapseAfterAnimation = idsToCollapse_children.ToList(); + + tree.expandQueue_toAnimate = idsToCollapse_roots.Select(id => new Tree.ExpandQueueEntry { id = id, expand = false }) + .OrderBy(row => tree.VisibleRowIndex(row.id)).ToList(); + + + EditorApplication.RepaintProjectWindow(); + + } + void collapseEverythingElse() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.modifiers != EventModifiers.Shift) return; + if (!VFoldersMenu.collapseEverythingElseEnabled) return; + + if (lastHoveredTreeItem == null) return; + if (!lastHoveredRowRect_screenSpace.Contains(lastKnownMousePosition_screenSpace)) return; + + curEvent.Use(); + + if (lastHoveredTreeItem.children == null) return; + if (lastHoveredTreeItem.children.Count == 0) return; + + var tree = GetTree(hoveredProjectBrowser); + + + + var parents = new List(); + + for (var cur = lastHoveredTreeItem.parent; cur != null; cur = cur.parent) + parents.Add(cur); + + + + var idsToCollapse = new List(); + + foreach (var id in tree.expandedIds.ToList()) + if (!parents.Any(r => r.id == id)) + if (id != lastHoveredTreeItem.id) + idsToCollapse.Add(id); + + + + tree.expandQueue_toAnimate = idsToCollapse.Select(id => new Tree.ExpandQueueEntry { id = id, expand = false }) + .Append(new Tree.ExpandQueueEntry { id = lastHoveredTreeItem.id, expand = true }) + .OrderBy(r => tree.VisibleRowIndex(r.id)).ToList(); + + + EditorApplication.RepaintProjectWindow(); + + } + + toggleExpanded(); + collapseEverything(); + collapseEverythingElse(); + + } + + + + + + + + class Tree + { + public void UpdateExpandQueue() // called from gui because reflected methods rely on event.current + { + if (animatingExpansion) return; + + if (!expandQueue_toAnimate.Any()) + { + if (!expandQueue_toCollapseAfterAnimation.Any()) return; + + foreach (var id in expandQueue_toCollapseAfterAnimation) + SetExpanded(id, false); + + expandQueue_toCollapseAfterAnimation.Clear(); + + return; + } + + var iid = expandQueue_toAnimate.First().id; + var expand = expandQueue_toAnimate.First().expand; + + if (expandedIds.Contains(iid) != expand) + SetExpandedWithAnimation(iid, expand); + + expandQueue_toAnimate.RemoveAt(0); + + } + + public void SetExpandedWithAnimation(int instanceId, bool expanded) => treeViewController.InvokeMethod("ChangeFoldingForSingleItem", instanceId, expanded); + public void SetExpanded(int instanceId, bool expanded) => treeViewController.GetPropertyValue("data").InvokeMethod("SetExpanded", instanceId, expanded); + + public int VisibleRowIndex(int instanceId) => treeViewController.GetPropertyValue("data").InvokeMethod("GetRow", instanceId); + + public bool animatingExpansion => treeViewController.GetPropertyValue("animatingExpansion"); + + public bool IsExpanded(TreeViewItem treeItem) => expandedIds.Contains(treeItem.id); + + + public List expandQueue_toAnimate = new List(); + public List expandQueue_toCollapseAfterAnimation = new List(); + + public struct ExpandQueueEntry { public int id; public bool expand; } + + + + + + public void UpdateState() // delayCall loop + { + isTwoColumns = browser.GetFieldValue("m_ViewMode") == 1; + + treeViewController = browser.GetFieldValue(isTwoColumns ? "m_FolderTree" : "m_AssetTree"); + + data = treeViewController?.GetPropertyValue("data"); + + expandedIds = treeViewController?.GetPropertyValue("state")?.GetPropertyValue>("expandedIDs") ?? new List(); + + + EditorApplication.delayCall -= UpdateState; + EditorApplication.delayCall += UpdateState; + + } + + public bool isTwoColumns; + + public object treeViewController; + + public object data; + + public List expandedIds = new List(); + + + + + + public Tree(EditorWindow browser) => this.browser = browser; + + public EditorWindow browser; + + + + + public static MethodInfo mi_data_GetItem = typeof(Editor).Assembly.GetType("UnityEditor.IMGUI.Controls.ITreeViewDataSource").GetMethod("GetItem", maxBindingFlags); + + } + + static Tree GetTree(EditorWindow browser) + { + if (trees_byBrowser.TryGetValue(browser, out var existingState)) return existingState; + + + var tree = new Tree(browser); + + tree.UpdateState(); + + trees_byBrowser[browser] = tree; + + return tree; + + } + + static Dictionary trees_byBrowser = new Dictionary(); + + + + + + + + class FoldersFirstUpdater + { + public void Update() // delayCall loop + { + if (browser == null) { foldersFirstUpdater_byBrowser.Remove(browser); return; } + + + var isTwoColumns = browser.GetFieldValue("m_ViewMode") == 1; + + void oneColumn() + { + if (isTwoColumns) return; + if (initedForOneColumn) return; + + + var m_AssetTree = browser.GetFieldValue("m_AssetTree"); + + if (m_AssetTree == null) return; + + m_AssetTree.GetPropertyValue("data").SetPropertyValue("foldersFirst", true); + m_AssetTree.InvokeMethod("ReloadData"); + + + initedForOneColumn = true; + initedForTwoColumns = false; + + } + void twoColumns() + { + if (!isTwoColumns) return; + if (initedForTwoColumns) return; + + + var m_ListArea = browser.GetFieldValue("m_ListArea"); + + if (m_ListArea == null) return; + + m_ListArea.SetPropertyValue("foldersFirst", true); + browser.InvokeMethod("InitListArea"); + + + initedForOneColumn = false; + initedForTwoColumns = true; + + } + + + oneColumn(); + twoColumns(); + + + EditorApplication.delayCall -= Update; + EditorApplication.delayCall += Update; + + } + + bool initedForOneColumn; + bool initedForTwoColumns; + + + + + public FoldersFirstUpdater(EditorWindow browser) => this.browser = browser; + + public EditorWindow browser; + + + } + + static Dictionary foldersFirstUpdater_byBrowser = new Dictionary(); + + + + + + + + + + + + + + public static FolderInfo GetFolderInfo(string guid, bool createDataIfDoesntExist) + { + var folderInfo = new FolderInfo(); + + folderInfo.folderData = GetFolderData(guid, createDataIfDoesntExist); + folderInfo.folderState = GetFolderState(guid); + + return folderInfo; + + } + + public class FolderInfo + { + public string iconNameOrGuid + { + get + { + var iconNameOrGuid = folderData?.iconNameOrGuid; + + if (iconNameOrGuid == "" || iconNameOrGuid == null) + iconNameOrGuid = VFoldersMenu.autoIconsEnabled ? folderState.autoIconName : ""; + + if (iconNameOrGuid == "none") + iconNameOrGuid = ""; + + return iconNameOrGuid; + + } + set + { + if (!VFoldersMenu.autoIconsEnabled) { folderData.iconNameOrGuid = value; return; } + + if (value == folderState.autoIconName) + folderData.iconNameOrGuid = ""; + + else if (value == "") + folderData.iconNameOrGuid = "none"; + + else + folderData.iconNameOrGuid = value; + + + // if (value == folderState.autoIconName && VFoldersMenu.autoIconsEnabled) + // folderData.iconNameOrGuid = ""; + + // else if (value == "") + // folderData.iconNameOrGuid = "none"; + + // else + // folderData.iconNameOrGuid = value; + + } + + } + public string iconNameOrPath => iconNameOrGuid.Length == 32 ? iconNameOrGuid.ToPath() : iconNameOrGuid; + + public Color color + { + get + { + if (!hasColor) return default; + // return VFoldersPalette.GetDefaultColor(folderData.colorIndex - 1); + + if (palette) + return palette.colors[folderData.colorIndex - 1]; + else + return VFoldersPalette.GetDefaultColor(folderData.colorIndex - 1); + + } + } + + public bool hasIcon => (folderData != null && folderData.iconNameOrGuid != "" && folderData.iconNameOrGuid != "none") || (VFoldersMenu.autoIconsEnabled && folderState.autoIconName != "" && (folderData == null || folderData.iconNameOrGuid != "none")); + public bool hasColor => folderData != null && folderData.colorIndex.IsInRange(1, VFoldersPalette.colorsCount); + public bool isEmpty => folderState.isEmpty; + + public FolderData folderData; + public FolderState folderState; + + } + + + + public static FolderData GetFolderData(string guid, bool createDataIfDoesntExist) + { + if (!data) return null; + + FolderData folderData = null; + + void fromScripableObject() + { + if (VFoldersData.storeDataInMetaFiles) return; + + data.folderDatas_byGuid.TryGetValue(guid, out folderData); + + + if (folderData != null || !createDataIfDoesntExist) return; + + folderData = new FolderData(); + + data.folderDatas_byGuid[guid] = folderData; + + } + void fromMetaFile() + { + if (!VFoldersData.storeDataInMetaFiles) return; + + folderDatasFromMetaFiles_byGuid.TryGetValue(guid, out folderData); + + + if (folderData != null) return; + + var importer = AssetImporter.GetAtPath(guid.ToPath()); + + try { folderData = JsonUtility.FromJson(importer.userData); } catch { } + + folderDatasFromMetaFiles_byGuid[guid] = folderData; + + + if (folderData != null || !createDataIfDoesntExist) return; + + folderData = new FolderData(); + + folderDatasFromMetaFiles_byGuid[guid] = folderData; + + } + + fromScripableObject(); + fromMetaFile(); + + return folderData; + + } + + public static Dictionary folderDatasFromMetaFiles_byGuid = new Dictionary(); + + + + public static FolderState GetFolderState(string guid) + { + FolderState folderState = null; + + void getFromCache() + { + cache.folderStates_byGuid.TryGetValue(guid, out folderState); + } + void create() + { + if (folderState != null) return; + + folderState = new FolderState(); + + folderState.needsUpdate = true; + + cache.folderStates_byGuid[guid] = folderState; + + } + void update() + { + if (!folderState.needsUpdate) return; + if (!Directory.Exists(guid.ToPath())) { folderState.needsUpdate = false; return; } + + void isEmpty() + { + folderState.isEmpty = !Directory.EnumerateFileSystemEntries(guid.ToPath()).Any(); + } + void contentTypeNames() + { + var types = Directory.GetFiles(guid.ToPath(), "*.*") + .Select(r => AssetDatabase.GetMainAssetTypeAtPath(r)) + .Where(r => r != null); + + + var iconNames = new List(); + + foreach (var type in types) + + if (type == typeof(Texture2D)) + iconNames.Add("Texture Icon"); + + else if (type == typeof(GameObject)) + iconNames.Add("Prefab Icon"); + + else if (type.BaseType == typeof(ScriptableObject) || type.BaseType?.BaseType == typeof(ScriptableObject)) + iconNames.Add("ScriptableObject Icon"); + + // else if (type == typeof(MonoScript) || type == typeof(AssemblyDefinitionAsset) || type == typeof(AssemblyDefinitionReferenceAsset)) + else if (type == typeof(MonoScript))// || type == typeof(AssemblyDefinitionAsset) || type == typeof(AssemblyDefinitionReferenceAsset)) + iconNames.Add("cs Script Icon"); + + else if (AssetPreview.GetMiniTypeThumbnail(type)?.name is string iconName) + iconNames.Add(iconName); + + + folderState.contentMinimapIconNames = iconNames.Distinct().OrderBy(r => r).ToList(); + + } + void autoIconName() + { + folderState.autoIconName = ""; + + var types = Directory.GetFiles(guid.ToPath(), "*.*") + .Select(r => AssetDatabase.GetMainAssetTypeAtPath(r)) + .Where(r => r != null); + + if (!types.Any()) return; + if (!types.All(r => r == types.First())) return; + + var type = types.First(); + + if (type == typeof(SceneAsset)) + folderState.autoIconName = "SceneAsset Icon"; + + else if (type == typeof(GameObject)) + folderState.autoIconName = "Prefab Icon"; + + else if (type == typeof(Material)) + folderState.autoIconName = "Material Icon"; + + else if (type == typeof(Texture)) + folderState.autoIconName = "Texture Icon"; + + else if (type.BaseType == typeof(ScriptableObject)) + folderState.autoIconName = "ScriptableObject Icon"; + + else if (type == typeof(TerrainData)) + folderState.autoIconName = "TerrainData Icon"; + + else if (type == typeof(AudioClip)) + folderState.autoIconName = "AudioClip Icon"; + + else if (type == typeof(Shader)) + folderState.autoIconName = "Shader Icon"; + + else if (type == typeof(ComputeShader)) + folderState.autoIconName = "ComputeShader Icon"; + + else if (type == typeof(MonoScript) || type == typeof(AssemblyDefinitionAsset) || type == typeof(AssemblyDefinitionReferenceAsset)) + folderState.autoIconName = "cs Script Icon"; + } + + isEmpty(); + contentTypeNames(); + autoIconName(); + + folderState.needsUpdate = false; + + } + + getFromCache(); + create(); + update(); + + return folderState; + + } + + class FolderStateChangeDetector : AssetPostprocessor + { +#if UNITY_2021_2_OR_NEWER + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) +#else + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) +#endif + { + foreach (var path in importedAssets.Concat(deletedAssets).Concat(movedAssets).Concat(movedFromAssetPaths)) + if (path.HasParentPath()) + if (cache.folderStates_byGuid.TryGetValue(path.GetParentPath().ToGuid(), out var folderState)) + folderState.needsUpdate = true; + } + } + + public static VFoldersCache cache => VFoldersCache.instance; + + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VFoldersMenu.pluginDisabled) return; + + void subscribe() + { + EditorApplication.projectWindowItemOnGUI -= ProjectBrowserItemGUI; + EditorApplication.projectWindowItemOnGUI = ProjectBrowserItemGUI + EditorApplication.projectWindowItemOnGUI; + + EditorApplication.update -= Update; + EditorApplication.update += Update; + + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", CheckShortcuts + (globalEventHandler - CheckShortcuts)); + + + // EditorApplication.playModeStateChanged += (PlayModeStateChange obj) => UpdateFoldersFirst(); + // EditorApplication.projectChanged += UpdateFoldersFirst; + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString("vFolders-lastKnownDataPath-" + GetProjectId())); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VFoldersData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + EditorPrefs.SetString("vFolders-lastKnownDataPath-" + GetProjectId(), data.GetPath()); + + } + void loadPalette() + { + palette = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString("vFolders-lastKnownPalettePath-" + GetProjectId())); + + + if (palette) return; + + palette = AssetDatabase.FindAssets("t:VFoldersPalette").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!palette) return; + + EditorPrefs.SetString("vFolders-lastKnownPalettePath-" + GetProjectId(), palette.GetPath()); + + } + void loadDataAndPaletteDelayed() + { + if (!data) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + if (!palette) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadPalette; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + void migrateDataFromV1() + { + if (!data) return; + if (EditorPrefs.GetBool("vFolders-dataMigrationFromV1Attempted-" + GetProjectId(), false)) return; + + EditorPrefs.SetBool("vFolders-dataMigrationFromV1Attempted-" + GetProjectId(), true); + + var lines = System.IO.File.ReadAllLines(data.GetPath()); + + if (lines.Length < 15 || !lines[14].Contains("folderDatasByGuid")) return; + + var guids = new List(); + var icons = new List(); + var colors = new List(); + + void parseGudis() + { + for (int i = 16; i < lines.Length; i++) + { + if (lines[i].Contains("values:")) break; + + var startIndex = lines[i].IndexOf("- ") + 2; + + if (startIndex < lines[i].Length) + guids.Add(lines[i].Substring(startIndex)); + else + guids.Add(""); + + } + + } + void parseIcons() + { + for (int i = 0; i < guids.Count; i++) + if (lines[29 + i * 5 + 3] is string line) + if (line.Length > line.IndexOf(": ") + 2) + icons.Add(line.Substring(line.IndexOf(": ") + 2)); + else + icons.Add(""); + + } + void parseColors() + { + for (int i = 0; i < guids.Count; i++) + if (lines[29 + i * 5 + 1] is string line) + if (line.Length > line.IndexOf(": ") + 2) + colors.Add(int.Parse(line.Substring(line.IndexOf(": ") + 2))); + else + colors.Add(0); + + } + + void remapColors() + { + for (int i = 0; i < colors.Count; i++) + if (colors[i] == 10) + colors[i] = 1; + else if (colors[i] != 0) + colors[i]++; + + } + void fillData() + { + for (int i = 0; i < guids.Count; i++) + if (icons[i] != "" || colors[i] != 0) + data.folderDatas_byGuid[guids[i]] = new FolderData { iconNameOrGuid = icons[i], colorIndex = colors[i] }; + + data.Dirty(); + data.Save(); + + } + + + try + { + parseGudis(); + parseIcons(); + parseColors(); + + remapColors(); + fillData(); + + } + catch { } + + } + + subscribe(); + loadData(); + loadPalette(); + loadDataAndPaletteDelayed(); + migrateDataFromV1(); + + } + + public static VFoldersData data; + public static VFoldersPalette palette; + + + + + + static EditorWindow hoveredProjectBrowser => EditorWindow.mouseOverWindow?.GetType() == t_ProjectBrowser ? EditorWindow.mouseOverWindow : null; + + static IEnumerable allProjectBrowsers => _allProjectBrowsers ??= t_ProjectBrowser.GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allProjectBrowsers; + + static Type t_ProjectBrowser = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + + + + + + + + const string version = "2.0.8"; + + } +} +#endif diff --git a/Assets/Third Parties/vFolders/VFolders.cs.meta b/Assets/Third Parties/vFolders/VFolders.cs.meta new file mode 100644 index 00000000..726886f6 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFolders.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb4ae53db31ef4fdfbfc47484afb8192 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersCache.cs b/Assets/Third Parties/vFolders/VFoldersCache.cs new file mode 100644 index 00000000..474ffb60 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersCache.cs @@ -0,0 +1,117 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + +namespace VFolders +{ + [FilePath("Library/vFolders Cache.asset", FilePathAttribute.Location.ProjectFolder)] + public class VFoldersCache : ScriptableSingleton + { + public Texture2D GetIcon(int key) + { + if (instance.iconTextures_byKey.ContainsKey(key)) return instance.iconTextures_byKey[key]; + + if (instance.iconTextureDatas_byKey.ContainsKey(key)) return instance.iconTextures_byKey[key] = instance.iconTextureDatas_byKey[key].GetTexture(); + + return null; + + } + public void AddIcon(int key, Texture2D icon) + { + instance.iconTextures_byKey[key] = icon; + instance.iconTextureDatas_byKey[key] = new TextureData(icon); + + instance.Save(true); + + } + public bool HasIcon(int key) => instance.iconTextureDatas_byKey.ContainsKey(key); + + public Dictionary iconTextures_byKey = new Dictionary(); + public SerializableDictionary iconTextureDatas_byKey = new SerializableDictionary(); + + [System.Serializable] + public class TextureData + { + public byte[] rawData; + public int width; + public int height; + public TextureFormat format; + public int mipCount; + public float pixelsPerPoint; + + public TextureData(Texture2D t) + { + rawData = t.GetRawTextureData(); + width = t.width; + height = t.height; + format = t.format; + mipCount = t.mipmapCount; + pixelsPerPoint = t.GetPropertyValue("pixelsPerPoint"); + + } + + public Texture2D GetTexture() + { + var t = new Texture2D(width, height, format, mipCount, false); + t.LoadRawTextureData(rawData); + t.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + t.hideFlags = HideFlags.DontSave; + t.Apply(); + + return t; + + } + + } + + + + + public SerializableDictionary folderStates_byGuid = new SerializableDictionary(); + + [System.Serializable] + public class FolderState + { + public bool isEmpty; + + public List contentMinimapIconNames = new List(); + + public string autoIconName = ""; + + public bool needsUpdate; + + } + + + + + + + public static void Clear() + { + instance.iconTextures_byKey.Clear(); + instance.iconTextureDatas_byKey.Clear(); + + instance.folderStates_byGuid.Clear(); + + instance.Save(true); + + } + + + + } +} +#endif + diff --git a/Assets/Third Parties/vFolders/VFoldersCache.cs.meta b/Assets/Third Parties/vFolders/VFoldersCache.cs.meta new file mode 100644 index 00000000..8cdb8933 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69c14c9fceb5b4c968f55fe5786a0bee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersData.cs b/Assets/Third Parties/vFolders/VFoldersData.cs new file mode 100644 index 00000000..759a85d5 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersData.cs @@ -0,0 +1,90 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + +namespace VFolders +{ + public class VFoldersData : ScriptableObject + { + public SerializableDictionary folderDatas_byGuid = new SerializableDictionary(); + + [System.Serializable] + public class FolderData + { + public int colorIndex; + public string iconNameOrGuid = ""; + + } + + + + + [CustomEditor(typeof(VFoldersData))] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = EditorStyles.label; + style.wordWrap = true; + + + void normal() + { + if (storeDataInMetaFiles) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("This file contains data about which icons and colors are assigned to folders", style); + + Space(6); + GUILayout.Label("If there are multiple people working on the project, you might want to store this data in .meta files of folders to avoid merge conflicts. To do that, click the ... button at the top right corner and click 'Store data in .meta files of folders' ", style); + + EndIndent(10); + ResetGUIEnabled(); + } + void meta() + { + if (!storeDataInMetaFiles) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("vFolders currently stores data in .meta files of folders", style); + + Space(6); + GUILayout.Label("If you want this data to be stored in this file, click the ... button at the top right corner and click 'Store data in .meta files' ", style); + + EndIndent(10); + ResetGUIEnabled(); + } + + normal(); + meta(); + + + } + } + + public static bool storeDataInMetaFiles { get => EditorPrefs.GetBool("vFolders-storeDataInMetaFilesEnabled", false); set => EditorPrefs.SetBool("vFolders-storeDataInMetaFilesEnabled", value); } + + [ContextMenu("Store data in .meta files of folders")] + public void MigrateDataBetweenMetaFilesAndSO() => storeDataInMetaFiles = !storeDataInMetaFiles; // todo + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersData.cs.meta b/Assets/Third Parties/vFolders/VFoldersData.cs.meta new file mode 100644 index 00000000..30bf11c4 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adb9c12a8c6c949f7af83a3d238359d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersIconEditor.cs b/Assets/Third Parties/vFolders/VFoldersIconEditor.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersIconEditor.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vFolders/VFoldersIconEditor.cs.meta b/Assets/Third Parties/vFolders/VFoldersIconEditor.cs.meta new file mode 100644 index 00000000..759e2127 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersIconEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc2d205dd3ec1446d843e566b05c2eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersLibs.cs b/Assets/Third Parties/vFolders/VFoldersLibs.cs new file mode 100644 index 00000000..8f09ba10 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersLibs.cs @@ -0,0 +1,2107 @@ + +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Reflection; +using System.Linq; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Experimental.Rendering; +using UnityEditor; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + + +namespace VFolders.Libs +{ + public static class VUtils + { + #region Text + + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + + + + + #endregion + + #region IEnumerables + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + public static int LastIndex(this List l) => l.Count - 1; // toremove + + // public static T GetAtWrapped(this List list, int i) // toremove + // { + // while (i < 0) i += list.Count; + // while (i >= list.Count) i -= list.Count; + + // return list[i]; + // } + + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + + public static bool None(this IEnumerable ie, System.Func f) => !ie.Any(f); + + public static bool None(this IEnumerable ie) => !ie.Any(); + + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + + + public static TValue GetValueOrDefault(this Dictionary dic, TKey key) => dic.ContainsKey(key) ? dic[key] : default; + + + + + + + #endregion + + #region Reflection + + public static object GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + public static object GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + public static object GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + + public static void SetFieldValue(this object o, string fieldName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in '{type.Name}' type and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in '{type.Name}' type and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in '{type.Name}' type and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in '{type.Name}' type, its parent types and interfaces"); + + } + + + + static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + static Dictionary> fieldInfoCache = new Dictionary>(); + + static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + static Dictionary> propertyInfoCache = new Dictionary>(); + + static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + static Dictionary> methodInfoCache = new Dictionary>(); + + + + public static T GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) => (T)o.GetFieldValue(fieldName, exceptionIfNotFound); + public static T GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) => (T)o.GetPropertyValue(propertyName, exceptionIfNotFound); + public static T GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) => (T)o.GetMemberValue(memberName, exceptionIfNotFound); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + #endregion + + #region Math + + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + public static bool CloseTo(this float f1, float f2, float distance) => f1.DistTo(f2) <= distance; + public static float DistTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Dist(float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Avg(float f1, float f2) => (f1 + f2) / 2; + public static float Abs(this float f) => Mathf.Abs(f); + public static int Abs(this int f) => Mathf.Abs(f); + public static float Sign(this float f) => Mathf.Sign(f); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new Vector2(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new Vector3(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + + + public static Color Greyscale(float brightness, float alpha = 1) => new Color(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + public static Color MultiplyAlpha(this Color color, float k) { color.a = k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Vectors + + + public static Vector2 AddX(this Vector2 v, float f) => new Vector2(v.x + f, v.y + 0); + public static Vector2 AddY(this Vector2 v, float f) => new Vector2(v.x + 0, v.y + f); + + public static Vector3 AddX(this Vector3 v, float f) => new Vector3(v.x + f, v.y + 0, v.z + 0); + public static Vector3 AddY(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + f, v.z + 0); + public static Vector3 AddZ(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + 0, v.z + f); + + public static Vector2 xx(this Vector3 v) { return new Vector2(v.x, v.x); } + public static Vector2 xy(this Vector3 v) { return new Vector2(v.x, v.y); } + public static Vector2 xz(this Vector3 v) { return new Vector2(v.x, v.z); } + public static Vector2 yx(this Vector3 v) { return new Vector2(v.y, v.x); } + public static Vector2 yy(this Vector3 v) { return new Vector2(v.y, v.y); } + public static Vector2 yz(this Vector3 v) { return new Vector2(v.y, v.z); } + public static Vector2 zx(this Vector3 v) { return new Vector2(v.z, v.x); } + public static Vector2 zy(this Vector3 v) { return new Vector2(v.z, v.y); } + public static Vector2 zz(this Vector3 v) { return new Vector2(v.z, v.z); } + + + + + + #endregion + + #region Textures + + + public static Texture2D CreateTexture2D(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false) + { + return new Texture2D(width, height, graphicsFormat, useMips ? TextureCreationFlags.MipChain : TextureCreationFlags.None); + } + + public static RenderTexture CreateRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = new RenderTexture(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + public static RenderTexture GetTemporaryRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = RenderTexture.GetTemporary(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor) => new RenderTexture(descriptor); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.CreateRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor) => RenderTexture.GetTemporary(descriptor); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.GetTemporaryRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + + public static void ReleaseTemporary(this RenderTexture rt) { if (rt) RenderTexture.ReleaseTemporary(rt); } + + + + public static Texture2D ToTexture2D(this RenderTexture rt) + { + var texture2D = CreateTexture2D(rt.width, rt.height, rt.graphicsFormat, rt.useMipMap); + + texture2D.ReadPixelsFrom(rt); + texture2D.Apply(); + + return texture2D; + + } + public static RenderTexture ToRenderTexture(this Texture2D texture2d) + { + var rt = CreateRT(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, rt); + + return rt; + + } + + + public static void ReadPixelsFrom(this Texture2D texture2D, RenderTexture renderTexture) + { + var prevActive = RenderTexture.active; + + RenderTexture.active = renderTexture; + + texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + + RenderTexture.active = prevActive; + + } + // public static void CopyTo(this RenderTexture source, Texture2D target) // todo to readpixels overload + // { + // var prevActive = RenderTexture.active; + + // RenderTexture.active = source; + + // target.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); + // target.Apply(); + + // RenderTexture.active = prevActive; + + + // // somewhere in unity source code reading is done like this, but it throws out of bounds read exception on win: + + // // if (!SystemInfo.graphicsUVStartsAtTop || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) + // // texture2d.ReadPixels(new Rect(0, 0, texture2d.width, texture2d.height), 0, 0); + // // else + // // texture2d.ReadPixels(new Rect(0, texture2d.height, texture2d.width, texture2d.height), 0, 0); + + // // this was used in the legacy rt.CopyToTexture2D extension method + + // } + + public static Texture2D CreateCopy(this Texture2D texture2d) + { + var copy = CreateTexture2D(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, copy); + + return copy; + + } + public static Texture2D CreateResizedCopy(this Texture2D texture2d, int w, int h) + { + var rt = GetTemporaryRT(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), false); + + Graphics.Blit(texture2d, rt); + + + var resizedCopy = CreateTexture2D(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), texture2d.mipmapCount > 1); + + resizedCopy.ReadPixelsFrom(rt); + resizedCopy.Apply(); + + if (RenderTexture.active == rt) + RenderTexture.active = null; + + rt.ReleaseTemporary(); + + + return resizedCopy; + + } + + + public static void FillWithColor(this Texture2D texture2d, Color color) + { + var pixels = new Color32[texture2d.width * texture2d.height]; + + var color32 = (Color32)color; + + for (int i = 0; i < pixels.Length; i++) + pixels[i] = color32; + + texture2d.SetPixels32(pixels); + texture2d.Apply(); + + } + public static RenderTexture FillWithColor(this RenderTexture rt, Color color) // todo builtin shader or GL.clear + { + var mat = new Material(Shader.Find("Hidden/VBlitColor")); + + mat.SetColor("_color", color); + + Graphics.Blit(null, rt, mat); + + mat.Destroy(); + + return rt; + + } + + + + public static GraphicsFormat GetCompatibleForRendering(this GraphicsFormat graphicsFormat) + { +#if UNITY_2023_2_OR_NEWER + return SystemInfo.GetCompatibleFormat(graphicsFormat, GraphicsFormatUsage.Render); +#else + return SystemInfo.GetCompatibleFormat(graphicsFormat, FormatUsage.Render); +#endif + + } + + + +#if UNITY_EDITOR + + public static void SavePNG(this Texture2D texture2d, string path) => File.WriteAllBytes(path, texture2d.EncodeToPNG()); + + public static void SetImportSettings(this Texture2D texture2d, int? maxSize = null, bool? useMips = null, bool? sRGB = null, bool? isReadable = null, bool? useCompression = null) + { + var importer = texture2d.GetImporter(); + + if (useCompression != null) + importer.textureCompression = useCompression.GetValueOrDefault() ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; + + if (sRGB != null) + importer.sRGBTexture = sRGB.GetValueOrDefault(); + + if (maxSize != null) + importer.maxTextureSize = maxSize.GetValueOrDefault(); + + if (useMips != null) + importer.mipmapEnabled = useMips.GetValueOrDefault(); + + + // if (texture2d.format == TextureFormat.R16 || texture2d.format == TextureFormat.RG32) + if (texture2d.format == TextureFormat.R16) + { + var platformSettings = importer.GetDefaultPlatformTextureSettings(); + + platformSettings.format = TextureImporterFormat.R16; + + if (maxSize != null) + platformSettings.maxTextureSize = maxSize.GetValueOrDefault(); + + importer.SetPlatformTextureSettings(platformSettings); + + } + + } + + public static TextureImporter GetImporter(this Texture2D t) => (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + +#endif + + + + + + + + #endregion + + #region Compute + + [System.Serializable] + public class GaussianKernel + { + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + } + + + + #endregion + + #region Drawing + + + public static void DrawLine(Vector3 v0, Vector3 v1, Color color = default, Camera cam = null, int layer = 0, bool drawAboveAll = false) + { + if (color == default) + color = Color.white; + + var mesh = new Mesh(); + mesh.SetVertices(new[] { v0, v1 }); + mesh.SetIndices(new[] { 0, 1 }, MeshTopology.Lines, 0); + + Graphics.DrawMesh(mesh, Vector3.zero, Quaternion.identity, _GetDrawingMat(color, drawAboveAll), layer, cam, 0); + + } + public static void DrawDot(Vector3 position, float size = 1, Color color = default, Camera cam = null, int layer = 0, bool drawAboveAll = false) + { + if (color == default) + color = Color.white; + + if (!_sphereMesh) + { + int latitudes = 8; + int longtitudes = 10; + + Vector3 point(int la, int lo) => Quaternion.AngleAxis(360f * lo / (longtitudes - 1), Vector3.up) * Quaternion.AngleAxis(180f * la / (latitudes - 1), Vector3.right) * Vector3.up / 2; + int index(int la, int lo) => la * longtitudes + (lo == longtitudes ? 0 : lo); + + var verts = new List(); + var tris = new List(); + + for (int la = 0; la < latitudes; la++) + for (int lo = 0; lo < longtitudes; lo++) + { + verts.Add(point(la, lo)); + + if (la == latitudes - 1) continue; + + tris.Add(index(la, lo)); + tris.Add(index(la + 1, lo)); + tris.Add(index(la + 1, lo + 1)); + + tris.Add(index(la, lo)); + tris.Add(index(la + 1, lo + 1)); + tris.Add(index(la, lo + 1)); + } + + _sphereMesh = new Mesh(); + _sphereMesh.SetVertices(verts); + _sphereMesh.SetIndices(tris, MeshTopology.Triangles, 0); + } + ; + Graphics.DrawMesh(_sphereMesh, Matrix4x4.TRS(position, Quaternion.identity, Vector3.one * size), _GetDrawingMat(color, drawAboveAll), layer, cam, 0); + + } + + static Material _GetDrawingMat(Color color, bool zTestAlways) + { + var p = new DrawingMatParams { color = color, zTestAlways = zTestAlways }; + + if (!_drawingMatsByParams.ContainsKey(p)) + { + _drawingMatsByParams[p] = new Material(Shader.Find("Hidden/Internal-Colored")); + _drawingMatsByParams[p].SetColor("_Color", color); + _drawingMatsByParams[p].SetInt("_ZTest", zTestAlways ? 0 : 4); + } + + return _drawingMatsByParams[p]; + } + static Mesh _sphereMesh; + struct DrawingMatParams { public Color color; public bool zTestAlways; } + static Dictionary _drawingMatsByParams = new Dictionary(); + + + + + + #endregion + + #region GameObjects + + + public static bool IsPrefab(this GameObject go) => go.scene.name == null || go.scene.name == go.name; + + public static Bounds GetBounds(this GameObject go, bool local = false) + { + Bounds bounds = default; + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? r.gameObject.GetComponent().sharedMesh.bounds : r.bounds; + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(b); + } + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? new Bounds(r.terrainData.size / 2, r.terrainData.size) : new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size); + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size)); + + } + + if (bounds == default) + bounds.center = go.transform.position; + + return bounds; + } + + + + + + #endregion + + #region Transform + + + public static List GetChildren(this Transform t) + { + var l = new List(); + for (int i = 0; i < t.childCount; i++) + l.Add(t.GetChild(i)); + + return l; + } + + public static Transform ResetLocals(this Transform t) + { + t.localPosition = t.localEulerAngles = Vector3.zero; + t.localScale = Vector3.one; + return t; + } + + public static Transform DestroyChildren(this Transform t) + { + while (t.childCount > 0) + if (Application.isPlaying) + Object.Destroy(t.GetChild(0).gameObject); + else + Object.DestroyImmediate(t.GetChild(0).gameObject); + + return t; + + } + + + + + + #endregion + + #region Camera + + + public static float FOVByFocusArea(float focusDistance, float focusArea, float cameraAspectRatio) => 2 * Mathf.Atan(Mathf.Sqrt(focusArea / focusDistance / focusDistance / cameraAspectRatio) / 2) * Mathf.Rad2Deg; + public static float FOVByFocusHeight(float focusDistance, float focusHeight) => 2 * Mathf.Atan(focusHeight / focusDistance / 2) * Mathf.Rad2Deg; + public static float FOVByFocusWidth(float focusDistance, float focusWidth, float cameraAspectRatio) => 2 * Mathf.Atan(focusWidth * cameraAspectRatio / focusDistance / 2) * Mathf.Rad2Deg; + public static float FOVByFocusHeightWidthMax(float focusDistance, float focusWidth, float focusHeight, float cameraAspectRatio) => 2 * Mathf.Atan(Mathf.Max(focusWidth * cameraAspectRatio, focusHeight) / focusDistance / 2) * Mathf.Rad2Deg; + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + } + + public static GlobalID GetGlobalID(this Object o) => new GlobalID(o); + + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + } + + +#endif + + + + + #endregion + + #region Paths + + + public static string GetParentPath(this string path) => path.Substring(0, path.LastIndexOf('/')); + public static bool HasParentPath(this string path) => path.Contains('/') && path.GetParentPath() != ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + public static string CombinePath(this string p, string p2) => Path.Combine(p, p2); + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + +#endif + + + + + + #endregion + + #region Serialization + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + public List keys = new List(); + public List values = new List(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + public static void ToggleDefineDisabledInScript(Type scriptType) + { + var path = GetScriptPath(scriptType.Name); + + var lines = File.ReadAllLines(path); + if (lines.First().StartsWith("#define DISABLED")) + File.WriteAllLines(path, lines.Skip(1)); + else + File.WriteAllLines(path, lines.Prepend("#define DISABLED // this line was added by VUtils.ToggleDefineDisabledInScript")); + + AssetDatabase.ImportAsset(path); + } + public static bool ScriptHasDefineDisabled(Type scriptType) => File.ReadLines(GetScriptPath(scriptType.Name)).First().StartsWith("#define DISABLED"); + public static void SetDefineDisabledInScript(Type scriptType, bool defineDisabled) + { + if (ScriptHasDefineDisabled(scriptType) != defineDisabled) + ToggleDefineDisabledInScript(scriptType); + + } + + public static int GetProjectId() => Application.dataPath.GetHashCode(); + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void RecordUndo(this Object o) + { + Undo.RecordObject(o, ""); + } +#if UNITY_2021_1_OR_NEWER + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); +#else + public static void Save(this Object o) { } +#endif + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + + public static void RemoveEditorErrors() => removeEditorErrorsMethod.Invoke(null, new object[] { 1 }); + static MethodInfo removeEditorErrorsMethod = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(r => r.GetName().ToString().Contains("UnityEditor.CoreModule")).GetTypes().First(r => r.Name.Contains("LogEntry")).GetMethod("RemoveLogEntriesByMode", BindingFlags.Static | BindingFlags.NonPublic); + + +#endif + + + + + + #endregion + + } + + public static class VGUI + { + #region Colors + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + #endregion + + #region Shortcuts + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fotSize) + { + SetLabelFontSize(fotSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + + public static void SetGUIColor(Color c) + { + if (!_guiColorModified) + _defaultGuiColor = GUI.color; + + _guiColorModified = true; + + GUI.color = _defaultGuiColor * c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorModified ? _defaultGuiColor : Color.white; + + _guiColorModified = false; + + } + static bool _guiColorModified; + static Color _defaultGuiColor; + + + + #endregion + + #region Events + + public struct WrappedEvent + { + public Event e; + + public bool isNull => e == null; + public bool isRepaint => isNull ? default : e.type == EventType.Repaint; + public bool isLayout => isNull ? default : e.type == EventType.Layout; + public bool isUsed => isNull ? default : e.type == EventType.Used; + public bool isMouseLeaveWindow => isNull ? default : e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => isNull ? default : e.type == EventType.MouseEnterWindow; + public bool isContextClick => isNull ? default : e.type == EventType.ContextClick; + + public bool isKeyDown => isNull ? default : e.type == EventType.KeyDown; + public bool isKeyUp => isNull ? default : e.type == EventType.KeyUp; + public KeyCode keyCode => isNull ? default : e.keyCode; + public char characted => isNull ? default : e.character; + + public bool isExecuteCommand => isNull ? default : e.type == EventType.ExecuteCommand; + public string commandName => isNull ? default : e.commandName; + + public bool isMouse => isNull ? default : e.isMouse; + public bool isMouseDown => isNull ? default : e.type == EventType.MouseDown; + public bool isMouseUp => isNull ? default : e.type == EventType.MouseUp; + public bool isMouseDrag => isNull ? default : e.type == EventType.MouseDrag; + public bool isMouseMove => isNull ? default : e.type == EventType.MouseMove; + public bool isScroll => isNull ? default : e.type == EventType.ScrollWheel; + public int mouseButton => isNull ? default : e.button; + public int clickCount => isNull ? default : e.clickCount; + public Vector2 mousePosition => isNull ? default : e.mousePosition; + public Vector2 mousePosition_screenSpace => isNull ? default : GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => isNull ? default : e.delta; + + public bool isDragUpdate => isNull ? default : e.type == EventType.DragUpdated; + public bool isDragPerform => isNull ? default : e.type == EventType.DragPerform; + public bool isDragExit => isNull ? default : e.type == EventType.DragExited; + + public EventModifiers modifiers => isNull ? default : e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => isNull ? default : e.alt; + public bool holdingShift => isNull ? default : e.shift; + public bool holdingCtrl => isNull ? default : e.control; + public bool holdingCmd => isNull ? default : e.command; + public bool holdingCmdOrCtrl => isNull ? default : e.command || e.control; + + public bool holdingAltOnly => isNull ? default : e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => isNull ? default : e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => isNull ? default : e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => isNull ? default : e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => isNull ? default : (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + public static WrappedEvent Wrap(this Event e) => new WrappedEvent(e); + public static WrappedEvent curEvent => (Event.current ?? _fi_s_Current.GetValue(null) as Event).Wrap(); // todo no reflection? + static FieldInfo _fi_s_Current = typeof(Event).GetField("s_Current", maxBindingFlags); + + // public static Event e => Event.current; + // public static bool ePresent => Event.current != null; + // public static UnityEngine.EventType eType => ePresent ? e.type : UnityEngine.EventType.Ignore; + // public static bool mouseDown(this Event e) => curEvent.isMouseDown && curEvent.mouseButton == 0; + // public static bool mouseUp(this Event e) => curEvent.isMouseUp && curEvent.mouseButton == 0; + // public static bool keyDown(this Event e) => curEvent.isKeyDown; + // public static bool keyUp(this Event e) => eType == EventType.KeyUp; + + + // public static bool holdingAlt => ePresent && (e.alt); + // public static bool holdingCmd => ePresent && (e.command || e.control); + // public static bool holdingShift => ePresent && (e.shift); + + + + + + + #endregion + + #region Layout + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new Stack(); + + + public static void Horizontal() { if (__hor) GUILayout.EndHorizontal(); else GUILayout.BeginHorizontal(); __hor = !__hor; } + public static void Vertical() { if (__v) GUILayout.EndVertical(); else GUILayout.BeginVertical(); __v = !__v; } + public static void Area(Rect r) { if (__a) GUILayout.EndArea(); else GUILayout.BeginArea(r); __a = !__a; } + public static void Area() { if (__a) GUILayout.EndArea(); __a = !__a; } + public static void ResetUIBools() { __a = __hor = __v = false; _prevGuiEnabled = true; } + static bool __hor, __a, __v; + + #endregion + + #region Drawing + + public static Rect Draw(this Rect r) { EditorGUI.DrawRect(r, Color.black); return r; } + public static Rect Draw(this Rect r, Color c) { EditorGUI.DrawRect(r, c); return r; } + + public static Rect DrawOutline(this Rect r) => r.DrawOutline(Color.black); + public static Rect DrawOutline(this Rect r, Color c) { OutlineRect(r, c, false, 1); return r; } + + public static void OutlineRect(Rect rect, Color col, bool greyedOut = false, int px = 1) + { + bool offset = false; + + int f = px; + Rect r; + // + Color tint = greyedOut ? Color.white * .74f : Color.white; + + r = rect; + r.height = f; + + if (offset) + { + r.x += 1; + r.width -= 2; + r.y += 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.width = f; + + if (offset) + { + r.y += 1; + r.height -= 2; + r.x += 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.y += r.height; + r.height = f; + r.y -= r.height; + + if (offset) + { + r.x += 1; + r.width -= 2; + r.y -= 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.x += r.width; + r.width = f; + r.x -= r.width; + + if (offset) + { + r.y += 1; + r.height -= 2; + r.x -= 1; + } + + EditorGUI.DrawRect(r, col * tint); + } + + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, float cornerRadius) => rect.DrawWithRoundedCorners(color, cornerRadius.RoundToInt()); + static Dictionary _roundedStylesByCornerRadius = new Dictionary(); + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + if (textureWidth <= 0) return rect; // happens on 2021.1.28 + if (textureHeight <= 0) return rect; // happens on 2021.1.28 + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = new GaussianKernel(false, blurRadiusScaled).Array2d(); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new Dictionary<(int, int), GUIStyle>(); + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + static Texture2D[] _gradientTextures; + + + + public static bool IsHovered(this Rect r) => !curEvent.isNull && r.Contains(curEvent.mousePosition); + + #endregion + + #region Spacing + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static void Divider(float space = 15, float yOffset = 0) + { + GUILayout.Label("", GUILayout.Height(space), GUILayout.ExpandWidth(true)); + lastRect.SetHeightFromMid(1).SetWidthFromMid(lastRect.width - 16).MoveY(yOffset).Draw(isDarkTheme ? Color.white * .42f : Color.white * .72f); + } + + public static Rect ExpandSpace() { GUILayout.Label("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); return lastRect; } + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + #endregion + + #region Icons + + + public static class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath) + { + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult)) return cachedResult; + + var icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + return icons_byName[iconNameOrPath] = icon; + + } + + static Dictionary icons_byName = new Dictionary(); + } + + + + + #endregion + + #region Other + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + + #endregion + + } + +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersLibs.cs.meta b/Assets/Third Parties/vFolders/VFoldersLibs.cs.meta new file mode 100644 index 00000000..0c41ce9a --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2528d4639fe84d44bfdfde62c65dc86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersMenu.cs b/Assets/Third Parties/vFolders/VFoldersMenu.cs new file mode 100644 index 00000000..4a76b4ff --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersMenu.cs @@ -0,0 +1,132 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + +namespace VFolders +{ + class VFoldersMenu + { + + public static bool hierarchyLinesEnabled { get => EditorPrefs.GetBool("vFolders-hierarchyLinesEnabled", false); set => EditorPrefs.SetBool("vFolders-hierarchyLinesEnabled", value); } + public static bool clearerRowsEnabled { get => EditorPrefs.GetBool("vFolders-clearerRowsEnabled", false); set => EditorPrefs.SetBool("vFolders-clearerRowsEnabled", value); } + public static bool minimalModeEnabled { get => EditorPrefs.GetBool("vFolders-minimalModeEnabled", false); set => EditorPrefs.SetBool("vFolders-minimalModeEnabled", value); } + public static bool zebraStripingEnabled { get => EditorPrefs.GetBool("vFolders-zebraStripingEnabled", false); set => EditorPrefs.SetBool("vFolders-zebraStripingEnabled", value); } + public static bool contentMinimapEnabled { get => EditorPrefs.GetBool("vFolders-contentMinimapEnabled", false); set => EditorPrefs.SetBool("vFolders-contentMinimapEnabled", value); } + public static bool autoIconsEnabled { get => EditorPrefs.GetBool("vFolders-autoIconsEnabled", false); set => EditorPrefs.SetBool("vFolders-autoIconsEnabled", value); } + public static bool foldersFirstEnabled { get => EditorPrefs.GetBool("vFolders-foldersFirstEnabled", false); set => EditorPrefs.SetBool("vFolders-foldersFirstEnabled", value); } + + public static bool toggleExpandedEnabled { get => EditorPrefs.GetBool("vFolders-toggleExpandedEnabled", true); set => EditorPrefs.SetBool("vFolders-toggleExpandedEnabled", value); } + public static bool collapseEverythingElseEnabled { get => EditorPrefs.GetBool("vFolders-collapseEverythingElseEnabled", true); set => EditorPrefs.SetBool("vFolders-collapseEverythingElseEnabled", value); } + public static bool collapseEverythingEnabled { get => EditorPrefs.GetBool("vFolders-collapseEverythingEnabled", true); set => EditorPrefs.SetBool("vFolders-collapseEverythingEnabled", value); } + + public static bool pluginDisabled { get => EditorPrefs.GetBool("vFolders-pluginDisabled", false); set => EditorPrefs.SetBool("vFolders-pluginDisabled", value); } + + + + + const string dir = "Tools/vFolders/"; + + const string hierarchyLines = dir + "Hierarchy lines"; + const string clearerRows = dir + "Clearer rows"; + const string minimalMode = dir + "Minimal mode"; + const string zebraStriping = dir + "Zebra striping"; + const string autoIcons = dir + "Automatic icons"; + const string contentMinimap = dir + "Content minimap"; + const string foldersFirst = dir + "Sort folders first"; + + const string toggleExpanded = dir + "E to expand or collapse"; + const string collapseEverythingElse = dir + "Shift-E to collapse everything else"; + const string collapseEverything = dir + "Ctrl-Shift-E to collapse everything"; + + const string disablePlugin = dir + "Disable vFolders"; + + + + + + + [MenuItem(dir + "Features", false, 1)] static void daasddsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsdasas123() => false; + + [MenuItem(hierarchyLines, false, 2)] static void dadsadadsadass() { hierarchyLinesEnabled = !hierarchyLinesEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(hierarchyLines, true, 2)] static bool dadsaddasaasddsas() { Menu.SetChecked(hierarchyLines, hierarchyLinesEnabled); return !pluginDisabled; } + + [MenuItem(clearerRows, false, 3)] static void dadsadadsadsadass() { clearerRowsEnabled = !clearerRowsEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(clearerRows, true, 3)] static bool dadsaddasadsaasddsas() { Menu.SetChecked(clearerRows, clearerRowsEnabled); return !pluginDisabled; } + + [MenuItem(minimalMode, false, 4)] static void dadsadadsaddsasadass() { minimalModeEnabled = !minimalModeEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(minimalMode, true, 4)] static bool dadsaddasadsadsaasddsas() { Menu.SetChecked(minimalMode, minimalModeEnabled); return !pluginDisabled; } + + [MenuItem(zebraStriping, false, 5)] static void dadsadaddsasadsadass() { zebraStripingEnabled = !zebraStripingEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(zebraStriping, true, 5)] static bool dadsaddadassadsaasddsas() { Menu.SetChecked(zebraStriping, zebraStripingEnabled); return !pluginDisabled; } + + [MenuItem(contentMinimap, false, 6)] static void dadsadadasdsadass() { contentMinimapEnabled = !contentMinimapEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(contentMinimap, true, 6)] static bool dadsadddasasaasddsas() { Menu.SetChecked(contentMinimap, contentMinimapEnabled); return !pluginDisabled; } + + [MenuItem(autoIcons, false, 7)] static void dadsadadsas() { autoIconsEnabled = !autoIconsEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(autoIcons, true, 7)] static bool dadsaddasadsas() { Menu.SetChecked(autoIcons, autoIconsEnabled); return !pluginDisabled; } +#if UNITY_EDITOR_OSX + [MenuItem(foldersFirst, false, 8)] static void dadsdsfaadsdadsas() { foldersFirstEnabled = !foldersFirstEnabled; EditorApplication.RepaintProjectWindow(); if (!foldersFirstEnabled) UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(foldersFirst, true, 8)] static bool dadsasdfdadsdasadsas() { Menu.SetChecked(foldersFirst, foldersFirstEnabled); return !pluginDisabled; } +#endif + + + + [MenuItem(dir + "Shortcuts", false, 101)] static void dadsas() { } + [MenuItem(dir + "Shortcuts", true, 101)] static bool dadsas123() => false; + + [MenuItem(toggleExpanded, false, 102)] static void dadsadsadasdsadadsas() => toggleExpandedEnabled = !toggleExpandedEnabled; + [MenuItem(toggleExpanded, true, 102)] static bool dadsaddsasadadsdasadsas() { Menu.SetChecked(toggleExpanded, toggleExpandedEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverythingElse, false, 103)] static void dadsadsasdadasdsadadsas() => collapseEverythingElseEnabled = !collapseEverythingElseEnabled; + [MenuItem(collapseEverythingElse, true, 103)] static bool dadsaddsdasasadadsdasadsas() { Menu.SetChecked(collapseEverythingElse, collapseEverythingElseEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverything, false, 104)] static void dadsadsdasadasdsadadsas() => collapseEverythingEnabled = !collapseEverythingEnabled; + [MenuItem(collapseEverything, true, 104)] static bool dadsaddssdaasadadsdasadsas() { Menu.SetChecked(collapseEverything, collapseEverythingEnabled); return !pluginDisabled; } + + + + + + + [MenuItem(dir + "More", false, 1001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 1001)] static bool dadsadsdasas123() => false; + + [MenuItem(dir + "Join our Discord", false, 1002)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/4dG9KsbspG"); + + + + [MenuItem(dir + "Check out vFavorites 2", false, 1003)] + static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=checkoutvfav"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vHierarchy 2", false, 1003)] + // static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/251320?aid=1100lGLBn&pubref=menucheckout"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vTabs 2", false, 1004)] + // static void dadadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263645?aid=1100lGLBn&pubref=menucheckout"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vFavorites 2", false, 1005)] + // static void dadadsadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=menucheckout"); + + + + + + [MenuItem(disablePlugin, false, 10001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 10001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + + + + // [MenuItem(dir + "Clear cache", false, 10001)] + // static void dassaadsdc() => VFoldersCache.Clear(); + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersMenu.cs.meta b/Assets/Third Parties/vFolders/VFoldersMenu.cs.meta new file mode 100644 index 00000000..19b54b39 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f118e09f35fa241fda19f1f3b1e7c5a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersMenuItems.cs b/Assets/Third Parties/vFolders/VFoldersMenuItems.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vFolders/VFoldersMenuItems.cs.meta b/Assets/Third Parties/vFolders/VFoldersMenuItems.cs.meta new file mode 100644 index 00000000..4bdc039c --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec6d8bc24e3ec4cb6b055630a6425686 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersOutline.shader b/Assets/Third Parties/vFolders/VFoldersOutline.shader new file mode 100644 index 00000000..e102ce9c --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersOutline.shader @@ -0,0 +1,85 @@ +Shader "Hidden/VFoldersOutline" +{ + Properties + { + _MainTex ("Texture", Any) = "white" {} + [HDR] _Color ("Tint", Color) = (1,1,1,1) + } + + CGINCLUDE + + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + + struct appdata_t + { + float4 vertex : POSITION; + float2 texcoord : TEXCOORD0; + UNITY_VERTEX_INPUT_INSTANCE_ID + }; + + struct v2f + { + float4 vertex : SV_POSITION; + float2 texcoord : TEXCOORD0; + float2 clipUV : TEXCOORD1; + }; + + sampler2D _MainTex; + sampler2D _GUIClipTexture; + bool _ManualTex2SRGB; + float4 _MainTex_ST; + float4 _Color; + float4x4 unity_GUIClipTextureMatrix; + + v2f vert (appdata_t v) + { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + o.vertex = UnityObjectToClipPos(v.vertex); + float3 eyePos = UnityObjectToViewPos(v.vertex); + o.clipUV = mul(unity_GUIClipTextureMatrix, float4(eyePos.xy, 0, 1.0)); + o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + float4 c = tex2D(_MainTex, i.texcoord); + c.rgb = _Color.rgb; + c.a *= _Color.a; + c.a *= tex2D(_GUIClipTexture, i.clipUV).a; + return c; + } + + ENDCG + + SubShader + { + Blend SrcAlpha OneMinusSrcAlpha, One One + Cull Off + ZWrite Off + ZTest Always + + Pass + { + CGPROGRAM + ENDCG + } + } + + SubShader + { + Blend SrcAlpha OneMinusSrcAlpha + Cull Off + ZWrite Off + ZTest Always + + Pass + { + CGPROGRAM + ENDCG + } + } +} diff --git a/Assets/Third Parties/vFolders/VFoldersOutline.shader.meta b/Assets/Third Parties/vFolders/VFoldersOutline.shader.meta new file mode 100644 index 00000000..7f9fee3c --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersOutline.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 8a3e8e1270d3f40688b73ded8c1e3087 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersPalette.cs b/Assets/Third Parties/vFolders/VFoldersPalette.cs new file mode 100644 index 00000000..57478727 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPalette.cs @@ -0,0 +1,246 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditorInternal; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + +namespace VFolders +{ + public class VFoldersPalette : ScriptableObject + { + public List colors = new List(); + + public bool colorsEnabled; + + public void ResetColors() + { + colors.Clear(); + + for (int i = 0; i < colorsCount; i++) + colors.Add(GetDefaultColor(i)); + + colorsEnabled = true; + + this.Dirty(); + + } + + public static Color GetDefaultColor(int colorIndex) + { + Color color = default; + + void grey() + { + if (colorIndex >= greyColorsCount) return; + +#if UNITY_2022_1_OR_NEWER + color = Greyscale(EditorGUIUtility.isProSkin ? .16f : .9f); +#else + color = Greyscale(EditorGUIUtility.isProSkin ? .315f : .9f); +#endif + + } + void rainbowDarkTheme() + { + if (colorIndex < greyColorsCount) return; + if (!isDarkTheme) return; + + + var t = (colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount; + + if (colorIndex == 0) + t += .01f; + + if (colorIndex == 1) + t -= .02f; + + if (colorIndex == 2) + t -= .015f; + + if (colorIndex == 3) + t -= .01f; + + if (colorIndex == 4) + t += .02f; + + if (colorIndex == 5) + t += .01f; + + + if (colorIndex == 8) + t -= .01f; + + + // color = HSLToRGB(t, .61f, .57f); + color = HSLToRGB(t, .61f, .57f); + + + if (colorIndex == 0) + color *= 1.16f; + + if (colorIndex == 1) + color *= 1.17f; + + if (colorIndex == 2) + color *= 1.03f; + + if (colorIndex == 6) + color *= 1.2f; + + if (colorIndex == 7) + color *= 1.55f; + + if (colorIndex == 8) + color *= 1.2f; + + if (colorIndex == 9) + color *= 1.08f; + + + color.a = .1f; + + } + void rainbowLightTheme() + { + if (colorIndex < greyColorsCount) return; + if (isDarkTheme) return; + + color = HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .62f, .8f); + + // color.a = .1f; + color.a = 1f; + + } + + grey(); + rainbowDarkTheme(); + rainbowLightTheme(); + + return color; + + } + + public static int greyColorsCount = 0; + public static int rainbowColorsCount = 10; + public static int colorsCount => greyColorsCount + rainbowColorsCount; + + + + + public List iconRows = new List(); + + [System.Serializable] + public class IconRow + { + public List builtinIcons = new List(); // names + public List customIcons = new List(); // guids + + public bool enabled = true; + + public bool isCustom => !builtinIcons.Any() || customIcons.Any(); + public bool isEmpty => !builtinIcons.Any() && !customIcons.Any(); + public int iconCount => builtinIcons.Count + customIcons.Count; + + public IconRow(string[] builtinIcons) => this.builtinIcons = builtinIcons.ToList(); + public IconRow() { } + + } + + public void ResetIcons() + { + iconRows.Clear(); + + iconRows.Add(new IconRow(new[] + { + "SceneAsset Icon", + "Prefab Icon", + "PrefabModel Icon", + "Material Icon", + "Texture Icon", + "Mesh Icon", + "cs Script Icon", + "Shader Icon", + "ComputeShader Icon", + "ScriptableObject Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Light Icon", + "LightProbes Icon", + "LightmapParameters Icon", + "LightingDataAsset Icon", + "Cubemap Icon" + + })); + iconRows.Add(new IconRow(new[] + { + "PhysicMaterial Icon", + "BoxCollider Icon", + "TerrainCollider Icon", + "MeshCollider Icon", + "WheelCollider Icon", + "Rigidbody Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "AudioClip Icon", + "AudioMixerController Icon", + "AudioMixerGroup Icon", + "AudioEchoFilter Icon", + "AudioSource Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "TextAsset Icon", + "AssemblyDefinitionAsset Icon", + "TerrainData Icon", + "Terrain Icon", + "AnimatorController Icon", + "AnimationClip Icon", + "Font Icon", + "RawImage Icon", + "Settings Icon", + + })); + + this.Dirty(); + + } + + + + + [ContextMenu("Export palette")] + public void Export() + { + var packagePath = EditorUtility.SaveFilePanel("Export vHierarchy Palette", "", this.GetPath().GetFilename(withExtension: false), "unitypackage"); + + var iconPaths = iconRows.SelectMany(r => r.customIcons).Select(r => r.ToPath()).Where(r => !r.IsNullOrEmpty()); + + AssetDatabase.ExportPackage(iconPaths.Append(this.GetPath()).ToArray(), packagePath); + + EditorUtility.RevealInFinder(packagePath); + + } + + + + + void Reset() { ResetColors(); ResetIcons(); } + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersPalette.cs.meta b/Assets/Third Parties/vFolders/VFoldersPalette.cs.meta new file mode 100644 index 00000000..318740f0 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPalette.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 618f730fcd14941fab38169a7dde8c64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs b/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs new file mode 100644 index 00000000..9f23d778 --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs @@ -0,0 +1,723 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEditorInternal; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +using static VFolders.VFoldersPalette; + + +namespace VFolders +{ + [CustomEditor(typeof(VFoldersPalette))] + public class VFoldersPaletteEditor : Editor + { + public override void OnInspectorGUI() + { + void colors() + { + var rowRect = ExpandWidthLabelRect(cellSize).SetX(rowsOffsetX).SetWidth(rowWidth); + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void toggle() + { + var toggleRect = rowRect.SetWidth(16).MoveX(5); + + var prevEnabled = palette.colorsEnabled; + var newEnabled = EditorGUI.Toggle(toggleRect, palette.colorsEnabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + palette.colorsEnabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void crossIcon() + { + var crossIconRect = rowRect.SetX(rowsOffsetX + iconsOffsetX + iconSpacing / 2).SetWidth(iconSize).SetHeightFromMid(iconSize); + + SetGUIColor(palette.colorsEnabled ? Color.white : disabledRowTint); + SetLabelAlignmentCenter(); + + GUI.Label(crossIconRect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetGUIColor(); + ResetLabelStyle(); + + } + void color(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + (i + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundPicking() + { + if (!pickingColor) return; + if (i != pickingColorAtIndex) return; + + cellRect.DrawWithRoundedCorners(pickingBackground, 2); + + } + void color() + { + var tint = palette.colorsEnabled ? Color.white : disabledRowTint; + + var brightness = 1.00f; + var outlineColor = Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawWithRoundedCorners(outlineColor * tint, 4); + cellRect.Resize(4).DrawWithRoundedCorners(palette.colors[i].SetAlpha(1) * brightness * tint, 3); + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - palette.colors[i].a) * .5f)); + + } + void startPickingColorButton() + { + if (!palette.colorsEnabled) return; + if (!cellRect.IsHovered()) return; + if (pickingColor) return; + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + GUI.Label(cellRect.Resize(.5f), EditorGUIUtility.IconContent("Preset.Context")); + + + if (!clicked) return; + + colorPicker = OpenColorPicker((c) => { palette.RecordUndo(); palette.Dirty(); palette.colors[i] = c; }, palette.colors[i], showAlpha: true, false); + + colorPicker.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingColor = true; + pickingColorAtIndex = i; + + } + void updatePickingColor() + { + if (!pickingColor) return; + + EditorApplication.RepaintProjectWindow(); + + } + void stopPickingColor() + { + if (!pickingColor) return; + if (colorPicker) return; + + pickingColor = false; + + } + + + cellRect.MarkInteractive(); + + backgroundPicking(); + color(); + startPickingColorButton(); + updatePickingColor(); + stopPickingColor(); + + } + + backgroundHovered(); + toggle(); + crossIcon(); + + for (int i = 0; i < palette.colors.Count; i++) + color(i); + + Space(rowSpacing - 2); + + } + void icons() + { + void row(Rect rowRect, IconRow row) + { + var isLastRow = row == palette.iconRows.Last(); + var isDraggedRow = row == draggedRow; + var spaceForCrossIcon = 0f; + + + void startPickingIcon(int i, Rect cellRect) + { + iconPicker = OpenObjectPicker(AssetDatabase.LoadAssetAtPath(row.customIcons[i].ToPath()), controlID: 123); + + iconPicker.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingIcon = true; + pickingIconAtIndex = i; + pickingIconAtRow = row; + + } + void updatePickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (EditorGUIUtility.GetObjectPickerControlID() != 123) return; + if (pickingIconAtIndex >= row.customIcons.Count) return; // somehow happens if RecordUndo is used + + palette.RecordUndo(); + palette.Dirty(); + + row.customIcons[pickingIconAtIndex] = (EditorGUIUtility.GetObjectPickerObject() as Texture2D).GetPath().ToGuid(); + + } + void stopPickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (iconPicker) return; + + if (pickingIconAtIndex < row.customIcons.Count) + if (row.customIcons[pickingIconAtIndex] == null) + row.customIcons.RemoveAt(pickingIconAtIndex); + + pickingIcon = false; + + } + void dragndrop() + { + if (!rowRect.IsHovered()) return; + + if (curEvent.isDragUpdate && DragAndDrop.objectReferences.First() is Texture2D) + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + if (!curEvent.isDragPerform) return; + if (!(DragAndDrop.objectReferences.Any(r => r is Texture2D))) return; + + DragAndDrop.AcceptDrag(); + + palette.RecordUndo(); + palette.Dirty(); + + foreach (var icon in DragAndDrop.objectReferences.Where(r => r is Texture2D)) + row.customIcons.Add(icon.GetPath().ToGuid()); + + } + + void calcSpaceForCrossIcon() + { + if (row == curFirstEnabledRow) + spaceForCrossIcon = crossIconAnimationT * cellSize; + + if (row == crossIconAnimationSourceRow) + spaceForCrossIcon = (1 - crossIconAnimationT) * cellSize; + + } + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (pickingIcon) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void backgroundDragged() + { + if (!isDraggedRow) return; + + rowRect.DrawBlurred(Greyscale(0, .3f), 12); + rowRect.Draw(draggedRowBackground); + + } + void toggle() + { + var prevEnabled = row.enabled; + var newEnabled = EditorGUI.Toggle(rowRect.SetWidth(16).MoveX(5), row.enabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + row.enabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void addIconButton() + { + if (!row.isCustom) return; + + var cellRect = rowRect.MoveX(iconsOffsetX + row.customIcons.Count * cellSize + spaceForCrossIcon).SetWidth(cellSize).SetHeightFromMid(cellSize); + + + + SetGUIColor(Greyscale(1, row.enabled ? 1 : .5f)); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + SetGUIColor(Greyscale(1, row.enabled ? 1 : .5f)); + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.Resize(1), EditorGUIUtility.IconContent("Toolbar Plus")); + + ResetLabelStyle(); + ResetGUIColor(); + + + + if (!clicked) return; + + palette.RecordUndo(); + + row.customIcons.Add(null); + + startPickingIcon(row.customIcons.Count - 1, cellRect); + + } + void icon(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + var isCustomIcon = i > row.builtinIcons.Count - 1; + + void backgroundPicking() + { + if (!pickingIcon) return; + if (row != pickingIconAtRow) return; + if (i != pickingIconAtIndex) return; + + cellRect.Resize(1).DrawWithRoundedCorners(pickingBackground, 2); + + } + void drawBuiltin() + { + if (isCustomIcon) return; + + SetLabelAlignmentCenter(); + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent(row.builtinIcons[i])); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void drawCustom() + { + if (!isCustomIcon) return; + if (cellRect.IsHovered()) return; + if (!(AssetDatabase.LoadAssetAtPath(row.customIcons[i - row.builtinIcons.Count].ToPath()) is Texture2D texture)) return; + + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), texture); + + ResetGUIColor(); + + } + void editCustomButton() + { + if (!isCustomIcon) return; + if (!cellRect.IsHovered()) return; + if (pickingIcon) return; + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + GUI.Label(cellRect.Resize(.5f), EditorGUIUtility.IconContent("Preset.Context")); + + + + if (!clicked) return; + + GenericMenu menu = new GenericMenu(); + + menu.AddItem(new GUIContent("Replace icon"), false, () => { palette.RecordUndo(); palette.Dirty(); startPickingIcon(i, cellRect.MoveY(75)); }); + menu.AddItem(new GUIContent("Remove icon"), false, () => { palette.RecordUndo(); row.customIcons.RemoveAt(i); palette.Dirty(); }); + + menu.ShowAsContext(); + + + + } + + cellRect.MarkInteractive(); + + backgroundPicking(); + drawBuiltin(); + drawCustom(); + editCustomButton(); + + } + + + rowRect.MarkInteractive(); + + updatePickingIcon(); + stopPickingIcon(); + dragndrop(); + + calcSpaceForCrossIcon(); + backgroundHovered(); + backgroundDragged(); + toggle(); + addIconButton(); + + for (int i = 0; i < row.iconCount; i++) + icon(i); + + } + + void updateRowsCount() + { + palette.iconRows.RemoveAll(r => r.isEmpty && r != palette.iconRows.Last()); + + if (!palette.iconRows.Last().isEmpty) + palette.iconRows.Add(new IconRow()); + + } + void updateRowGapsCount() + { + while (rowGaps.Count < palette.iconRows.Count) + rowGaps.Add(0); + + while (rowGaps.Count > palette.iconRows.Count) + rowGaps.RemoveLast(); + + } + + void normalRow(int i) + { + Space(rowGaps[i] * (cellSize + rowSpacing)); + + if (i == 0 && lastRect.y != 0) + firstRowY = lastRect.y; + + Space(cellSize + rowSpacing); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, lastRect.y).SetSize(rowWidth, cellSize); + + if (curEvent.isRepaint) + if (rowRect.IsHovered()) + hoveredRow = palette.iconRows[i]; + + + row(rowRect, palette.iconRows[i]); + + } + void draggedRow_() + { + if (!draggingRow) return; + + draggedRowY = (curEvent.mousePosition.y + draggedRowHoldOffset).Clamp(firstRowY, firstRowY + (palette.iconRows.Count - 1) * (cellSize + rowSpacing)); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, draggedRowY).SetSize(rowWidth, cellSize); + + row(rowRect, draggedRow); + + } + void crossIcon() + { + if (!palette.iconRows.Any(r => r.enabled)) return; + + var rect = Rect.zero.SetPos(rowsOffsetX + iconsOffsetX, crossIconY).SetSize(cellSize, cellSize).Resize(iconSpacing / 2); + + SetLabelAlignmentCenter(); + + GUI.Label(rect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + + + updateRowsCount(); + updateRowGapsCount(); + + + if (curEvent.isRepaint) + hoveredRow = null; + + for (int i = 0; i < palette.iconRows.Count; i++) + normalRow(i); + + crossIcon(); + + draggedRow_(); + + + } + void tutor() + { + SetGUIEnabled(false); + + + GUILayout.Label("Click a color to edit it"); + + Space(4); + GUILayout.Label("Click '+' to add a custom icon"); + + Space(4); + GUILayout.Label("Click a custom icon to replace or remove it"); + + Space(4); + GUILayout.Label("Drag rows to reorder them"); + + + ResetGUIEnabled(); + + } + + + Space(15); + colors(); + + Space(15); + icons(); + + Space(25); + tutor(); + + UpdateAnimations(); + + UpdateDragging(); + + palette.Dirty(); + + if (draggingRow || animatingCrossIcon) + Repaint(); + + } + + float iconSize => 18; + float iconSpacing => 2; + float cellSize => iconSize + iconSpacing; + float rowSpacing = 1; + float rowsOffsetX => 14; + float iconsOffsetX => 27; + + Color hoveredRowBackground => Greyscale(isDarkTheme ? 1 : 0, .05f); + Color draggedRowBackground => Greyscale(isDarkTheme ? .3f : .9f); + Color pickingBackground => new Color(.3f, .5f, .7f, .8f);// Greyscale(1, .17f); + Color colorOutline => Greyscale(.2f, .5f); + Color disabledRowTint => Greyscale(1, .45f); + + float rowWidth => cellSize * Mathf.Max(palette.colors.Count, palette.iconRows.Max(r => r.iconCount + 1)) + 55; + + bool pickingColor; + int pickingColorAtIndex; + EditorWindow colorPicker; + + bool pickingIcon; + int pickingIconAtIndex; + IconRow pickingIconAtRow; + EditorWindow iconPicker; + + IconRow hoveredRow; + + float firstRowY = 51; + + + + + + + void UpdateAnimations() + { + void calcDeltaTime() + { + if (!curEvent.isLayout) return; + + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + + void lerpRowGaps() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = draggingRow ? 12 : 12321; + + for (int i = 0; i < rowGaps.Count; i++) + rowGaps[i] = Lerp(rowGaps[i], draggingRow && i == insertDraggedRowAtIndex ? 1 : 0, lerpSpeed, deltaTime);// todo deltatime + + for (int i = 0; i < rowGaps.Count; i++) + if (rowGaps[i].Approx(0)) + rowGaps[i] = 0; + else if (rowGaps[i].Approx(1)) + rowGaps[i] = 1; + + + } + + void lerpCrossIconAnimationT() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = 12; + + Lerp(ref crossIconAnimationT, 1, lerpSpeed, deltaTime); + + } + void startCrossIconAnimation() + { + if (prevFirstEnabledRow == null) { prevFirstEnabledRow = curFirstEnabledRow; return; } + if (prevFirstEnabledRow == curFirstEnabledRow) return; + + crossIconAnimationT = 0; + crossIconAnimationSourceRow = prevFirstEnabledRow; + + prevFirstEnabledRow = curFirstEnabledRow; + + } + void stopCrossIconAnimation() + { + if (!crossIconAnimationT.Approx(1)) return; + + crossIconAnimationT = 1; + crossIconAnimationSourceRow = null; + + } + void calcCrossIconY() + { + var indexOfFirstEnabled = palette.iconRows.IndexOfFirst(r => r.enabled); + var yOfFirstEnabled = firstRowY + indexOfFirstEnabled * (cellSize + rowSpacing); + for (int i = 0; i < indexOfFirstEnabled + 1; i++) + yOfFirstEnabled += rowGaps[i] * (cellSize + rowSpacing); + + + var indexOfSourceRow = palette.iconRows.IndexOf(crossIconAnimationSourceRow); + var yOfSourceRow = firstRowY + indexOfSourceRow * (cellSize + rowSpacing); + for (int i = 0; i < indexOfSourceRow + 1; i++) + yOfSourceRow += rowGaps[i] * (cellSize + rowSpacing); + + if (crossIconAnimationSourceRow == draggedRow) + yOfSourceRow = draggedRowY; + + + crossIconY = Lerp(yOfSourceRow, yOfFirstEnabled, crossIconAnimationT); + + if (indexOfFirstEnabled == indexOfSourceRow) { crossIconAnimationT = 1; } + + } + + + calcDeltaTime(); + + lerpRowGaps(); + + lerpCrossIconAnimationT(); + startCrossIconAnimation(); + stopCrossIconAnimation(); + calcCrossIconY(); + + } + + List rowGaps = new List(); + + float deltaTime; + double lastLayoutTime; + + float crossIconY = 51; + float crossIconAnimationT = 1; + IconRow crossIconAnimationSourceRow; + bool animatingCrossIcon => crossIconAnimationT != 1; + + IconRow prevFirstEnabledRow; + IconRow curFirstEnabledRow => palette.iconRows.FirstOrDefault(r => r.enabled); + + + + + + + void UpdateDragging() + { + void startDragging() + { + if (draggingRow) return; + if (!curEvent.isMouseDrag) return; + if (hoveredRow == null) return; + if (hoveredRow == palette.iconRows.Last()) return; + + palette.RecordUndo(); + + draggingRow = true; + draggedRow = hoveredRow; + draggingRowFromIndex = palette.iconRows.IndexOf(hoveredRow); + draggedRowHoldOffset = firstRowY + draggingRowFromIndex * (cellSize + rowSpacing) - curEvent.mousePosition.y; + + palette.iconRows.Remove(hoveredRow); + rowGaps[draggingRowFromIndex] = 1; + + } + void updateDragging() + { + if (!draggingRow) return; + + insertDraggedRowAtIndex = ((curEvent.mousePosition.y - firstRowY) / (cellSize + rowSpacing)).FloorToInt().Clamp(0, palette.iconRows.Count - 1); + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!draggingRow) return; + if (!curEvent.isMouseUp) return; + + palette.RecordUndo(); + palette.Dirty(); + + palette.iconRows.AddAt(draggedRow, insertDraggedRowAtIndex); + + rowGaps[insertDraggedRowAtIndex] = 0; + + draggingRow = false; + draggedRow = null; + + EditorGUIUtility.hotControl = 0; + + } + + + startDragging(); + updateDragging(); + stopDragging(); + + } + + IconRow draggedRow; + bool draggingRow; + int draggingRowFromIndex; + float draggedRowHoldOffset; + float draggedRowY; + int insertDraggedRowAtIndex; + + + + + + + VFoldersPalette palette => target as VFoldersPalette; + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs.meta b/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs.meta new file mode 100644 index 00000000..2a387dfa --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPaletteEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 085d323b143d5403fb4beda728e65139 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs b/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs new file mode 100644 index 00000000..1e3b6c0b --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs @@ -0,0 +1,566 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +using static VFolders.VFolders; +using static VFolders.VFoldersData; +using static VFolders.VFoldersPalette; +using static VFolders.VFoldersCache; + + +namespace VFolders +{ + public class VFoldersPaletteWindow : EditorWindow + { + void OnGUI() + { + if (!palette) { Close(); return; } + + int hoveredColorIndex = -1; + string hoveredIconNameOrGuid = null; + + void background() + { + position.SetPos(0, 0).Draw(windowBackground); + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + void colors() + { + if (!palette.colorsEnabled) { Space(-spaceAfterColors); return; } + + var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); + + void color(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundSelected() + { + if (!initialColorIndexes.Contains(i)) return; + + cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (i != 0) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + void color() + { + if (i == 0) return; + + var brightness = 1.00f; + var outlineColor = Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawWithRoundedCorners(outlineColor, 4); + cellRect.Resize(4).DrawWithRoundedCorners((palette.colors[i - 1] * brightness).SetAlpha(1), 3); + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - palette.colors[i - 1].a) * .45f)); + // cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - .1f) * .45f)); + + } + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredColorIndex = i; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseDown) return; + + Close(); + + } + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + color(); + setHovered(); + closeOnClick(); + + } + + for (int i = 0; i < palette.colors.Count + 1; i++) + color(i); + + } + void icons() + { + void row(IconRow iconRow) + { + if (!iconRow.enabled) return; + if (iconRow.isEmpty) return; + + var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); + var isFirstEnabledRow = palette.iconRows.First(r => r.enabled) == iconRow; + + void icon(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var isCrossIcon = isFirstEnabledRow && i == 0; + var actualIconIndex = isFirstEnabledRow ? i - 1 : i; + var isBuiltinIcon = !isCrossIcon && actualIconIndex < iconRow.builtinIcons.Count; + var isCustomIcon = !isCrossIcon && actualIconIndex >= iconRow.builtinIcons.Count; + var iconNameOrGuid = isCrossIcon ? "" : isCustomIcon ? iconRow.customIcons[actualIconIndex - iconRow.builtinIcons.Count] : iconRow.builtinIcons[actualIconIndex]; + + void backgroundSelected() + { + if (!initialIconNamesOrGuids.Contains(iconNameOrGuid)) return; + + cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (!isCrossIcon) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + void builtinIcon() + { + if (!isBuiltinIcon) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent(iconNameOrGuid)); + + ResetLabelStyle(); + + } + void customIcon() + { + if (!isCustomIcon) return; + + var texture = AssetDatabase.LoadAssetAtPath(iconNameOrGuid.ToPath()); + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), texture ?? Texture2D.blackTexture); + + } + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredIconNameOrGuid = iconNameOrGuid; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseDown) return; + + Close(); + + } + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + builtinIcon(); + customIcon(); + setHovered(); + closeOnClick(); + + } + + for (int i = 0; i < iconRow.iconCount + (isFirstEnabledRow ? 1 : 0); i++) + icon(i); + + Space(rowSpacing - 2); + + } + + for (int i = 0; i < palette.iconRows.Count; i++) + row(palette.iconRows[i]); + + } + + void setColorsAndIcons() + { + if (!curEvent.isRepaint) return; + + + if (palette.iconRows.Any(r => r.enabled)) + if (hoveredIconNameOrGuid != null) + SetIcon(hoveredIconNameOrGuid); + else + SetInitialIcons(); + + + if (palette.colorsEnabled) + if (hoveredColorIndex != -1) + SetColor(hoveredColorIndex); + else + SetInitialColors(); + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + var speed = 9; + + SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + position = position.SetPos(currentPosition); + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + if (!currentPosition.magnitude.Approx(targetPosition.magnitude)) + Repaint(); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + SetInitialColors(); + SetInitialIcons(); + + Close(); + + } + + + RecordUndoOnDatas(); + + background(); + outline(); + + Space(paddingY); + colors(); + + Space(spaceAfterColors); + icons(); + + setColorsAndIcons(); + updatePosition(); + closeOnEscape(); + + EditorApplication.RepaintProjectWindow(); + + EditorApplication.delayCall += EditorApplication.RepaintProjectWindow; // to show icons that will be generated in update + + } + + static float iconSize => 18; + static float iconSpacing => 2; + static float cellSize => iconSize + iconSpacing; + static float spaceAfterColors => 11; + public float rowSpacing = 1; + static float paddingX => 12; + static float paddingY => 12; + + Color windowBackground => isDarkTheme ? Greyscale(.23f) : Greyscale(.7f); + Color selectedBackground => isDarkTheme ? new Color(.3f, .5f, .7f, .8f) : new Color(.3f, .5f, .7f, .4f) * 1.35f; + Color hoveredBackground = Greyscale(1, .3f); + Color colorOutline => Greyscale(.2f, .5f); + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + + + + + + void SetIcon(string iconNameOrGuid) + { + foreach (var r in folderInfos) + r.iconNameOrGuid = iconNameOrGuid; + } + void SetColor(int colorIndex) + { + foreach (var r in folderInfos) + r.folderData.colorIndex = colorIndex; + } + + void SetInitialIcons() + { + for (int i = 0; i < folderInfos.Count; i++) + folderInfos[i].iconNameOrGuid = initialIconNamesOrGuids[i]; + } + void SetInitialColors() + { + for (int i = 0; i < folderInfos.Count; i++) + folderInfos[i].folderData.colorIndex = initialColorIndexes[i]; + } + + void RemoveEmptyFolderDatas() + { + if (VFoldersData.storeDataInMetaFiles) return; // empties removed from meta files in SaveData() + + var toRemove = folderInfos.Select(r => r.folderData).Where(r => r.iconNameOrGuid == "" && r.colorIndex == 0); + + foreach (var r in toRemove) + data.folderDatas_byGuid.RemoveValue(r); + + if (toRemove.Any()) + Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); + + } + + void RecordUndoOnDatas() + { + if (!VFoldersData.storeDataInMetaFiles) + if (data) + data.RecordUndo(); + + if (VFoldersData.storeDataInMetaFiles) + foreach (var r in guids) + AssetImporter.GetAtPath(r.ToPath()).RecordUndo(); + + } + void MarkDatasDirty() + { + if (!VFoldersData.storeDataInMetaFiles) + if (data) + data.Dirty(); + + if (VFoldersData.storeDataInMetaFiles) + VFolders.folderDatasFromMetaFiles_byGuid.Clear(); + + } + void SaveData() + { + if (!VFoldersData.storeDataInMetaFiles) { data.Save(); return; } + + for (int i = 0; i < guids.Count; i++) + AssetImporter.GetAtPath(guids[i].ToPath()).userData = folderInfos[i].folderData.iconNameOrGuid == "" && folderInfos[i].folderData.colorIndex == 0 ? "" : JsonUtility.ToJson(folderInfos[i].folderData); + + for (int i = 0; i < guids.Count; i++) + AssetImporter.GetAtPath(guids[i].ToPath()).SaveAndReimport(); + + } + + + + + + + + void OnLostFocus() + { + if (curEvent.holdingAlt && EditorWindow.focusedWindow?.GetType().Name == "ProjectBrowser") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + + + + + + + + + + public void Init(List guids) + { + void createData() + { + if (VFolders.data) return; + + VFolders.data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VFolders.data, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Data.asset")); + + } + void createPalette() + { + if (VFolders.palette) return; + + VFolders.palette = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VFolders.palette, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Palette.asset")); + + } + void setSize() + { + var rowCellCounts = new List(); + + if (palette.colorsEnabled) + rowCellCounts.Add(palette.colors.Count + 1); + + foreach (var r in palette.iconRows.Where(r => r.enabled)) + rowCellCounts.Add(r.iconCount + (r == palette.iconRows.First(r => r.enabled) ? 1 : 0)); + + var width = rowCellCounts.Max() * cellSize + paddingX * 2; + + + + var iconRowCount = palette.iconRows.Count(r => r.enabled && !r.isEmpty); + var rowCount = iconRowCount + (palette.colorsEnabled ? 1 : 0); + + var height = rowCount * (cellSize + rowSpacing) + (palette.colorsEnabled && palette.iconRows.Any(r => r.enabled && !r.isEmpty) ? spaceAfterColors : 0) + paddingY * 2; + + + + position = position.SetSize(width, height).SetPos(targetPosition); + + } + void getInfos() + { + folderInfos.Clear(); + + foreach (var r in guids) + folderInfos.Add(VFolders.GetFolderInfo(r, createDataIfDoesntExist: true)); + + } + void getInitColorsAndIcons() + { + initialColorIndexes.Clear(); + initialIconNamesOrGuids.Clear(); + + foreach (var r in folderInfos) + initialColorIndexes.Add(r.folderData.colorIndex); + + foreach (var r in folderInfos) + initialIconNamesOrGuids.Add(r.iconNameOrGuid); + + } + + + this.guids = guids; + + RecordUndoOnDatas(); + + createData(); + createPalette(); + setSize(); + getInfos(); + getInitColorsAndIcons(); + + Undo.undoRedoPerformed -= EditorApplication.RepaintProjectWindow; + Undo.undoRedoPerformed += EditorApplication.RepaintProjectWindow; + + } + + void OnDestroy() + { + RemoveEmptyFolderDatas(); + MarkDatasDirty(); + SaveData(); + + } + + public List guids = new List(); + public List folderInfos = new List(); + + public List initialColorIndexes = new List(); + public List initialIconNamesOrGuids = new List(); + + static VFoldersPalette palette => VFolders.palette; + static VFoldersData data => VFolders.data; + + + + + + + + public static void CreateInstance(Vector2 position) + { + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + + instance.position = instance.position.SetPos(position).SetSize(200, 300); + instance.targetPosition = position; + + } + + public static VFoldersPaletteWindow instance; + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs.meta b/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs.meta new file mode 100644 index 00000000..6f8ecd4c --- /dev/null +++ b/Assets/Third Parties/vFolders/VFoldersPaletteWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68011e63381a84f3bacee6e5ee9e0b82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy.meta b/Assets/Third Parties/vHierarchy.meta new file mode 100644 index 00000000..0d51a571 --- /dev/null +++ b/Assets/Third Parties/vHierarchy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd2b9e231b0ec4a9f97e258072ae2b33 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/Manual.pdf b/Assets/Third Parties/vHierarchy/Manual.pdf new file mode 100644 index 00000000..80efc767 Binary files /dev/null and b/Assets/Third Parties/vHierarchy/Manual.pdf differ diff --git a/Assets/Third Parties/vHierarchy/Manual.pdf.meta b/Assets/Third Parties/vHierarchy/Manual.pdf.meta new file mode 100644 index 00000000..8bed03f4 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/Manual.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d8e44b0f278584fbfa8ae00d01b2ce3f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchy.asmdef b/Assets/Third Parties/vHierarchy/VHierarchy.asmdef new file mode 100644 index 00000000..22702c62 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchy.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VHierarchy", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchy.asmdef.meta b/Assets/Third Parties/vHierarchy/VHierarchy.asmdef.meta new file mode 100644 index 00000000..d859f2d1 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchy.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2c3f48364a5004fd3a152fbdf5fea703 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchy.cs b/Assets/Third Parties/vHierarchy/VHierarchy.cs new file mode 100644 index 00000000..aba87682 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchy.cs @@ -0,0 +1,1855 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using UnityEditor.Experimental.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyCache; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + public static class VHierarchy + { + static void GameObjectRowGUI(GameObject go, Rect rowRect) + { + var fullRowRect = rowRect.SetX(32).SetXMax(rowRect.xMax + 16); + + var isRowHovered = fullRowRect.AddWidthFromRight(32).IsHovered(); + + + var isRowSelected = false; + var isRowBeingRenamed = false; + var isTreeFocused = false; + + void setState() + { + void set_isRowSelected() + { + if (!curEvent.isRepaint) return; + +#if UNITY_2021_1_OR_NEWER + var dragSelectionList = treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List"); +#else + var dragSelectionList = treeViewController?.GetFieldValue>("m_DragSelection"); +#endif + + var dragging = dragSelectionList != null && dragSelectionList.Any(); + + isRowSelected = dragging ? (dragSelectionList.Contains(go.GetInstanceID())) : Selection.Contains(go); + + } + void set_isRowBeingRenamed() + { + if (!curEvent.isRepaint) return; + + isRowBeingRenamed = EditorGUIUtility.editingTextField && + isRowSelected && + treeViewController?.GetMemberValue("state")?.GetMemberValue("renameOverlay")?.InvokeMethod("IsRenaming") == true; + + } + void set_isTreeFocused() + { + if (!curEvent.isRepaint) return; + + isTreeFocused = EditorWindow.focusedWindow == hierarchyWindow && + GUIUtility.keyboardControl == hierarchyWindow?.GetMemberValue("sceneHierarchy")?.GetMemberValue("m_TreeViewKeyboardControlID"); + + } + void set_lastVisibleSelectedRowRect() + { + if (!Selection.gameObjects.Contains(go)) return; + + lastVisibleSelectedRowRect = rowRect; + + } + void set_mousePressed() + { + if (curEvent.isMouseDown && isRowHovered) + mousePressed = true; + + if (curEvent.isMouseUp || curEvent.isMouseLeaveWindow || curEvent.isDragPerform) + mousePressed = false; + + } + void set_hoveredGo() + { + if (curEvent.isLayout) + hoveredGo = null; + + if (curEvent.isRepaint && isRowHovered) + hoveredGo = go; + + } + + set_isRowSelected(); + set_isRowBeingRenamed(); + set_isTreeFocused(); + set_lastVisibleSelectedRowRect(); + set_mousePressed(); + set_hoveredGo(); + + } + + + void drawing() + { + if (!curEvent.isRepaint) { hierarchyLines_isFirstRowDrawn = false; return; } + + var goData = GetGameObjectData(go, createDataIfDoesntExist: false); + + var showBackgroundColor = goData != null && goData.colorIndex.IsInRange(1, VHierarchyPalette.colorsCount);// && !(isRowSelected && isHierarchyFocused); + var showCustomIcon = goData != null && !goData.iconNameOrGuid.IsNullOrEmpty(); + var showDefaultIcon = !showCustomIcon && (isRowBeingRenamed || (!VHierarchyMenu.minimalModeEnabled || (PrefabUtility.IsAddedGameObjectOverride(go) && PrefabUtility.IsPartOfPrefabInstance(go)))); + + var makeTriangleBrighter = showBackgroundColor && goData.colorIndex > VHierarchyPalette.greyColorsCount && isDarkTheme; + var makeNameBrighter = showBackgroundColor && goData.colorIndex > VHierarchyPalette.greyColorsCount && isDarkTheme; + + Color defaultBackground; + + + void calcDefaultBackground() + { + var selectedFocused = GUIColors.selectedBackground; + var selectedUnfocused = isDarkTheme ? Greyscale(.3f) : Greyscale(.68f); + var hovered = isDarkTheme ? Greyscale(.265f) : Greyscale(.7f); + var normal = GUIColors.windowBackground; + + if (isRowSelected && !isRowBeingRenamed) + defaultBackground = isTreeFocused ? selectedFocused : selectedUnfocused; + + else if (isRowHovered) + defaultBackground = hovered; + + else + defaultBackground = normal; + + } + void hideDefaultIcon() + { + if (showDefaultIcon) return; + + rowRect.SetWidth(16).Draw(defaultBackground); + + } + void hideName() + { + if (!showBackgroundColor && (showCustomIcon || showDefaultIcon)) return; + + var nameRect = rowRect.MoveX(16).SetWidth(go.name.GetLabelWidth()); +#if UNITY_2023_2_OR_NEWER + if (!go.activeInHierarchy && PrefabUtility.IsPartOfPrefabInstance(go)) + nameRect.width *= 1.1f; +#endif + + nameRect.Draw(defaultBackground); + + } + + void backgroundColor() + { + if (!showBackgroundColor) return; + + + var hasLeftGradient = go.transform.parent; + + + + var colorRect = rowRect.AddWidthFromRight(28).AddWidth(16); + + if (!isRowSelected) + colorRect = colorRect.AddHeightFromMid(EditorGUIUtility.pixelsPerPoint >= 2 ? -.5f : -1); + + if (hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(3); + + if (PrefabUtility.HasPrefabInstanceAnyOverrides(go, false) && !hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(EditorGUIUtility.pixelsPerPoint >= 2 ? -2.5f : -3); + + + + + var leftGradientWith = go.transform.parent ? 22 : 0; + var rightGradientWidth = (fullRowRect.width * .77f).Min(colorRect.width - leftGradientWith); + + var leftGradientRect = colorRect.SetWidth(leftGradientWith); + var rightGradientRect = colorRect.SetWidthFromRight(rightGradientWidth); + + var flatColorRect = colorRect.SetX(leftGradientRect.xMax).SetXMax(rightGradientRect.x); + + + + + var colorWithFlatness = palette ? palette.colors[goData.colorIndex - 1] : VHierarchyPalette.GetDefaultColor(goData.colorIndex - 1); + + var flatness = colorWithFlatness.a; + + var color = colorWithFlatness.SetAlpha(1); + + if (isRowHovered) + color *= 1.1f; + + if (isRowSelected) + color *= 1.2f; + + + + + leftGradientRect.AddWidth(1).Draw(color.SetAlpha((flatness - .1f) / .9f)); + leftGradientRect.AddWidth(1).DrawCurtainLeft(color); + + flatColorRect.AddWidth(1).Draw(color); + + rightGradientRect.Draw(color.MultiplyAlpha(flatness)); + rightGradientRect.DrawCurtainRight(color); + + + } + void triangle() + { + if (!showBackgroundColor) return; + if (go.transform.childCount == 0) return; + + var triangleRect = rowRect.MoveX(-15.5f).SetWidth(16).Resize(1.5f); + + GUI.DrawTexture(triangleRect, EditorIcons.GetIcon(IsExpanded(go) ? "IN_foldout_on" : "IN_foldout")); + + + if (!makeTriangleBrighter) return; + + GUI.DrawTexture(triangleRect, EditorIcons.GetIcon(IsExpanded(go) ? "IN_foldout_on" : "IN_foldout")); + + } + void name() + { + if (!showBackgroundColor && (showCustomIcon || showDefaultIcon)) return; + if (isRowBeingRenamed) return; + + + var nameRect = rowRect.MoveX(18); + + if (VHierarchyMenu.minimalModeEnabled && !showCustomIcon && !showDefaultIcon) + nameRect = nameRect.MoveX(-17); + + if (showBackgroundColor && goData.colorIndex <= VHierarchyPalette.greyColorsCount) + nameRect = nameRect.MoveY(.5f); + + if (!go.activeInHierarchy) // correcting unity's style padding inconsistencies + if (PrefabUtility.IsPartOfAnyPrefab(go)) + nameRect = nameRect.MoveY(-1); + else + nameRect = nameRect.Move(-1, -1.5f); + + if (makeNameBrighter && go.activeInHierarchy) + nameRect = nameRect.MoveX(-2).MoveY(-.5f); + + + + var styleName = PrefabUtility.IsPartOfAnyPrefab(go) ? + (go.activeInHierarchy ? "PR PrefabLabel" : "PR DisabledPrefabLabel") : + (go.activeInHierarchy ? "TV Line" : "PR DisabledLabel"); + + if (makeNameBrighter && go.activeInHierarchy) + styleName = "WhiteLabel"; + + + + if (makeNameBrighter) + SetGUIColor(Greyscale(!go.activeInHierarchy ? 1.4f : isRowSelected ? 1 : .9f)); + + GUI.skin.GetStyle(styleName).Draw(nameRect, go.name, false, false, isRowSelected, hierarchyWindow == EditorWindow.focusedWindow); + + if (makeNameBrighter) + ResetGUIColor(); + + } + void defaultIcon() + { + if (!showBackgroundColor) return; + if (!showDefaultIcon) return; + + var iconRect = rowRect.SetWidth(16); + + SetGUIColor(go.activeInHierarchy ? Color.white : Greyscale(1, .4f)); + + GUI.DrawTexture(iconRect, PrefabUtility.GetIconForGameObject(go)); + + if (PrefabUtility.IsAddedGameObjectOverride(go)) + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("PrefabOverlayAdded Icon")); + + ResetGUIColor(); + + } + void customIcon() + { + if (!showCustomIcon) return; + + var iconRect = rowRect.SetWidth(16); + var iconNameOrPath = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + + SetGUIColor(go.activeInHierarchy ? Color.white : Greyscale(1, .4f)); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon(iconNameOrPath) ?? Texture2D.blackTexture); + + ResetGUIColor(); + + } + void hierarchyLines() + { + if (!VHierarchyMenu.hierarchyLinesEnabled) return; + + var lineThickness = 1f; + var lineContrast = isDarkTheme ? .35f : .55f; + + if (isRowSelected) + if (isTreeFocused) + lineContrast += isDarkTheme ? .1f : -.25f; + else + lineContrast += isDarkTheme ? .05f : -.05f; + + + var depth = ((rowRect.x - 60) / 14).RoundToInt(); + + bool isLastChild(Transform transform) => transform.parent?.GetChild(transform.parent.childCount - 1) == transform; + bool hasChilren(Transform transform) => transform.childCount > 0; + + void calcVerticalGaps_beforeFirstRowDrawn() + { + if (hierarchyLines_isFirstRowDrawn) return; + + hierarchyLines_verticalGaps.Clear(); + + var curTransform = go.transform.parent; + var curDepth = depth - 1; + + while (curTransform != null && curTransform.parent != null) + { + if (isLastChild(curTransform)) + hierarchyLines_verticalGaps.Add(curDepth - 1); + + curTransform = curTransform.parent; + curDepth--; + } + + } + void updateVerticalGaps_beforeNextRowDrawn() + { + if (isLastChild(go.transform)) + hierarchyLines_verticalGaps.Add(depth - 1); + + if (depth < hierarchyLines_prevRowDepth) + hierarchyLines_verticalGaps.RemoveAll(r => r >= depth); + + } + + void drawVerticals() + { + for (int i = 0; i < depth; i++) + if (!hierarchyLines_verticalGaps.Contains(i)) + rowRect.SetX(53 + i * 14 - lineThickness / 2) + .SetWidth(lineThickness) + .SetHeight(isLastChild(go.transform) && i == depth - 1 ? 8 + lineThickness / 2 : 16) + .Draw(Greyscale(lineContrast)); + + } + void drawHorizontals() + { + if (depth == 0) return; + + rowRect.MoveX(-21) + .SetHeightFromMid(lineThickness) + .SetWidth(hasChilren(go.transform) ? 7 : 17) + .Draw(Greyscale(lineContrast)); + + } + + + + calcVerticalGaps_beforeFirstRowDrawn(); + + drawVerticals(); + drawHorizontals(); + + updateVerticalGaps_beforeNextRowDrawn(); + + hierarchyLines_prevRowDepth = depth; + hierarchyLines_isFirstRowDrawn = true; + + } + void zebraStriping() + { + if (!VHierarchyMenu.zebraStripingEnabled) return; + if (isRowSelected) return; + if (goData?.colorIndex == 1) return; + + var contrast = isDarkTheme ? .033f : .05f; + + var t = rowRect.y.PingPong(16f) / 16f; + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, contrast * t)); + + } + + + calcDefaultBackground(); + hideDefaultIcon(); + hideName(); + + hierarchyLines(); + backgroundColor(); + triangle(); + name(); + defaultIcon(); + customIcon(); + zebraStriping(); + + } + + void componentMinimap() + { + if (!VHierarchyMenu.componentMinimapEnabled) return; + + void componentButton(Rect buttonRect, Component component) + { + void componentIcon() + { + if (!curEvent.isRepaint) return; + + + var normalOpacity = isDarkTheme ? .47f : .7f; + var activeOpacity = 1; + var pressedOpacity = isDarkTheme ? .65f : .9f; + + var isActive = (buttonRect.IsHovered() && curEvent.holdingAlt) || VHierarchyComponentWindow.floatingInstance?.component == component; + var isPressed = buttonRect.IsHovered() && mousePressed; + + var icon = GetComponentIcon(component); + + + if (!icon) return; + + SetGUIColor(Greyscale(1, isActive ? (isPressed ? pressedOpacity : activeOpacity) : normalOpacity)); + + GUI.DrawTexture(buttonRect.SetSizeFromMid(12, 12), icon); + + ResetGUIColor(); + + } + + void mouseDown() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseDown) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + mouseDownPos = curEvent.mousePosition; + + } + void mouseUp() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + if (VHierarchyComponentWindow.floatingInstance?.component == component) { VHierarchyComponentWindow.floatingInstance.Close(); return; } + + + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(rowRect.xMax + 25, rowRect.y)); + + if (!VHierarchyComponentWindow.floatingInstance) + VHierarchyComponentWindow.CreateFloatingInstance(position); + + VHierarchyComponentWindow.floatingInstance.Init(component); + VHierarchyComponentWindow.floatingInstance.Focus(); + + VHierarchyComponentWindow.floatingInstance.targetPosition = position; + + } + + + if (curEvent.holdingAlt) + buttonRect.MarkInteractive(); + + componentIcon(); + + mouseDown(); + mouseUp(); + + } + + void transformComponent() + { + if (!isRowHovered) return; + if (!curEvent.holdingAlt) return; + if (!go.GetComponent()) return; + + componentButton(fullRowRect.SetWidth(13).MoveX(1.5f), go.GetComponent()); + + } + void otherComponetns() + { + var buttonWidth = 13; + var minButtonX = rowRect.x + go.name.GetLabelWidth() + buttonWidth + 2; + var buttonRect = fullRowRect.SetWidthFromRight(buttonWidth).MoveX(-1.5f); + + if (PrefabUtility.IsAnyPrefabInstanceRoot(go) && !PrefabUtility.IsPartOfModelPrefab(go)) + buttonRect = buttonRect.MoveX(-13); + + foreach (var component in go.GetComponents()) + { + if (component is Transform) continue; + if (buttonRect.x < minButtonX) continue; + + componentButton(buttonRect, component); + + buttonRect = buttonRect.MoveX(-buttonWidth); + + } + + + } + + transformComponent(); + otherComponetns(); + + } + void activationToggle() + { + if (!VHierarchyMenu.activationToggleEnabled) return; + if (!isRowHovered) return; + if (curEvent.holdingAlt) return; + + var toggleRect = fullRowRect.SetWidth(16).MoveX(1); + + + SetGUIColor(Greyscale(1, .9f)); + + var newActiveSelf = EditorGUI.Toggle(toggleRect, go.activeSelf); + + ResetGUIColor(); + + + if (newActiveSelf == go.activeSelf) return; + + var gos = Selection.gameObjects.Contains(go) ? Selection.gameObjects : new[] { go }; + var newActive = gos != null && !gos.Any(r => r && r.activeSelf); + + foreach (var r in gos) + r.RecordUndo(); + + foreach (var r in gos) + r.SetActive(newActiveSelf); + + GUI.FocusControl(null); + + } + + void altDrag() + { + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!rowRect.IsHovered()) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!rowRect.Contains(mouseDownPos)) return; + if (!rowRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { go }; + DragAndDrop.StartDrag(go.name); + + } + + mouseDown(); + mouseDrag(); + + // altdrag has to be set up manually before altClick because altClick will use() mouseDown event to prevent selection change + } + void altClick() + { + if (!isRowHovered) return; + if (!curEvent.holdingAlt) return; + if (Application.isPlaying) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var editMultiSelection = Selection.gameObjects.Length > 1 && Selection.gameObjects.Contains(go); + + var gosToEdit = (editMultiSelection ? Selection.gameObjects : new[] { go }).ToList(); + + + if (VHierarchyPaletteWindow.instance && VHierarchyPaletteWindow.instance.gameObjects.SequenceEqual(gosToEdit)) { VHierarchyPaletteWindow.instance.Close(); return; } + + var openNearRect = editMultiSelection ? lastVisibleSelectedRowRect : rowRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(openNearRect.x - 14, openNearRect.y + 18)); + + if (!VHierarchyPaletteWindow.instance) + VHierarchyPaletteWindow.CreateInstance(position); + + VHierarchyPaletteWindow.instance.Init(gosToEdit); + VHierarchyPaletteWindow.instance.Focus(); + + VHierarchyPaletteWindow.instance.targetPosition = position; + + if (editMultiSelection) + Selection.objects = null; + + } + + mouseDown(); + mouseUp(); + + } + + + setState(); + + drawing(); + + componentMinimap(); + activationToggle(); + + altDrag(); + altClick(); + + } + + static List hierarchyLines_verticalGaps = new List(); + static bool hierarchyLines_isFirstRowDrawn; + static int hierarchyLines_prevRowDepth; + + static bool mousePressed; + static GameObject hoveredGo; + static Vector2 mouseDownPos; + + static Rect lastVisibleSelectedRowRect; + + + + + static void SceneRowGUI(Scene scene, Rect rowRect) + { + + void collapseAll() + { + if (!VHierarchyMenu.collapseAllButtonEnabled) return; + + var buttonRect = rowRect.SetWidthFromRight(18).MoveX(VHierarchyMenu.editLightingButtonEnabled ? -22 : -4); + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(buttonRect, ""); + + var normalColor = isDarkTheme ? Greyscale(.85f) : Greyscale(.1f); + var hoveredColor = isDarkTheme ? Color.white : normalColor; + + SetGUIColor(buttonRect.IsHovered() ? hoveredColor : normalColor); + GUI.Label(buttonRect.Resize(1.5f).MoveY(-.5f), EditorGUIUtility.IconContent("PreviewCollapse")); + + ResetGUIColor(); + + + if (!clicked) return; + + var expandedRoots = new List(); + var expandedChildren = new List(); + + foreach (var iid in expandedIds) + if (EditorUtility.InstanceIDToObject(iid) is GameObject expandedGo && expandedGo.scene == scene) + if (expandedGo.transform.parent) + expandedChildren.Add(expandedGo); + else + expandedRoots.Add(expandedGo); + + expandQueue_toCollapseAfterAnimation = expandedChildren; + expandQueue_toAnimate = expandedRoots.Select(r => new ExpandQueueEntry { instanceId = r.GetInstanceID(), expand = false }) + .OrderBy(r => VisibleRowIndex(r.instanceId)).ToList(); + + EditorApplication.RepaintHierarchyWindow(); + + } + void lighting() + { + if (!VHierarchyMenu.editLightingButtonEnabled) return; + + var buttonRect = rowRect.SetWidthFromRight(18).MoveX(-4); + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(buttonRect, ""); + + var normalColor = isDarkTheme ? Greyscale(.9f) : Greyscale(1f, .9f); + var hoveredColor = isDarkTheme ? Color.white : normalColor; + + SetGUIColor(buttonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(buttonRect.Resize(1).MoveY(-.5f), EditorGUIUtility.IconContent("Lighting")); + + ResetGUIColor(); + + + if (!clicked) return; + + VHierarchyLightingWindow.CreateInstance(EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition) + new Vector2(8, -8)); + + VHierarchyLightingWindow.instance.Focus(); + + } + + collapseAll(); + lighting(); + + } + + static void RowGUI(int instanceId, Rect rowRect) + { + if (EditorWindow.focusedWindow is EditorWindow focusedWindow) + if (focusedWindow.GetType() == t_SceneHierarchyWindow && focusedWindow != hierarchyWindow) + _hierarchyWindow = focusedWindow; // fixes wrong isTreeFocused value when there are multiple hierarchies + + + if (curEvent.isLayout) + UpdateExpandQueue(); + + if (expandedIds == null) + UpdateExpandedIdsList(); + + if (EditorUtility.InstanceIDToObject(instanceId) is GameObject go) + GameObjectRowGUI(go, rowRect); + else + { + var iScene = -1; + + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).GetHashCode() == instanceId) + iScene = i; + + if (iScene != -1) + SceneRowGUI(EditorSceneManager.GetSceneAt(iScene), rowRect); + } + + } + + + + + + static void CheckShortcuts() // globalEventHandler + { + if (EditorWindow.mouseOverWindow?.GetType() != t_SceneHierarchyWindow) return; + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode == KeyCode.None) return; + + + void updateHierarchyWindow() + { + if (hierarchyWindow == EditorWindow.mouseOverWindow) return; + + _hierarchyWindow = EditorWindow.mouseOverWindow; + + UpdateExpandedIdsList(); + + } + + void toggleExpanded() + { + if (!hoveredGo) return; + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (Tools.viewTool == ViewTool.FPS) return; + if (!VHierarchyMenu.toggleExpandedEnabled) return; + + curEvent.Use(); + + if (transformToolNeedsReset = Application.unityVersion.Contains("2022")) + previousTransformTool = Tools.current; + + + if (hoveredGo.transform.childCount == 0) return; + + SetExpandedWithAnimation(hoveredGo.GetInstanceID(), !expandedIds.Contains(hoveredGo.GetInstanceID())); + + EditorApplication.RepaintHierarchyWindow(); + + } + void toggleActive() + { + if (!hoveredGo) return; + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.A) return; + if (Tools.viewTool == ViewTool.FPS) return; + if (!VHierarchyMenu.toggleActiveEnabled) return; + + var gos = Selection.gameObjects.Contains(hoveredGo) ? Selection.gameObjects : new[] { hoveredGo }; + var active = !gos.Any(r => r.activeSelf); + + foreach (var r in gos) + { + r.RecordUndo(); + r.SetActive(active); + } + + curEvent.Use(); + + } + void delete() + { + if (!hoveredGo) return; + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.X) return; + if (!VHierarchyMenu.deleteEnabled) return; + + var gos = Selection.gameObjects.Contains(hoveredGo) ? Selection.gameObjects : new[] { hoveredGo }; + + foreach (var r in gos) + Undo.DestroyObjectImmediate(r); + + curEvent.Use(); + } + void collapseEverything() + { + if (curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Command) && curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Control)) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (!VHierarchyMenu.collapseEverythingEnabled) return; + + curEvent.Use(); + + var expandedRoots = new List(); + var expandedChildren = new List(); + + foreach (var iid in expandedIds) + if (EditorUtility.InstanceIDToObject(iid) is GameObject expandedGo) + if (expandedGo.transform.parent) + expandedChildren.Add(expandedGo); + else + expandedRoots.Add(expandedGo); + + expandQueue_toCollapseAfterAnimation = expandedChildren; + expandQueue_toAnimate = expandedRoots.Select(r => new ExpandQueueEntry { instanceId = r.GetInstanceID(), expand = false }) + .OrderBy(r => VisibleRowIndex(r.instanceId)).ToList(); + + EditorApplication.RepaintHierarchyWindow(); + + } + void collapseEverythingElse() + { + if (!hoveredGo) return; + if (curEvent.modifiers != EventModifiers.Shift) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (!VHierarchyMenu.collapseEverythingElseEnabled) return; + + curEvent.Use(); + + if (hoveredGo.transform.childCount == 0) return; + + var parents = new List(); + + var cur = hoveredGo; + while (cur = cur.transform.parent?.gameObject) + parents.Add(cur); + + var toCollapse = new List(); + + foreach (var iid in expandedIds.ToList()) + if (EditorUtility.InstanceIDToObject(iid) is GameObject expandedGo && !parents.Contains(expandedGo) && expandedGo != hoveredGo) + toCollapse.Add(expandedGo); + + + expandQueue_toAnimate = toCollapse.Select(r => new ExpandQueueEntry { instanceId = r.GetInstanceID(), expand = false }) + .Append(new ExpandQueueEntry { instanceId = hoveredGo.GetInstanceID(), expand = true }) + .OrderBy(r => VisibleRowIndex(r.instanceId)).ToList(); + + EditorApplication.RepaintHierarchyWindow(); + + + } + void focus() + { + if (!curEvent.isKeyDown) return; + if (curEvent.modifiers != EventModifiers.None) return; + if (curEvent.keyCode != KeyCode.F) return; + if (SceneView.sceneViews.Count == 0) return; + if (!hoveredGo) return; + if (!VHierarchyMenu.focusEnabled) return; + + var sv = SceneView.lastActiveSceneView; + + if (!sv || !sv.hasFocus) + sv = SceneView.sceneViews.ToArray().FirstOrDefault(r => (r as SceneView).hasFocus) as SceneView; + + if (!sv) + (sv = SceneView.lastActiveSceneView ?? SceneView.sceneViews[0] as SceneView).Focus(); + + sv.Frame(hoveredGo.GetBounds(), false); + + } + + + updateHierarchyWindow(); + + toggleExpanded(); + toggleActive(); + delete(); + collapseEverything(); + collapseEverythingElse(); + focus(); + + } + + + + static void UpdateExpandQueue() // called from gui because reflected methods rely on event.current + { + if (treeViewController.GetPropertyValue("animatingExpansion")) return; + + if (!expandQueue_toAnimate.Any()) + { + if (!expandQueue_toCollapseAfterAnimation.Any()) return; + + foreach (var r in expandQueue_toCollapseAfterAnimation) + SetExpanded(r.GetInstanceID(), false); + + expandQueue_toCollapseAfterAnimation.Clear(); + + return; + } + + var iid = expandQueue_toAnimate.First().instanceId; + var expand = expandQueue_toAnimate.First().expand; + + if (expandedIds.Contains(iid) != expand) + SetExpandedWithAnimation(iid, expand); + + expandQueue_toAnimate.RemoveAt(0); + + } + + static List expandQueue_toAnimate = new List(); + static List expandQueue_toCollapseAfterAnimation = new List(); + + struct ExpandQueueEntry { public int instanceId; public bool expand; } + + + static void UpdateExpandedIdsList() // delayCall loop + { + var eIds = hierarchyWindow?.GetFieldValue("m_SceneHierarchy")?.GetFieldValue("m_TreeViewState")?.GetPropertyValue("expandedIDs"); + expandedIds = eIds as List ?? (eIds as System.Collections.Generic.IEnumerable)?.ToList() ?? new List(); + + EditorApplication.delayCall -= UpdateExpandedIdsList; + EditorApplication.delayCall += UpdateExpandedIdsList; + + } + + static List expandedIds = new List(); + + static bool IsExpanded(GameObject go) => expandedIds.Contains(go.GetInstanceID()); + static bool IsVisible(GameObject go) => !go.transform.parent || (IsExpanded(go.transform.parent.gameObject) && IsVisible(go.transform.parent.gameObject)); + + static void SetExpandedWithAnimation(int instanceId, bool expanded) => treeViewController.InvokeMethod("ChangeFoldingForSingleItem", instanceId, expanded); + static void SetExpanded(int instanceId, bool expanded) => treeViewController.GetPropertyValue("data").InvokeMethod("SetExpanded", instanceId, expanded); // static void SetExpanded(int instanceId, bool expanded) => hierarchyWindow.InvokeMethod("SetExpanded", instanceId, expanded); + static int VisibleRowIndex(int instanceId) => treeViewController.GetPropertyValue("data").InvokeMethod("GetRow", instanceId); + + + + + + + public static string GetComponentName(Component component) + { + var s = new GUIContent(EditorGUIUtility.ObjectContent(component, component.GetType())).text; + s = s.Substring(s.LastIndexOf('(') + 1); + s = s.Substring(0, s.Length - 1); + + return s; + } + + public static Texture GetComponentIcon(Component component) + { + if (!component) return null; + + if (!componentIcons_byType.ContainsKey(component.GetType())) + componentIcons_byType[component.GetType()] = EditorGUIUtility.ObjectContent(component, component.GetType()).image; + + return componentIcons_byType[component.GetType()]; + + } + + static Dictionary componentIcons_byType = new Dictionary(); + + + + + + + + + + + + + public static GameObjectData GetGameObjectData(GameObject go, bool createDataIfDoesntExist) + { + if (!data) return null; + if (firstDataCacheLayer.TryGetValue(go, out var cachedResult)) return cachedResult; + + GameObjectData goData = null; + SceneData sceneData = null; + + void sceneObject() + { + if (StageUtility.GetCurrentStage() is PrefabStage) return; + + + SceneIdMap sceneIdMap = null; + + var currentSceneGuid = go.scene.path.ToGuid(); + var originalSceneGuid = cache.originalSceneGuids_byInstanceId.GetValueOrDefault(go.GetInstanceID()) ?? currentSceneGuid; + + + void getSceneDataFromComponents() + { + if (!dataComponents_byScene.ContainsKey(go.scene)) + dataComponents_byScene[go.scene] = Resources.FindObjectsOfTypeAll().FirstOrDefault(r => r.gameObject?.scene == go.scene); + + if (dataComponents_byScene[go.scene]) + sceneData = dataComponents_byScene[go.scene].sceneData; + + } + void getSceneDataFromScriptableObject() + { + if (sceneData != null) return; + + data.sceneDatas_byGuid.TryGetValue(originalSceneGuid, out sceneData); + + } + void createSceneData() + { + if (sceneData != null) return; + if (!createDataIfDoesntExist) return; + + sceneData = new SceneData(); + + data.sceneDatas_byGuid[originalSceneGuid] = sceneData; + + } + + void getSceneIdMap() + { + if (sceneData == null) return; + + cache.sceneIdMaps_bySceneGuid.TryGetValue(originalSceneGuid, out sceneIdMap); + + } + void createSceneIdMap() + { + if (sceneIdMap != null) return; + if (sceneData == null) return; + if (currentSceneGuid != originalSceneGuid) return; + + sceneIdMap = new SceneIdMap(); + + cache.sceneIdMaps_bySceneGuid[currentSceneGuid] = sceneIdMap; + + } + void updateSceneIdMapAndOriginalSceneGuids() + { + if (sceneIdMap == null) return; + if (currentSceneGuid != originalSceneGuid) return; + + + var curInstanceIdsHash = go.scene.GetRootGameObjects().FirstOrDefault()?.GetInstanceID() ?? 0; + var curGlobalIdsHash = sceneData.goDatas_byGlobalId.Keys.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + if (sceneIdMap.instanceIdsHash == curInstanceIdsHash && sceneIdMap.globalIdsHash == curGlobalIdsHash) return; + + + var globalIds = sceneData.goDatas_byGlobalId.Keys.ToList(); + var instanceIds = globalIds.Select(r => Application.isPlaying ? r.UnpackForPrefab() : r) + .GetObjectInstanceIds(); + void clearSceneGuids() + { + foreach (var instanceId in sceneIdMap.globalIds_byInstanceId.Keys) + cache.originalSceneGuids_byInstanceId.Remove(instanceId); + + } + void fillIdMap() + { + sceneIdMap.globalIds_byInstanceId = new SerializableDictionary(); + + for (int i = 0; i < instanceIds.Length; i++) + if (instanceIds[i] != 0) + sceneIdMap.globalIds_byInstanceId[instanceIds[i]] = globalIds[i]; + + } + void fillSceneGuids() + { + for (int i = 0; i < instanceIds.Length; i++) + cache.originalSceneGuids_byInstanceId[instanceIds[i]] = currentSceneGuid; + + } + + + clearSceneGuids(); + fillIdMap(); + fillSceneGuids(); + + sceneIdMap.instanceIdsHash = curInstanceIdsHash; + sceneIdMap.globalIdsHash = curGlobalIdsHash; + + } + + void getGoData() + { + if (sceneData == null) return; + if (sceneIdMap == null) return; + if (!sceneIdMap.globalIds_byInstanceId.TryGetValue(go.GetInstanceID(), out var globalId)) return; + + sceneData.goDatas_byGlobalId.TryGetValue(globalId, out goData); + + } + void moveGoDataToCurrentSceneGuid() // totest + { + if (goData == null) return; + if (currentSceneGuid == originalSceneGuid) return; + if (Application.isPlaying) return; + + var originalSceneData = sceneData; + var currentSceneData = dataComponents_byScene.GetValueOrDefault(go.scene)?.sceneData ?? data.sceneDatas_byGuid.GetValueOrDefault(currentSceneGuid); + + if (originalSceneData == null) return; + if (currentSceneData == null) return; + + var globalId = go.GetGlobalID(); + + originalSceneData.goDatas_byGlobalId.Remove(originalSceneData.goDatas_byGlobalId.First(r => r.Value == goData).Key); + currentSceneData.goDatas_byGlobalId[go.GetGlobalID()] = goData; + + } + void createGoData() + { + if (goData != null) return; + if (!createDataIfDoesntExist) return; + + goData = new GameObjectData(); + + sceneData.goDatas_byGlobalId[go.GetGlobalID()] = goData; + + } + + + getSceneDataFromComponents(); + getSceneDataFromScriptableObject(); + createSceneData(); + + getSceneIdMap(); + createSceneIdMap(); + updateSceneIdMapAndOriginalSceneGuids(); + + getGoData(); + moveGoDataToCurrentSceneGuid(); + createGoData(); + + } + void prefabObject() + { + if (!(StageUtility.GetCurrentStage() is PrefabStage prefabStage)) return; + + + var prefabGuid = prefabStage.assetPath.ToGuid(); + + GlobalID sourceGlobalId; + + void calcGlobalId() + { + + var rawGlobalId = go.GetGlobalID(); + + +#if UNITY_2023_2_OR_NEWER + + var so = new SerializedObject(go); + + so.SetPropertyValue("inspectorMode", UnityEditor.InspectorMode.Debug); + + var rawFileId = so.FindProperty("m_LocalIdentfierInFile").longValue; + + if (rawFileId == 0) // happens for prefab variants in unity 6 + rawFileId = (long)t_Unsupported.InvokeMethod("GetOrGenerateFileIDHint", go); + +#else + + var rawFileId = rawGlobalId.fileId; +#endif + + // fixes fileId for prefab variants + // also works for getting prefab's unpacked fileId + var fileId = ((long)rawFileId ^ (long)rawGlobalId.globalObjectId.targetPrefabId) & 0x7fffffffffffffff; + + + sourceGlobalId = new GlobalID($"GlobalObjectId_V1-1-{prefabGuid}-{fileId}-0"); + + } + + + void getSceneDataFromScriptableObject() + { + data.sceneDatas_byGuid.TryGetValue(prefabGuid, out sceneData); + } + void createSceneData() + { + if (sceneData != null) return; + if (!createDataIfDoesntExist) return; + + sceneData = new SceneData(); + + data.sceneDatas_byGuid[prefabGuid] = sceneData; + + } + + void getGoData() + { + if (sceneData == null) return; + + sceneData.goDatas_byGlobalId.TryGetValue(sourceGlobalId, out goData); + + } + void createGoData() + { + if (goData != null) return; + if (!createDataIfDoesntExist) return; + + goData = new GameObjectData(); + + sceneData.goDatas_byGlobalId[sourceGlobalId] = goData; + + } + + + calcGlobalId(); + + getSceneDataFromScriptableObject(); + createSceneData(); + + getGoData(); + createGoData(); + + } + void prefabInstance_editMode() + { + if (!PrefabUtility.IsPartOfPrefabInstance(go)) return; + if (goData != null) return; + + void tryGetForSourceGo(GameObject sourceGo) + { + var sourceGoGlobalId = sourceGo.GetGlobalID(); + var sourcePrefabGuid = sourceGoGlobalId.guid; + + cache.prefabInstanceGlobalIds_byInstanceIds[go.GetInstanceID()] = sourceGoGlobalId; + + + data.sceneDatas_byGuid.TryGetValue(sourcePrefabGuid, out sceneData); + + sceneData?.goDatas_byGlobalId.TryGetValue(sourceGoGlobalId, out goData); + + + if (goData == null) + try + { + if (PrefabUtility.GetCorrespondingObjectFromSource(sourceGo) is GameObject previousSourceGo) + tryGetForSourceGo(previousSourceGo); + + // wrapped in try-catch because GetCorrespondingObjectFromSource throws exceptions on broken prefabs + } + catch { } + + } + + tryGetForSourceGo(PrefabUtility.GetCorrespondingObjectFromSource(go)); + + } + void prefabInstance_playmode() + { + if (!Application.isPlaying) return; + if (goData != null) return; + + + if (!cache.prefabInstanceGlobalIds_byInstanceIds.TryGetValue(go.GetInstanceID(), out var globalId)) return; + + var prefabGuid = globalId.guid; + + + data.sceneDatas_byGuid.TryGetValue(prefabGuid, out sceneData); + + sceneData?.goDatas_byGlobalId.TryGetValue(globalId, out goData); + + } + + sceneObject(); + prefabObject(); + prefabInstance_editMode(); + prefabInstance_playmode(); + + if (goData != null) + goData.sceneData = sceneData; + + firstDataCacheLayer[go] = goData; + + return goData; + + } + + public static Dictionary firstDataCacheLayer = new Dictionary(); // cleared on data serialization callbacks, ie when data is added or removed + + public static Dictionary dataComponents_byScene = new Dictionary(); + + static VHierarchyCache cache => VHierarchyCache.instance; + + + + + + + + + static Texture2D GetIcon_forVTabs(GameObject gameObject) + { + var goData = GetGameObjectData(gameObject, false); + + if (goData == null) return null; + + var iconNameOrPath = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + + if (!iconNameOrPath.IsNullOrEmpty()) + return EditorIcons.GetIcon(iconNameOrPath); + + return null; + + } + + static string GetIconName_forVFavorites(GameObject gameObject) + { + var goData = GetGameObjectData(gameObject, false); + + if (goData == null) return ""; + + var iconNameOrPath = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + + return iconNameOrPath; + + } + static string GetIconName_forVInspector(GameObject gameObject) + { + return GetIconName_forVFavorites(gameObject); + } + + + + + + + + static void RepaintOnAlt() // Update + { + var lastEvent = typeof(Event).GetFieldValue("s_Current"); + + if (lastEvent.alt != wasAlt) + if (EditorWindow.mouseOverWindow?.GetType() == t_SceneHierarchyWindow) + EditorApplication.RepaintHierarchyWindow(); + + wasAlt = lastEvent.alt; + + } + + static bool wasAlt; + + + + + static void SetPreviousTransformTool() + { + if (!transformToolNeedsReset) return; + + Tools.current = previousTransformTool; + + transformToolNeedsReset = false; + + // E shortcut changes transform tool in 2022 + // here we undo this + + } + + static bool transformToolNeedsReset; + static Tool previousTransformTool; + + + + + + + + static void DuplicateSceneData(string originalSceneGuid, string duplicatedSceneGuid) + { + var originalSceneData = data.sceneDatas_byGuid[originalSceneGuid]; + var duplicatedSceneData = data.sceneDatas_byGuid[duplicatedSceneGuid] = new SceneData(); + + foreach (var kvp in originalSceneData.goDatas_byGlobalId) + { + var duplicatedGlobalId = new GlobalID(kvp.Key.ToString().Replace(originalSceneGuid, duplicatedSceneGuid)); + var duplicatedGoData = new GameObjectData() { colorIndex = kvp.Value.colorIndex, iconNameOrGuid = kvp.Value.iconNameOrGuid }; + + duplicatedSceneData.goDatas_byGlobalId[duplicatedGlobalId] = duplicatedGoData; + + } + + } + + static void OnSceneImported(string importedScenePath) + { + if (curEvent.commandName != "Duplicate" && curEvent.commandName != "Paste") return; + + + var copiedAssets_paths = new List(); + + var assetClipboard = typeof(Editor).Assembly.GetType("UnityEditor.AssetClipboardUtility").GetMemberValue("assetClipboard").InvokeMethod("GetEnumerator"); + + while (assetClipboard.MoveNext()) + copiedAssets_paths.Add(assetClipboard.Current.GetMemberValue("guid").ToString().ToPath()); + + + + var originalScenePath = copiedAssets_paths.FirstOrDefault(r => File.Exists(r) && new FileInfo(r).Length + == new FileInfo(importedScenePath).Length); + var originalSceneGuid = originalScenePath.ToGuid(); + var duplicatedSceneGuid = importedScenePath.ToGuid(); + + if (!data.sceneDatas_byGuid.ContainsKey(originalSceneGuid)) return; + if (data.sceneDatas_byGuid.ContainsKey(duplicatedSceneGuid)) return; + + DuplicateSceneData(originalSceneGuid, duplicatedSceneGuid); + + } + + class SceneImportDetector : AssetPostprocessor + { + // scene data duplication won't work on earlier versions anyway +#if UNITY_2021_2_OR_NEWER + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) + { + if (!data) return; + + foreach (var r in importedAssets) + if (r.EndsWith(".unity")) + OnSceneImported(r); + + } +#endif + } + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VHierarchyMenu.pluginDisabled) return; + + void subscribe() + { + EditorApplication.hierarchyWindowItemOnGUI -= RowGUI; + EditorApplication.hierarchyWindowItemOnGUI = RowGUI + EditorApplication.hierarchyWindowItemOnGUI; + + EditorApplication.update -= RepaintOnAlt; + EditorApplication.update += RepaintOnAlt; + + EditorApplication.update -= SetPreviousTransformTool; + EditorApplication.update += SetPreviousTransformTool; + + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", CheckShortcuts + (globalEventHandler - CheckShortcuts)); + + var projectWasLoaded = typeof(EditorApplication).GetFieldValue("projectWasLoaded"); + typeof(EditorApplication).SetFieldValue("projectWasLoaded", (projectWasLoaded - ClearCacheOnProjectLoaded) + ClearCacheOnProjectLoaded); + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString("vHierarchy-lastKnownDataPath-" + GetProjectId())); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VHierarchyData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + EditorPrefs.SetString("vHierarchy-lastKnownDataPath-" + GetProjectId(), data.GetPath()); + + } + void loadPalette() + { + palette = AssetDatabase.LoadAssetAtPath(EditorPrefs.GetString("vHierarchy-lastKnownPalettePath-" + GetProjectId())); + + + if (palette) return; + + palette = AssetDatabase.FindAssets("t:VHierarchyPalette").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!palette) return; + + EditorPrefs.SetString("vHierarchy-lastKnownPalettePath-" + GetProjectId(), palette.GetPath()); + + } + void loadDataAndPaletteDelayed() + { + if (!data) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + if (!palette) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadPalette; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + void migrateDataFromV1() + { + if (!data) return; + if (EditorPrefs.GetBool("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId(), false)) return; + + EditorPrefs.SetBool("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId(), true); + + var lines = System.IO.File.ReadAllLines(data.GetPath()); + + if (lines.Length < 15 || !lines[14].Contains("sceneDatasByGuid")) return; + + var sceneGuids = new List(); + var globalIdLists = new List>(); + var goDatasByInstanceIdCounts = new List(); + var sceneDatas = new List(); + + + void parseSceneGuids() + { + for (int i = 16; i < lines.Length; i++) + { + if (lines[i].Contains("values:")) break; + + var startIndex = lines[i].IndexOf("- ") + 2; + + if (startIndex < lines[i].Length) + sceneGuids.Add(lines[i].Substring(startIndex)); + else + sceneGuids.Add(""); + + } + + } + void parseGlobalIdLists_andCountGoDatasByInstanceId() + { + var parsingGlobalIdList = false; + var parsingGlobalIdListAtIndex = -1; + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i]; + + void startParsing() + { + if (!line.Contains("goDatasByGlobalId")) return; + + parsingGlobalIdList = true; + parsingGlobalIdListAtIndex++; + + globalIdLists.Add(new List()); + + } + void parse() + { + if (!parsingGlobalIdList) return; + if (!line.Contains("- GlobalObjectId")) return; + + var startIndex = line.IndexOf("- ") + 2; + + if (startIndex < line.Length) + globalIdLists[parsingGlobalIdListAtIndex].Add(line.Substring(startIndex)); + else + globalIdLists[parsingGlobalIdListAtIndex].Add(""); + + } + void stopParsing_andCountDatasByInstanceId() + { + if (!line.Contains("goDatasByInstanceId")) return; + + + parsingGlobalIdList = false; + + + var goDatasByInstanceId_keysLine = lines[i + 1]; + var goDatasByInstanceId_count = (goDatasByInstanceId_keysLine.Length - 14) / 8; + + goDatasByInstanceIdCounts.Add(goDatasByInstanceId_count); + + + } + + startParsing(); + parse(); + stopParsing_andCountDatasByInstanceId(); + + } + + } + void parseSceneDatas() + { + var firstLineIndexOfFirstSceneData = 17 + sceneGuids.Count; + + + + void parseSceneData(int sceneDataIndex) + { + var sceneData = new SceneData(); + + var globalIds = globalIdLists[sceneDataIndex]; + var firstLineIndex = getFirstLineIndex(sceneDataIndex); + + + + void parseGoData(int iGoData) + { + var goData = new GameObjectData(); + + + var colorLine = lines[getColorLineIndex(iGoData)]; + + if (colorLine.Length > 18) + goData.colorIndex = int.Parse(colorLine.Substring(18)); + + + var iconLine = lines[getIconLineIndex(iGoData)]; + + if (iconLine.Length > 16) + goData.iconNameOrGuid = iconLine.Substring(16); + + + + var globalIdString = globalIdLists[sceneDataIndex][iGoData]; + + var globalId = new GlobalID(globalIdString); + + + + sceneData.goDatas_byGlobalId[globalId] = goData; + // sceneData.goDatas_byGlobalId.Add(globalId, goData); + + } + + int getColorLineIndex(int goDataIndex) + { + var index = firstLineIndex; // - goDatasByGlobalId: + + index += 1; // keys: + index += globalIds.Count; + + index += 1; // values: + index += 1; // zeroth godata + index += goDataIndex * 2; + + return index; + + } + int getIconLineIndex(int goDataIndex) => getColorLineIndex(goDataIndex) + 1; + + + + for (int i = 0; i < globalIds.Count; i++) + parseGoData(i); + + sceneDatas.Add(sceneData); + + } + + int getSceneDataLength(int sceneDataIndex) + { + int length = 0; + + length += 1; // - goDatasByGlobalId: + + length += 1; // - keys: + length += globalIdLists[sceneDataIndex].Count; + + length += 1; // - values: + length += globalIdLists[sceneDataIndex].Count * 2; + + + + length += 1; // - goDatasByInstanceId: + + length += 1; // - keys: 123123123 + + length += 1; // - values: + length += goDatasByInstanceIdCounts[sceneDataIndex] * 2; + + + return length; + + } + int getFirstLineIndex(int sceneDataIndex) + { + var index = firstLineIndexOfFirstSceneData; + + for (int i = 0; i < sceneDataIndex; i++) + index += getSceneDataLength(i); + + return index; + + } + + + + for (int i = 0; i < sceneGuids.Count; i++) + parseSceneData(i); + + } + + void remapColorIndexes() + { + foreach (var sceneData in sceneDatas) + foreach (var goData in sceneData.goDatas_byGlobalId.Values) + if (goData.colorIndex == 7) + goData.colorIndex = 1; + else if (goData.colorIndex == 8) + goData.colorIndex = 2; + else if (goData.colorIndex >= 2) + goData.colorIndex += 2; + + } + void setSceneDatasToData() + { + for (int i = 0; i < sceneDatas.Count; i++) + data.sceneDatas_byGuid[sceneGuids[i]] = sceneDatas[i]; + + data.Dirty(); + data.Save(); + + } + + + try + { + parseSceneGuids(); + parseGlobalIdLists_andCountGoDatasByInstanceId(); + parseSceneDatas(); + + remapColorIndexes(); + setSceneDatasToData(); + + } + catch { } + + } + + subscribe(); + loadData(); + loadPalette(); + loadDataAndPaletteDelayed(); + migrateDataFromV1(); + + UpdateExpandedIdsList(); + + } + + public static VHierarchyData data; + public static VHierarchyPalette palette; + + + + [UnityEditor.Callbacks.PostProcessBuild] + public static void ClearCacheAfterBuild(BuildTarget _, string __) => VHierarchyCache.Clear(); + + static void ClearCacheOnProjectLoaded() => VHierarchyCache.Clear(); + + + + + + + + + static EditorWindow hierarchyWindow + { + get + { + if (_hierarchyWindow != null && _hierarchyWindow.GetType() != t_SceneHierarchyWindow) // happens on 2022.3.22f1 with enter playmode options on + _hierarchyWindow = null; + + if (_hierarchyWindow == null) + _hierarchyWindow = Resources.FindObjectsOfTypeAll(t_SceneHierarchyWindow).FirstOrDefault() as EditorWindow; + + return _hierarchyWindow; + + } + } + static EditorWindow _hierarchyWindow; + + static object treeViewController => hierarchyWindow?.GetFieldValue("m_SceneHierarchy").GetFieldValue("m_TreeView"); // recreated on prefab mode enter/exit + + + static Type t_SceneHierarchyWindow = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); + static Type t_Unsupported = typeof(Editor).Assembly.GetType("UnityEditor.Unsupported"); + + + + + + public const string version = "2.0.18"; + + } +} +#endif + + diff --git a/Assets/Third Parties/vHierarchy/VHierarchy.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchy.cs.meta new file mode 100644 index 00000000..1831a9a2 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67e949be20d3641adbc9494ed5bd764e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyCache.cs b/Assets/Third Parties/vHierarchy/VHierarchyCache.cs new file mode 100644 index 00000000..448cc207 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyCache.cs @@ -0,0 +1,62 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + [FilePath("Library/vHierarchy Cache.asset", FilePathAttribute.Location.ProjectFolder)] + public class VHierarchyCache : ScriptableSingleton + { + // used for finding SceneData and SceneIdMap for objects that were moved out of their original scene + public SerializableDictionary originalSceneGuids_byInstanceId = new SerializableDictionary(); + + // used as cache for converting GlobalID to InstanceID and as a way to find GameObjectData for prefabs in playmode (when prefabs produce invalid GlobalIDs) + public SerializableDictionary sceneIdMaps_bySceneGuid = new SerializableDictionary(); + + // used for fetching icons set inside prefab instances in playmode (when prefabs produce invalid GlobalIDs) + public SerializableDictionary prefabInstanceGlobalIds_byInstanceIds = new SerializableDictionary(); + + + [System.Serializable] + public class SceneIdMap + { + public SerializableDictionary globalIds_byInstanceId = new SerializableDictionary(); + + public int instanceIdsHash; + public int globalIdsHash; + + } + + + + + + public static void Clear() + { + instance.originalSceneGuids_byInstanceId.Clear(); + instance.sceneIdMaps_bySceneGuid.Clear(); + + instance.Save(true); + + } + + // public static void Save() => instance.Save(true); // cache is never saved to disk, it just needs to survive domain reloads + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyCache.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyCache.cs.meta new file mode 100644 index 00000000..b6d95120 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fd3b966dd497472d86df0d7c9271088 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs b/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs.meta new file mode 100644 index 00000000..18dd4d01 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyComponentEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d51d8117d96b64eaa9a83667bf4297d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs b/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs new file mode 100644 index 00000000..3586f963 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs @@ -0,0 +1,575 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + public class VHierarchyComponentWindow : EditorWindow + { + void OnGUI() + { + if (!component) { Close(); return; } // todo script components break on playmode + + + void background() + { + position.SetPos(0, 0).Draw(GUIColors.windowBackground); + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + void header() + { + var headerRect = ExpandWidthLabelRect(18).Resize(-1).AddWidthFromMid(6); + var pinButtonRect = headerRect.SetWidthFromRight(17).SetHeightFromMid(17).Move(-21, .5f); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, .5f); + + var backgroundColor = isDarkTheme ? Greyscale(.25f) : GUIColors.windowBackground; + + void startDragging() + { + if (isResizingVertically) return; + if (isResizingHorizontally) return; + if (isDragged) return; + if (!curEvent.isMouseDrag) return; + if (!headerRect.IsHovered()) return; + + + isDragged = true; + + dragStartMousePos = EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition); + dragStartWindowPos = position.position; + + + isPinned = true; + + if (floatingInstance == this) + floatingInstance = null; + + EditorApplication.RepaintHierarchyWindow(); + + + } + void updateDragging() + { + if (!isDragged) return; + + + var draggedPosition = dragStartWindowPos + EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition) - dragStartMousePos; + + if (!curEvent.isRepaint) + position = position.SetPos(draggedPosition); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!isDragged) return; + if (!curEvent.isMouseUp) return; + + + isDragged = false; + + EditorGUIUtility.hotControl = 0; + + } + + void background() + { + headerRect.Draw(backgroundColor); + + headerRect.SetHeightFromBottom(1).Draw(isDarkTheme ? Greyscale(.2f) : Greyscale(.7f)); + + } + void icon() + { + var iconRect = headerRect.SetWidth(20).MoveX(14).MoveY(-1); + + GUI.Label(iconRect, VHierarchy.GetComponentIcon(component)); + + } + void toggle() + { + var toggleRect = headerRect.MoveX(36).SetSize(20, 20); + + + var pi_enabled = component.GetType().GetProperty("enabled") ?? + component.GetType().BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.BaseType?.GetProperty("enabled"); + + + if (pi_enabled == null) return; + + var enabled = (bool)pi_enabled.GetValue(component); + + + if (GUI.Toggle(toggleRect, enabled, "") == enabled) return; + + component.RecordUndo(); + pi_enabled.SetValue(component, !enabled); + + } + void name() + { + var nameRect = headerRect.MoveX(54).MoveY(-1); + + var s = VHierarchy.GetComponentName(component); + + if (isPinned) + s += " of " + component.gameObject.name; + + + SetLabelBold(); + + GUI.Label(nameRect, s); + + ResetLabelStyle(); + + } + void nameCurtain() + { + var flatColorRect = headerRect.SetX(pinButtonRect.x + 3).SetXMax(headerRect.xMax); + var gradientRect = headerRect.SetXMax(flatColorRect.x).SetWidthFromRight(30); + + flatColorRect.Draw(backgroundColor); + gradientRect.DrawCurtainLeft(backgroundColor); + + } + void pinButton() + { + if (!isPinned && closeButtonRect.IsHovered()) return; + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.8f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + var activeColor = Color.white; + + + + SetGUIColor(isPinned ? activeColor : pinButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(pinButtonRect, EditorGUIUtility.IconContent("pinned")); + + ResetGUIColor(); + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(pinButtonRect, ""); + + ResetGUIColor(); + + + if (!clicked) return; + + isPinned = !isPinned; + + if (isPinned && floatingInstance == this) + floatingInstance = null; + + if (!isPinned && !floatingInstance) + floatingInstance = this; + + EditorApplication.RepaintHierarchyWindow(); + + + } + void closeButton() + { + + SetGUIColor(Color.clear); + + if (GUI.Button(closeButtonRect, "")) + Close(); + + ResetGUIColor(); + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.35f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + + + SetGUIColor(closeButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(closeButtonRect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetGUIColor(); + + + if (isPinned) return; + + var escRect = closeButtonRect.Move(-22, -1).SetWidth(70); + + SetGUIEnabled(false); + + if (closeButtonRect.IsHovered()) + GUI.Label(escRect, "Esc"); + + ResetGUIEnabled(); + + } + + startDragging(); + updateDragging(); + stopDragging(); + + background(); + icon(); + toggle(); + name(); + nameCurtain(); + pinButton(); + closeButton(); + + } + void body() + { + EditorGUIUtility.labelWidth = (this.position.width * .4f).Max(120); + + + scrollPosition = EditorGUILayout.BeginScrollView(Vector2.up * scrollPosition).y; + BeginIndent(17); + + + editor?.OnInspectorGUI(); + + updateHeight(); + + + EndIndent(1); + EditorGUILayout.EndScrollView(); + + + EditorGUIUtility.labelWidth = 0; + + } + + void updateHeight() + { + + ExpandWidthLabelRect(height: -5); + + if (!curEvent.isRepaint) return; + if (isResizingVertically) return; + + + targetHeight = lastRect.y + 30; + + position = position.SetHeight(targetHeight.Min(maxHeight)); + + + prevHeight = position.height; + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default && !isPinned) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + if (isPinned) return; + + var speed = 9; + + SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + if (isPinned) return; + + position = position.SetPos(currentPosition); + + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + Close(); + } + + void horizontalResize() + { + var showingScrollbar = targetHeight > maxHeight; + + var resizeArea = this.position.SetPos(0, 0).SetWidthFromRight(showingScrollbar ? 3 : 5).AddHeightFromBottom(-20); + + void startResize() + { + if (isDragged) return; + if (isResizingHorizontally) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingHorizontally = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingHorizontally) return; + + + var resizedWidth = resizeStartWindowSize.x + curEvent.mousePosition_screenSpace.x - resizeStartMousePos.x; + + var width = resizedWidth.Max(300); + + if (!curEvent.isRepaint) + position = position.SetWidth(width); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + // GUI.focused + + } + void stopResize() + { + if (!isResizingHorizontally) return; + if (!curEvent.isMouseUp) return; + + isResizingHorizontally = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeHorizontal); + + startResize(); + updateResize(); + stopResize(); + + } + void verticalResize() + { + var resizeArea = this.position.SetPos(0, 0).SetHeightFromBottom(5); + + void startResize() + { + if (isDragged) return; + if (isResizingVertically) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingVertically = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingVertically) return; + + + var resizedHeight = resizeStartWindowSize.y + curEvent.mousePosition_screenSpace.y - resizeStartMousePos.y; + + var height = resizedHeight.Min(targetHeight).Max(50); + + if (!curEvent.isRepaint) + position = position.SetHeight(height); + + maxHeight = height; + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + // GUI.focused + + } + void stopResize() + { + if (!isResizingVertically) return; + if (!curEvent.isMouseUp) return; + + isResizingVertically = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeVertical); + + startResize(); + updateResize(); + stopResize(); + + } + + + background(); + outline(); + + horizontalResize(); + verticalResize(); + + + header(); + + Space(3); + body(); + + Space(7); + + + updatePosition(); + closeOnEscape(); + + if (!isPinned) + Repaint(); + + } + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + bool isDragged; + Vector2 dragStartMousePos; + Vector2 dragStartWindowPos; + + public bool isResizingHorizontally; + public bool isResizingVertically; + public Vector2 resizeStartMousePos; + public Vector2 resizeStartWindowSize; + + public float scrollPosition; + + public float targetHeight; + public float maxHeight; + public float prevHeight; + + + + + + void OnLostFocus() + { + if (isPinned) return; + + if (curEvent.holdingAlt && EditorWindow.focusedWindow.GetType().Name == "SceneHierarchyWindow") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + public bool isPinned; + + + + + + public void Init(Component component) + { + if (editor) + editor.DestroyImmediate(); + + this.component = component; + this.editor = Editor.CreateEditor(component); + + } + + void OnDestroy() + { + editor?.DestroyImmediate(); + + editor = null; + component = null; + + EditorPrefs.SetFloat("vHierarchy-componentWindowWidth", position.width); + + } + + public Component component; + public Editor editor; + + + + + + public static void CreateFloatingInstance(Vector2 position) + { + floatingInstance = ScriptableObject.CreateInstance(); + + floatingInstance.ShowPopup(); + + + floatingInstance.maxHeight = EditorGUIUtility.GetMainWindowPosition().height * .7f; + + + var savedWidth = EditorPrefs.GetFloat("vHierarchy-componentWindowWidth", minWidth); + + var width = savedWidth.Max(minWidth); + + floatingInstance.position = Rect.zero.SetPos(position).SetWidth(width).SetHeight(200); + floatingInstance.prevHeight = floatingInstance.position.height; + + floatingInstance.targetPosition = position; + + } + + public static VHierarchyComponentWindow floatingInstance; + + public static float minWidth => 300; + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs.meta new file mode 100644 index 00000000..741ab1d6 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyComponentWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b48d49a631ab443990f28938cbdedb8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyData.cs b/Assets/Third Parties/vHierarchy/VHierarchyData.cs new file mode 100644 index 00000000..6883e375 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyData.cs @@ -0,0 +1,76 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + public class VHierarchyData : ScriptableObject, ISerializationCallbackReceiver + { + + public SerializableDictionary sceneDatas_byGuid = new SerializableDictionary(); + + + [System.Serializable] + public class SceneData + { + public SerializableDictionary goDatas_byGlobalId = new SerializableDictionary(); + } + + + [System.Serializable] + public class GameObjectData + { + public int colorIndex; + public string iconNameOrGuid = ""; // name for buildin icons, guid for custom ones + + [System.NonSerialized] // set in GetGameObjectData + public SceneData sceneData; + + } + + public void OnBeforeSerialize() => VHierarchy.firstDataCacheLayer.Clear(); + public void OnAfterDeserialize() => VHierarchy.firstDataCacheLayer.Clear(); + + + + + + [CustomEditor(typeof(VHierarchyData))] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = new GUIStyle(EditorStyles.label) { wordWrap = true }; + + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("This file contains data about which icons and colors are assigned to objects", style); + + Space(6); + GUILayout.Label("If there are multiple people working on the project, you might want to store this data in scenes to avoid merge conflicts. To do that, create a script that inherits from VHierarchy.VHierarchyDataComponent and add it to any object in the scene", style); + + EndIndent(10); + ResetGUIEnabled(); + + } + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyData.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyData.cs.meta new file mode 100644 index 00000000..cd5eee5b --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a9752b0c8e144801967e6897679604b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs b/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs new file mode 100644 index 00000000..669eb836 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs @@ -0,0 +1,95 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.Experimental.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + [ExecuteInEditMode] + public abstract class VHierarchyDataComponent : MonoBehaviour, ISerializationCallbackReceiver + { + public void Awake() + { + void register() + { + VHierarchy.dataComponents_byScene[gameObject.scene] = this; + } + void handleSceneDuplication() + { + if (sceneData == null) return; + if (!sceneData.goDatas_byGlobalId.Any()) return; + + + var curSceneGuid = gameObject.scene.path.ToGuid(); + var dataSceneGuid = sceneData.goDatas_byGlobalId.Keys.First().guid; + + if (curSceneGuid == dataSceneGuid) return; + + + var newDic = new SerializableDictionary(); + + foreach (var kvp in sceneData.goDatas_byGlobalId) + newDic[new GlobalID(kvp.Key.ToString().Replace(dataSceneGuid, curSceneGuid))] = kvp.Value; + + + sceneData.goDatas_byGlobalId = newDic; + + + EditorSceneManager.MarkSceneDirty(gameObject.scene); + EditorSceneManager.SaveScene(gameObject.scene); + + } + + register(); + handleSceneDuplication(); + + } + + public SceneData sceneData; + + + public void OnBeforeSerialize() => VHierarchy.firstDataCacheLayer.Clear(); + public void OnAfterDeserialize() => VHierarchy.firstDataCacheLayer.Clear(); + + + + [CustomEditor(typeof(VHierarchyDataComponent), true)] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = EditorStyles.label; + style.wordWrap = true; + + + SetGUIEnabled(false); + BeginIndent(0); + + Space(4); + EditorGUILayout.LabelField("This component stores vHierarchy icons and colors that are assigned to objects in this scene", style); + + Space(2); + + EndIndent(10); + ResetGUIEnabled(); + + } + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs.meta new file mode 100644 index 00000000..be05b153 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyDataComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e20c7ea1a24b4a899ba82e98ad2b375 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs b/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs.meta new file mode 100644 index 00000000..90282a98 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyIconEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8710ada63f66c4909ab73d206f31e954 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs b/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs new file mode 100644 index 00000000..d520f07f --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs @@ -0,0 +1,2932 @@ +#if UNITY_EDITOR +using Type = System.Type; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Reflection; +using UnityEngine.Experimental.Rendering; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + +namespace VHierarchy.Libs +{ + public static class VUtils + { + #region Text + + + public static string FormatDistance(float meters) + { + int m = (int)meters; + + if (m < 1000) return m + " m"; + else return (m / 1000) + "." + (m / 100) % 10 + " km"; + } + public static string FormatLong(long l) => System.String.Format("{0:n0}", l); + public static string FormatInt(int l) => FormatLong((long)l); + public static string FormatSize(long bytes, bool sizeUnknownIfNotMoreThanZero = false) + { + if (sizeUnknownIfNotMoreThanZero && bytes == 0) return "Size unknown"; + + var ss = new[] { "B", "KB", "MB", "GB", "TB" }; + var bprev = bytes; + int i = 0; + while (bytes >= 1024 && i++ < ss.Length - 1) bytes = (bprev = bytes) / 1024; + + if (bytes < 0) return "? B"; + if (i < 3) return string.Format("{0:0.#} ", bytes) + ss[i]; + return string.Format("{0:0.##} ", bytes) + ss[i]; + } + public static string FormatTime(long ms, bool includeMs = false) + { + System.TimeSpan t = System.TimeSpan.FromMilliseconds(ms); + var s = ""; + if (t.Hours != 0) s += " " + t.Hours + " hour" + CountSuffix(t.Hours); + if (t.Minutes != 0) s += " " + t.Minutes + " minute" + CountSuffix(t.Minutes); + if (t.Seconds != 0) s += " " + t.Seconds + " second" + CountSuffix(t.Seconds); + if (t.Milliseconds != 0 && includeMs) s += " " + t.Milliseconds + " millisecond" + CountSuffix(t.Milliseconds); + + if (s == "") + if (includeMs) s = "0 milliseconds"; + else s = "0 seconds"; + + return s.Trim(); + } + static string CountSuffix(long c) => c % 10 != 1 ? "s" : ""; + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + + + + + #endregion + + #region IEnumerables + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + public static int LastIndex(this List l) => l.Count - 1; // toremove + + // public static T GetAtWrapped(this List list, int i) // toremove + // { + // while (i < 0) i += list.Count; + // while (i >= list.Count) i -= list.Count; + + // return list[i]; + // } + + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + + public static bool None(this IEnumerable ie, System.Func f) => !ie.Any(f); + + public static bool None(this IEnumerable ie) => !ie.Any(); + + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + + + + public static TValue GetValueOrDefault(this Dictionary dic, TKey key) => dic.ContainsKey(key) ? dic[key] : default; + + + + + + #endregion + + #region Reflection + + public static object GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + public static object GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + public static object GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in '{type.Name}' type and its parent types"); + + return null; + + } + + public static void SetFieldValue(this object o, string fieldName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in '{type.Name}' type and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in '{type.Name}' type and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in '{type.Name}' type and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in '{type.Name}' type, its parent types and interfaces"); + + } + + + + static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + static Dictionary> fieldInfoCache = new Dictionary>(); + + static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + static Dictionary> propertyInfoCache = new Dictionary>(); + + static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + static Dictionary> methodInfoCache = new Dictionary>(); + + + + public static T GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) => (T)o.GetFieldValue(fieldName, exceptionIfNotFound); + public static T GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) => (T)o.GetPropertyValue(propertyName, exceptionIfNotFound); + public static T GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) => (T)o.GetMemberValue(memberName, exceptionIfNotFound); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + #endregion + + #region Math + + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + public static bool CloseTo(this float f1, float f2, float distance) => f1.DistTo(f2) <= distance; + public static float DistTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Dist(float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Avg(float f1, float f2) => (f1 + f2) / 2; + public static float Abs(this float f) => Mathf.Abs(f); + public static int Abs(this int f) => Mathf.Abs(f); + public static float Sign(this float f) => Mathf.Sign(f); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new Vector2(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new Vector3(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + + + public static Color Greyscale(float brightness, float alpha = 1) => new Color(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Vectors + + + public static Vector2 AddX(this Vector2 v, float f) => new Vector2(v.x + f, v.y + 0); + public static Vector2 AddY(this Vector2 v, float f) => new Vector2(v.x + 0, v.y + f); + + public static Vector3 AddX(this Vector3 v, float f) => new Vector3(v.x + f, v.y + 0, v.z + 0); + public static Vector3 AddY(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + f, v.z + 0); + public static Vector3 AddZ(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + 0, v.z + f); + + public static Vector2 xx(this Vector3 v) { return new Vector2(v.x, v.x); } + public static Vector2 xy(this Vector3 v) { return new Vector2(v.x, v.y); } + public static Vector2 xz(this Vector3 v) { return new Vector2(v.x, v.z); } + public static Vector2 yx(this Vector3 v) { return new Vector2(v.y, v.x); } + public static Vector2 yy(this Vector3 v) { return new Vector2(v.y, v.y); } + public static Vector2 yz(this Vector3 v) { return new Vector2(v.y, v.z); } + public static Vector2 zx(this Vector3 v) { return new Vector2(v.z, v.x); } + public static Vector2 zy(this Vector3 v) { return new Vector2(v.z, v.y); } + public static Vector2 zz(this Vector3 v) { return new Vector2(v.z, v.z); } + + + + + + #endregion + + #region Textures + + + public static Texture2D CreateTexture2D(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false) + { + return new Texture2D(width, height, graphicsFormat, useMips ? TextureCreationFlags.MipChain : TextureCreationFlags.None); + } + + public static RenderTexture CreateRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = new RenderTexture(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + public static RenderTexture GetTemporaryRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = RenderTexture.GetTemporary(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor) => new RenderTexture(descriptor); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.CreateRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor) => RenderTexture.GetTemporary(descriptor); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.GetTemporaryRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + + public static void ReleaseTemporary(this RenderTexture rt) { if (rt) RenderTexture.ReleaseTemporary(rt); } + + + + public static Texture2D ToTexture2D(this RenderTexture rt) + { + var texture2D = CreateTexture2D(rt.width, rt.height, rt.graphicsFormat, rt.useMipMap); + + texture2D.ReadPixelsFrom(rt); + texture2D.Apply(); + + return texture2D; + + } + public static RenderTexture ToRenderTexture(this Texture2D texture2d) + { + var rt = CreateRT(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, rt); + + return rt; + + } + + + public static void ReadPixelsFrom(this Texture2D texture2D, RenderTexture renderTexture) + { + var prevActive = RenderTexture.active; + + RenderTexture.active = renderTexture; + + texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + + RenderTexture.active = prevActive; + + } + // public static void CopyTo(this RenderTexture source, Texture2D target) // todo to readpixels overload + // { + // var prevActive = RenderTexture.active; + + // RenderTexture.active = source; + + // target.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); + // target.Apply(); + + // RenderTexture.active = prevActive; + + + // // somewhere in unity source code reading is done like this, but it throws out of bounds read exception on win: + + // // if (!SystemInfo.graphicsUVStartsAtTop || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) + // // texture2d.ReadPixels(new Rect(0, 0, texture2d.width, texture2d.height), 0, 0); + // // else + // // texture2d.ReadPixels(new Rect(0, texture2d.height, texture2d.width, texture2d.height), 0, 0); + + // // this was used in the legacy rt.CopyToTexture2D extension method + + // } + + public static Texture2D CreateCopy(this Texture2D texture2d) + { + var copy = CreateTexture2D(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, copy); + + return copy; + + } + public static Texture2D CreateResizedCopy(this Texture2D texture2d, int w, int h) + { + var rt = GetTemporaryRT(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), false); + + Graphics.Blit(texture2d, rt); + + + var resizedCopy = CreateTexture2D(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), texture2d.mipmapCount > 1); + + resizedCopy.ReadPixelsFrom(rt); + resizedCopy.Apply(); + + if (RenderTexture.active == rt) + RenderTexture.active = null; + + rt.ReleaseTemporary(); + + + return resizedCopy; + + } + + + public static void FillWithColor(this Texture2D texture2d, Color color) + { + var pixels = new Color32[texture2d.width * texture2d.height]; + + var color32 = (Color32)color; + + for (int i = 0; i < pixels.Length; i++) + pixels[i] = color32; + + texture2d.SetPixels32(pixels); + texture2d.Apply(); + + } + public static RenderTexture FillWithColor(this RenderTexture rt, Color color) // todo builtin shader or GL.clear + { + var mat = new Material(Shader.Find("Hidden/VBlitColor")); + + mat.SetColor("_color", color); + + Graphics.Blit(null, rt, mat); + + mat.Destroy(); + + return rt; + + } + + + + public static GraphicsFormat GetCompatibleForRendering(this GraphicsFormat graphicsFormat) + { +#if UNITY_2023_2_OR_NEWER + return SystemInfo.GetCompatibleFormat(graphicsFormat, GraphicsFormatUsage.Render); +#else + return SystemInfo.GetCompatibleFormat(graphicsFormat, FormatUsage.Render); +#endif + + } + + + +#if UNITY_EDITOR + + public static void SavePNG(this Texture2D texture2d, string path) => File.WriteAllBytes(path, texture2d.EncodeToPNG()); + + public static void SetImportSettings(this Texture2D texture2d, int? maxSize = null, bool? useMips = null, bool? sRGB = null, bool? isReadable = null, bool? useCompression = null) + { + var importer = texture2d.GetImporter(); + + if (useCompression != null) + importer.textureCompression = useCompression.GetValueOrDefault() ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; + + if (sRGB != null) + importer.sRGBTexture = sRGB.GetValueOrDefault(); + + if (maxSize != null) + importer.maxTextureSize = maxSize.GetValueOrDefault(); + + if (useMips != null) + importer.mipmapEnabled = useMips.GetValueOrDefault(); + + + // if (texture2d.format == TextureFormat.R16 || texture2d.format == TextureFormat.RG32) + if (texture2d.format == TextureFormat.R16) + { + var platformSettings = importer.GetDefaultPlatformTextureSettings(); + + platformSettings.format = TextureImporterFormat.R16; + + if (maxSize != null) + platformSettings.maxTextureSize = maxSize.GetValueOrDefault(); + + importer.SetPlatformTextureSettings(platformSettings); + + } + + } + + public static TextureImporter GetImporter(this Texture2D t) => (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + +#endif + + + + + + + + #endregion + + #region Compute + + + [System.Serializable] + public class GaussianKernel + { + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + } + + + + + + #endregion + + #region GameObjects + + + public static bool IsPrefab(this GameObject go) => go.scene.name == null || go.scene.name == go.name; + + public static Bounds GetBounds(this GameObject go, bool local = false) + { + Bounds bounds = default; + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? r.gameObject.GetComponent().sharedMesh.bounds : r.bounds; + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(b); + } + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? new Bounds(r.terrainData.size / 2, r.terrainData.size) : new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size); + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size)); + + } + + if (bounds == default) + bounds.center = go.transform.position; + + return bounds; + } + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public int idType => globalObjectId.identifierType; + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + public ulong prefabId => globalObjectId.targetPrefabId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + + + + public GlobalID UnpackForPrefab() + { + var unpackedFileId = (this.fileId ^ this.prefabId) & 0x7fffffffffffffff; + + + var unpackedGId = new GlobalID($"GlobalObjectId_V1-{this.idType}-{this.guid}-{unpackedFileId}-0"); + + return unpackedGId; + + } + + } + + public static GlobalID GetGlobalID(this Object o) => new GlobalID(o); + + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + } + + +#endif + + + + + #endregion + + #region Paths + + + public static string GetParentPath(this string path) => path.Substring(0, path.LastIndexOf('/')); + public static bool HasParentPath(this string path) => path.Contains('/') && path.GetParentPath() != ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + public static string CombinePath(this string p, string p2) => Path.Combine(p, p2); + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + +#endif + + + + + + #endregion + + #region Files + +#if UNITY_EDITOR + + public static void Hide(string path) + { + if (IsHidden(path)) return; + + if (File.Exists(path)) + File.Move(path, path + "~"); + + + path += ".meta"; + if (File.Exists(path)) + File.Move(path, path + "~"); + } + public static void Unhide(string path) + { + if (!IsHidden(path)) return; + if (path.EndsWith("~")) path = path.Substring(0, path.Length - 1); + + if (File.Exists(path + "~")) + File.Move(path + "~", path); + + path += ".meta"; + if (File.Exists(path + "~")) + File.Move(path + "~", path); + } + public static bool IsHidden(this string path) => path.EndsWith("~") || File.Exists(path + "~"); + + public static void CopyDirectoryDeep(string sourcePath, string destinationPath) + { + CopyDirectoryRecursively(sourcePath, destinationPath); + + var metas = GetFilesRecursively(destinationPath, (f) => f.EndsWith(".meta")); + var guidTable = new List<(string originalGuid, string newGuid)>(); + + foreach (string meta in metas) + { + StreamReader file = new StreamReader(meta); + file.ReadLine(); + string guidLine = file.ReadLine(); + file.Close(); + string originalGuid = guidLine.Substring(6, guidLine.Length - 6); + string newGuid = GUID.Generate().ToString().Replace("-", ""); + guidTable.Add((originalGuid, newGuid)); + } + + var allFiles = GetFilesRecursively(destinationPath); + + foreach (string fileToModify in allFiles) + { + string content = File.ReadAllText(fileToModify); + + foreach (var guidPair in guidTable) + { + content = content.Replace(guidPair.originalGuid, guidPair.newGuid); + } + + File.WriteAllText(fileToModify, content); + } + + AssetDatabase.Refresh(); + } + + private static void CopyDirectoryRecursively(string sourceDirName, string destDirName) + { + DirectoryInfo dir = new DirectoryInfo(sourceDirName); + + DirectoryInfo[] dirs = dir.GetDirectories(); + + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string temppath = Path.Combine(destDirName, file.Name); + file.CopyTo(temppath, false); + } + + foreach (DirectoryInfo subdir in dirs) + { + string temppath = Path.Combine(destDirName, subdir.Name); + CopyDirectoryRecursively(subdir.FullName, temppath); + } + } + + private static List GetFilesRecursively(string path, System.Func criteria = null, List files = null) + { + if (files == null) + { + files = new List(); + } + + files.AddRange(Directory.GetFiles(path).Where(f => criteria == null || criteria(f))); + + foreach (string directory in Directory.GetDirectories(path)) + { + GetFilesRecursively(directory, criteria, files); + } + + return files; + } + +#endif + + + + + #endregion + + #region Serialization + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + public List keys = new List(); + public List values = new List(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + public static void ToggleDefineDisabledInScript(Type scriptType) + { + var path = GetScriptPath(scriptType.Name); + + var lines = File.ReadAllLines(path); + if (lines.First().StartsWith("#define DISABLED")) + File.WriteAllLines(path, lines.Skip(1)); + else + File.WriteAllLines(path, lines.Prepend("#define DISABLED // this line was added by VUtils.ToggleDefineDisabledInScript")); + + AssetDatabase.ImportAsset(path); + } + public static bool ScriptHasDefineDisabled(Type scriptType) => File.ReadLines(GetScriptPath(scriptType.Name)).First().StartsWith("#define DISABLED"); + public static void SetDefineDisabledInScript(Type scriptType, bool defineDisabled) + { + if (ScriptHasDefineDisabled(scriptType) != defineDisabled) + ToggleDefineDisabledInScript(scriptType); + + } + + public static int GetProjectId() => Application.dataPath.GetHashCode(); + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void RecordUndo(this Object o) => Undo.RecordObject(o, ""); +#if UNITY_2021_1_OR_NEWER + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); +#else + public static void Save(this Object o) { } +#endif + + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + + public static void RemoveEditorErrors() => removeEditorErrorsMethod.Invoke(null, new object[] { 1 }); + static MethodInfo removeEditorErrorsMethod = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(r => r.GetName().ToString().Contains("UnityEditor.CoreModule")).GetTypes().First(r => r.Name.Contains("LogEntry")).GetMethod("RemoveLogEntriesByMode", BindingFlags.Static | BindingFlags.NonPublic); + + + public static class EditorPrefsCached + { + public static int GetInt(string key, int defaultValue = 0) + { + if (ints_byKey.ContainsKey(key)) + return ints_byKey[key]; + else + return ints_byKey[key] = EditorPrefs.GetInt(key, defaultValue); + + } + public static bool GetBool(string key, bool defaultValue = false) + { + if (bools_byKey.ContainsKey(key)) + return bools_byKey[key]; + else + return bools_byKey[key] = EditorPrefs.GetBool(key, defaultValue); + + } + public static float GetFloat(string key, float defaultValue = 0) + { + if (floats_byKey.ContainsKey(key)) + return floats_byKey[key]; + else + return floats_byKey[key] = EditorPrefs.GetFloat(key, defaultValue); + + } + + public static void SetInt(string key, int value) + { + ints_byKey[key] = value; + + EditorPrefs.SetInt(key, value); + + } + public static void SetBool(string key, bool value) + { + bools_byKey[key] = value; + + EditorPrefs.SetBool(key, value); + + } + public static void SetFloat(string key, float value) + { + floats_byKey[key] = value; + + EditorPrefs.SetFloat(key, value); + + } + + + static Dictionary bools_byKey = new(); + static Dictionary floats_byKey = new(); + static Dictionary ints_byKey = new(); + + } + + +#endif + + + + + + #endregion + + } + + public static class VGUI + { + #region Controls + + public static T Field(Rect rect, string name, T cur) + { + if (typeof(Object).IsAssignableFrom(typeof(T))) + return (T)(object)EditorGUI.ObjectField(rect, name, cur as Object, typeof(T), true); + + if (typeof(T) == typeof(float)) + return (T)(object)EditorGUI.FloatField(rect, name, (float)(object)cur); + + if (typeof(T) == typeof(int)) + return (T)(object)EditorGUI.IntField(rect, name, (int)(object)cur); + + if (typeof(T) == typeof(bool)) + return (T)(object)EditorGUI.Toggle(rect, name, (bool)(object)cur); + + if (typeof(T) == typeof(string)) + return (T)(object)EditorGUI.TextField(rect, name, (string)(object)cur); + + return default; + } + public static T Field(string name, T cur) => Field(ExpandWidthLabelRect(), name, cur); + public static void Field(Rect rect, string name, ref T cur) => cur = Field(rect, name, cur); + public static void Field(string name, ref T cur) => cur = Field(name, cur); + + public static T Slider(Rect rect, string name, T cur, T min, T max) + { + if (typeof(T) == typeof(float)) + return (T)(object)EditorGUI.Slider(rect, name, (float)(object)cur, (float)(object)min, (float)(object)max); + + if (typeof(T) == typeof(int)) + return (T)(object)EditorGUI.IntSlider(rect, name, (int)(object)cur, (int)(object)min, (int)(object)max); + + return default; + } + public static T Slider(string name, T cur, T min, T max) => Slider(ExpandWidthLabelRect(), name, cur, min, max); + public static void Slider(Rect rect, string name, ref T cur, T min, T max) => cur = Slider(rect, name, cur, min, max); + public static void Slider(string name, ref T cur, T min, T max) => cur = Slider(name, cur, min, max); + + + + public static bool _ResetFieldButton(Rect rect, bool isObjectField = false) + { + var prev = GUI.color; + GUI.color = Color.clear; + var r = GUI.Button(rect.SetWidthFromRight(20).MoveX(isObjectField ? -18 : 0), ""); + GUI.color = prev; + return r; + } + public static void _DrawResettableFieldCrossIcon(Rect rect, bool isObjectField = false, float brightness = .36f) + { + var iconRect = rect.SetWidthFromRight(20).SetSizeFromMid(15).MoveX(isObjectField ? -18 : 0).MoveY(.5f); + + if (iconRect.Resize(-3).IsHovered()) + brightness = isDarkTheme ? .8f : .65f; + + if (isObjectField) + { + var fieldBg = isDarkTheme ? Greyscale(.152f) : Greyscale(.93f); + iconRect.MoveX(-2).SetWidth(19).Draw(fieldBg); + } + + SetGUIColor(Greyscale(brightness)); + GUI.Label(iconRect, EditorGUIUtility.IconContent("CrossIcon")); + ResetGUIColor(); + + } + + public static T ResettableField(Rect rect, string name, T cur, T resetTo = default) + { + var isObjectField = typeof(Object).IsAssignableFrom(typeof(T)); + var reset = _ResetFieldButton(rect, isObjectField); + + cur = Field(rect, name, cur); + + if (!object.Equals(cur, resetTo)) + _DrawResettableFieldCrossIcon(rect, isObjectField); + + return reset ? resetTo : cur; + } + public static T ResettableField(string name, T cur, T resetTo = default) => ResettableField(ExpandWidthLabelRect(), name, cur, resetTo); + public static void ResettableField(Rect rect, string name, ref T cur, T resetTo = default) => cur = ResettableField(rect, name, cur, resetTo); + public static void ResettableField(string name, ref T cur, T resetTo = default) => cur = ResettableField(name, cur, resetTo); + + public static T ResettableSlider(Rect rect, string name, T cur, T min, T max, T resetTo = default) + { + var reset = _ResetFieldButton(lastRect); + + cur = Slider(rect, name, cur, min, max); + + if (!object.Equals(cur, resetTo)) + _DrawResettableFieldCrossIcon(lastRect); + + return reset ? resetTo : cur; + } + public static T ResettableSlider(string name, T cur, T min, T max, T resetTo = default) => ResettableSlider(ExpandWidthLabelRect(), name, cur, min, max, resetTo); + public static void ResettableSlider(Rect rect, string name, ref T cur, T min, T max, T resetTo = default) => cur = ResettableSlider(rect, name, cur, min, max, resetTo); + public static void ResettableSlider(string name, ref T cur, T min, T max, T resetTo = default) => cur = ResettableSlider(name, cur, min, max, resetTo); + + + + // public static int ResettableFieldWithPlusMinsButtons(Rect rect, string name, int cur, int min, int max, int resetTo = 0, int increment = 1) + // { + // var buttonWidth = 24; + + // cur = ResettableField(rect.SetWidth(rect.width), name, cur, resetTo); + // cur = Mathf.Clamp(cur, min, max); + + + // var prev = GUI.enabled; + // GUI.skin.label.alignment = TextAnchor.MiddleCenter; + + // GUI.enabled = cur != max; + // var rPlus = rect.MoveX(EditorGUIUtility.labelWidth).SetWidth(buttonWidth).Resize(-.5f); + // if (GUI.Button(rPlus, "")) + // cur += increment; + // GUI.Label(rPlus.Resize(2), EditorGUIUtility.IconContent("Toolbar Plus")); + + // GUI.enabled = cur != min; + // var rMinus = rPlus.MoveX() SetWidth(buttonWidth).MoveX(-buttonWidth + 1).Resize(-.5f); + // if (GUI.Button(rMinus, "")) + // cur -= increment; + // GUI.Label(rMinus.Resize(2), EditorGUIUtility.IconContent("Toolbar Minus")); + + // GUI.enabled = prev; + // GUI.skin.label.alignment = TextAnchor.MiddleLeft; + + + + // return cur; + // } + public static int ResettableFieldWithPlusMinsButtons(Rect rect, string name, int cur, int min, int max, int resetTo = 0, int increment = 1) + { + var buttonWidth = 24; + + cur = ResettableField(rect.SetWidth(rect.width - buttonWidth * 2 - 4), name, cur, resetTo); + cur = Mathf.Clamp(cur, min, max); + + var prev = GUI.enabled; + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + + GUI.enabled = cur != max; + var rPlus = rect.SetWidthFromRight(buttonWidth).Resize(-.5f); + if (GUI.Button(rPlus, "")) + cur += increment; + GUI.Label(rPlus.Resize(2), EditorGUIUtility.IconContent("Toolbar Plus")); + + GUI.enabled = cur != min; + var rMinus = rect.SetWidthFromRight(buttonWidth).MoveX(-buttonWidth + 1).Resize(-.5f); + if (GUI.Button(rMinus, "")) + cur -= increment; + GUI.Label(rMinus.Resize(2), EditorGUIUtility.IconContent("Toolbar Minus")); + + GUI.enabled = prev; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + + + + return cur; + } + public static int ResettableFieldWithPlusMinsButtons(string name, int cur, int min, int max, int resetTo = 0, int increment = 1) => ResettableFieldWithPlusMinsButtons(ExpandWidthLabelRect(), name, cur, min, max, resetTo, increment); + public static int ResettableFieldWithPlusMinsButtons(Rect rect, string name, ref int cur, int min, int max, int resetTo = 0, int increment = 1) => cur = ResettableFieldWithPlusMinsButtons(rect, name, cur, min, max, resetTo, increment); + public static int ResettableFieldWithPlusMinsButtons(string name, ref int cur, int min, int max, int resetTo = 0, int increment = 1) => cur = ResettableFieldWithPlusMinsButtons(name, cur, min, max, resetTo, increment); + + public static int FieldWithPlusMinsButtons(Rect rect, string name, int cur, int min, int max, int increment = 1) => ResettableFieldWithPlusMinsButtons(rect, name, cur, min, max, cur, increment); + public static int FieldWithPlusMinsButtons(string name, int cur, int min, int max, int increment = 1) => FieldWithPlusMinsButtons(ExpandWidthLabelRect(), name, cur, min, max, increment); + public static int FieldWithPlusMinsButtons(Rect rect, string name, ref int cur, int min, int max, int increment = 1) => cur = FieldWithPlusMinsButtons(rect, name, cur, min, max, increment); + public static int FieldWithPlusMinsButtons(string name, ref int cur, int min, int max, int increment = 1) => cur = FieldWithPlusMinsButtons(name, cur, min, max, increment); + + + public static bool OnOffButton(Rect rect, string name, bool val) + { + var r = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 12321; + EditorGUI.PrefixLabel(rect, new GUIContent(name)); + EditorGUIUtility.labelWidth = r; + + var rr = GUI.backgroundColor; + GUI.backgroundColor = val ? GUIColors.pressedButtonBackground : rr; + if (GUI.Button(rect.SetWidthFromRight(50).Resize(-1), val ? "On\u2009\u2009" : "Off")) + val = !val; + GUI.backgroundColor = rr; + + + return val; + } + public static bool OnOffButton(string name, bool val) => OnOffButton(ExpandWidthLabelRect(), name, val); + public static void OnOffButton(Rect rect, string name, ref bool val) => val = OnOffButton(rect, name, val); + public static void OnOffButton(string name, ref bool val) => val = OnOffButton(ExpandWidthLabelRect(), name, val); + + + public static bool Toggle(Rect rect, string name, bool val) + { + val = EditorGUI.Toggle(rect, name, val); + + var r = GUI.color; + GUI.color = Color.clear; + if (GUI.Button(lastRect, "")) + val = !val; + GUI.color = Color.white; + + return val; + } + public static bool Toggle(string name, bool val) => Toggle(ExpandWidthLabelRect(), name, val); + public static void Toggle(Rect rect, string name, ref bool val) => val = Toggle(rect, name, val); + public static void Toggle(string name, ref bool val) => val = Toggle(ExpandWidthLabelRect(), name, val); + + + + public static bool LeftToggle(Rect rect, string name, bool val, float offset = 0) + { + rect = rect.MoveX(offset); + val = EditorGUI.Toggle(rect, val); + + var lr = rect.MoveX(19); + EditorGUI.LabelField(lr, name); + + + return val; + } + public static bool LeftToggle(string name, bool val, float offset = 0) => LeftToggle(ExpandWidthLabelRect(), name, val, offset); + public static void LeftToggle(Rect rect, string name, ref bool val, float offset = 0) => val = LeftToggle(rect, name, val, offset); + public static void LeftToggle(string name, ref bool val, float offset = 0) => val = LeftToggle(ExpandWidthLabelRect(), name, val, offset); + + + + public static Color SmallColorField(Rect rect, string name, Color val, bool showAlpha = true, bool hdr = false) + { + EditorGUI.PrefixLabel(rect, new GUIContent(name)); + + + var colorRect = rect.SetWidthFromRight(50).MoveX(2); + + + val = EditorGUI.ColorField(colorRect.Resize(2), new GUIContent(""), val, false, showAlpha, hdr); + + + var outlineColor = Greyscale(colorRect.IsHovered() ? .5f : .1f); + + var colNoAlpha = new Color(val.r, val.g, val.b, 1); + + colorRect.SetWidthFromMid(colorRect.width - 2).Draw(outlineColor); + colorRect.SetHeightFromMid(colorRect.height - 2).Draw(outlineColor); + + colorRect.Resize(2).SetWidthFromMid(colorRect.width - 2).Draw(colNoAlpha); + colorRect.Resize(2).SetHeightFromMid(colorRect.height - 2).Draw(colNoAlpha); + + + if (showAlpha) + { + var alphaRect = colorRect.SetHeightFromBottom(1).AddWidth(-4).MoveX(2).MoveY(-1); + + alphaRect.Draw(); + alphaRect.SetWidth(alphaRect.width * val.a).Draw(Color.white); + + } + + + return val; + } + public static Color SmallColorField(string name, Color val, bool showAlpha = true, bool hdr = false) => SmallColorField(ExpandWidthLabelRect().MoveX(-1), name, val, showAlpha, hdr); + public static void SmallColorField(Rect rect, string name, ref Color val, bool showAlpha = true, bool hdr = false) => val = SmallColorField(rect, name, val, showAlpha, hdr); + public static void SmallColorField(string name, ref Color val, bool showAlpha = true, bool hdr = false) => val = SmallColorField(ExpandWidthLabelRect(), name, val, showAlpha, hdr); + + + + + public static void ObjectFieldWidhoutPicker(Rect rect, string name, T val) where T : Object + { + var id = GUIUtility.GetControlID("s_ObjectFieldHash".GetHashCode(), FocusType.Keyboard, rect); + + void draw() + { + if (!curEvent.isRepaint) return; + + + EditorGUI.PrefixLabel(rect, new GUIContent(name)); + + + var fieldRect = rect.SetWidthFromRight(rect.width - EditorGUIUtility.labelWidth); + + var prev = EditorGUIUtility.GetIconSize(); + EditorGUIUtility.SetIconSize(Vector2.one * 12); + + var style = new GUIStyle(EditorStyles.textField); + style.imagePosition = ImagePosition.ImageLeft; + style.Draw(fieldRect, EditorGUIUtility.ObjectContent(val, typeof(T)), id, DragAndDrop.activeControlID == id, fieldRect.IsHovered()); + + EditorGUIUtility.SetIconSize(prev); + + } + void click() + { + if (!curEvent.isMouseDown || !rect.IsHovered() || curEvent.mouseButton != 0) return; + + + EditorGUIUtility.editingTextField = false; + + var obj = val as Object; + var component = val as Component; + + if (component) + obj = component.gameObject; + + if (curEvent.clickCount == 1) + { + GUIUtility.keyboardControl = id; + + EditorGUIUtility.PingObject(obj); + curEvent.Use(); + } + + if (curEvent.clickCount == 2 || (component && curEvent.clickCount == 1)) + { + if (obj) + { + AssetDatabase.OpenAsset(obj); + GUIUtility.ExitGUI(); + } + curEvent.Use(); + } + } + + + draw(); + click(); + + + } + public static void ObjectFieldWidhoutPicker(string name, T val) where T : Object => ObjectFieldWidhoutPicker(ExpandWidthLabelRect(), name, val); + + + + + public static bool InvisibleButton(Rect rect) + { + var c = GUI.color; + var e = GUI.enabled; + GUI.color = Color.clear; + GUI.enabled = true; + var r = GUI.Button(rect, ""); + GUI.color = c; + GUI.enabled = e; + + return r; + + } + + + + + + + + public static bool Foldout(string name, bool val) + { + EditorGUILayout.PrefixLabel(name); + val = EditorGUI.Foldout(lastRect, val, ""); + + if (lastRect.IsHovered() && curEvent.isMouseDown) + val = !val; + + return val; + } + + public static bool ClearButtonText(Rect rect, string text, bool val = false, bool drawPressed = false) + { + var hovered = rect.IsHovered(); + + if (hovered && curEvent.isMouseUp) + { + val = !val; + curEvent.Use(); + } + + var col = Color.white; + if (drawPressed && val) col *= new Color(.35f, .57f, 1f, 1f) * 1.25f; + + var r = GUI.skin.label.alignment; + var rr = GUI.color; + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUI.color = col; + GUI.Label(rect, text); + GUI.color = rr; + GUI.skin.label.alignment = r; + + return val; + } + + + + public static void ColorPickerButton(Rect rect, Color color, System.Action colorChangedCallback, bool showAlphaBar = false, Color displayTint = default, bool greyedOut = false) + { + if (displayTint == default) + displayTint = Color.white; + + if (GUI.Button(rect, "")) + OpenColorPicker(colorChangedCallback, color, true, false); + + var c = color; c.a = 1; + + rect.SetHeight(showAlphaBar ? rect.height - 2 : rect.height).Draw(c * displayTint * (greyedOut ? GUIColors.greyedOutTint : Color.white)); + + if (showAlphaBar) + { + var rAlphaBar = rect.SetHeightFromBottom(2).MoveY(0); + rAlphaBar.width *= color.a; + rAlphaBar.Draw(Greyscale(1, .65f) * (greyedOut ? GUIColors.greyedOutTint : Color.white)); + } + + // rect.DrawOutline(Greyscale(.15f)); + } + + + + + + public static int ResolutionPopup(string name, int cur, int min = 256, int max = 4096) + { + var ints = Enumerable.Range((int)Mathf.Log(min, 2), (int)Mathf.Log(max, 2) - (int)Mathf.Log(min, 2) + 1).Select(r => (int)Mathf.Pow(2, r)).ToArray(); + return EditorGUILayout.IntPopup(name, cur, ints.Select(r => r.ToString()).ToArray(), ints); + } + public static int HeightmapResolutionPopup(string name, int cur, int min = 513, int max = 16385) + { + var ints = Enumerable.Range((int)Mathf.Log(min - 1, 2), (int)Mathf.Log(max - 1, 2) - (int)Mathf.Log(min - 1, 2) + 1).Select(r => (int)Mathf.Pow(2, r)).Select(r => r + 1).ToArray(); + return EditorGUILayout.IntPopup(name, cur, ints.Select(r => r.ToString()).ToArray(), ints); + } + + public static T SelectorPopup(Rect rect, string name, T selected, List options, System.Func nameGetter, int labelOffset = -1) + { + var names = options.Select(r => nameGetter(r)).ToList(); + var curName = nameGetter(selected); + var ints = Enumerable.Range(0, options.Count); + int r = 0; + if (labelOffset < 0) + r = EditorGUI.IntPopup(rect, name, names.IndexOf(curName), names.ToArray(), ints.ToArray()); + else + { + r = EditorGUI.IntPopup(rect, names.IndexOf(curName), names.ToArray(), ints.ToArray()); + GUI.Label(rect.MoveX(-name.GetLabelWidth() - labelOffset), name); + } + + return options[r]; + } + public static T SelectorPopup(string name, T selected, List options, System.Func nameGetter) { GUILayout.Label("", GUILayout.ExpandWidth(true)); return SelectorPopup(lastRect, name, selected, options, nameGetter); } + + // todo make it a class + public static void SelectorWithIcons(ref T selected, List options, System.Func nameGetter, System.Func textureGetter, System.Func uvsGetter, ref Vector2 scrollPos, + ref bool renaming, ref string renamingTempName, float buttonSize = 60, float iconBorder = 1, bool showNoneOption = false, bool dragndropPossible = false, + System.Action onAdd = null, System.Action onDragndrop = null, System.Action onRenameEnded = null, System.Action onSelectedClicked = null, System.Action onRightclicked = null, Material iconMaterial = null, bool showPlusButton = true, + string noneOptionText = "None", Texture2D noneOptionTexture = null) + { + var renaming_ = renaming; + var selected_ = selected; + bool pressedSelect = false; + + var newNamerenamingTempName_ = renamingTempName; + + Rect nameRect(Rect lastRect) => lastRect.MoveY(lastRect.height).Resize(1).SetHeight(18); + void drawSelectedBlue(Rect lastRect, string name) + { + var rname = nameRect(lastRect); + + var rselect = rname.SetWidth(Mathf.Min(rname.width, name.GetLabelWidth() + 6)); + rselect.x += (rname.width - rselect.width) / 2; + rselect = rselect.Resize(2); + rselect.Draw(GUI.skin.settings.selectionColor * .9f); + } + void drawName(Rect rname, string name) + { + if (name.GetLabelWidth() < buttonSize) + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + GUI.Label(rname, name); + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + void item(T t) + { + if (GUILayout.Button("", GUILayout.Height(buttonSize), GUILayout.Width(buttonSize))) + { + if (t.Equals(selected_)) onSelectedClicked?.Invoke(); + else { selected_ = t; pressedSelect = true; } + + if (curEvent.mouseButton == 1 && onRightclicked != null) + onRightclicked(t); + } + + + // if (iconMaterial) EditorGUI.DrawPreviewTexture(lastRect.Resize(iconBorder), textureGetter(t), iconMaterial); + // else EditorGUI.DrawPreviewTexture(lastRect.Resize(iconBorder), textureGetter(t)); + var uvs = uvsGetter?.Invoke(t); + if (uvs != null) + GUI.DrawTextureWithTexCoords(lastRect.Resize(iconBorder), textureGetter(t), uvs.GetValueOrDefault()); + else if (iconMaterial != null) + EditorGUI.DrawPreviewTexture(lastRect.Resize(iconBorder), textureGetter(t), iconMaterial); + else + GUI.DrawTexture(lastRect.Resize(iconBorder), textureGetter(t)); + + + + if (t.Equals(selected_)) drawSelectedBlue(lastRect, nameGetter(t)); + + + if (curEvent.isKeyDown && curEvent.keyCode == KeyCode.Return) + if (!renaming_) { newNamerenamingTempName_ = nameGetter(selected_); renaming_ = true; curEvent.Use(); } + else { onRenameEnded(); renaming_ = false; curEvent.Use(); } + + if (t.Equals(selected_) && renaming_) + { + GUI.SetNextControlName("w"); + GUI.FocusControl("w"); + newNamerenamingTempName_ = GUI.TextField(nameRect(lastRect), newNamerenamingTempName_); + } + else + drawName(nameRect(lastRect), nameGetter(t)); + + } + void plus() + { + if (!showPlusButton) return; + + GUILayout.Label("", GUILayout.Height(buttonSize), GUILayout.Width(buttonSize)); + var rect = lastRect.Resize(buttonSize / 9).MoveX(-buttonSize / 9); + + if (GUI.Button(rect, EditorGUIUtility.IconContent("Toolbar Plus"))) + onAdd(); + + + + if (!dragndropPossible) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + var mouseIn = rect.Contains(curEvent.mousePosition); + + if (mouseIn) EditorGUI.DrawRect(rect, Greyscale(.8f, .07f)); + + + + if (!curEvent.isDragPerform || !mouseIn) return; + + DragAndDrop.AcceptDrag(); + + onDragndrop(DragAndDrop.objectReferences[0]); + } + void none() + { + if (!showNoneOption) return; + + + if (GUILayout.Button(noneOptionTexture, GUILayout.Height(buttonSize), GUILayout.Width(buttonSize))) + selected_ = default(T); + + + + + // if (selected_.Equals(default(T))) drawSelectedBlue(lastRect, noneOptionText); + if (selected_ == null) drawSelectedBlue(lastRect, noneOptionText); + drawName(nameRect(lastRect), noneOptionText); + } + + + scrollPos = GUILayout.BeginScrollView(scrollPos, false, false, GUIStyle.none, GUIStyle.none); + GUILayout.BeginHorizontal(); + + + none(); + + foreach (var r in options) + { + item(r); Space(2); + } + + plus(); + + GUILayout.EndHorizontal(); + GUILayout.Space(24); + EditorGUILayout.EndScrollView(); + + + renamingTempName = newNamerenamingTempName_; + renaming = renaming_; + + if (pressedSelect) selected = selected_; + } + + + // prev Toggle + public static bool ToggleLeftOld(string s, bool b, float offset = 6, float textOffset = -4, bool expandWidth = true) + { + bool washor = __hor; + if (!washor) Horizontal(); + GUILayout.Space(offset); + var r = EditorGUILayout.Toggle(b, GUILayout.Width(20)); + GUILayout.Space(EditorGUI.indentLevel * 16 + textOffset); + GUILayout.Label(s, GUILayout.ExpandWidth(expandWidth)); + if (!washor) Horizontal(); + + return r; + } + + public static bool ToggleLeftOld(Rect rect, string s, bool b, float offset = 6, float textOffset = -4, bool expandWidth = true) + { + // bool washor = __hor; + + return EditorGUI.ToggleLeft(rect, s, b); + + // var r = EditorGUILayout.Toggle(b, GUILayout.Width(20)); + // GUILayout.Space(EditorGUI.indentLevel * 16 + textOffset); + // GUILayout.Label(s, GUILayout.ExpandWidth(expandWidth)); + + // return r; + } + + + + + + + + public static float ExpSlider(Rect rect, string name, float val, float min, float max, bool showDecimal = true) + { + EditorGUI.PrefixLabel(rect, new GUIContent(name)); + + var valWindowSize = 50f; + + + var rVal = rect.SetWidthFromRight(valWindowSize); + + if (!showDecimal) + { + var i = Mathf.RoundToInt(val); + var ii = EditorGUI.IntField(rVal, i); + if (ii != i) + val = ii; + } + else + val = EditorGUI.FloatField(rVal, val); + + + var rSlider = rect.MoveX(-valWindowSize - 4).SetWidthFromRight(rect.width - EditorGUIUtility.labelWidth - valWindowSize - 6); + var e = 2.71828; + var k = (max - min) / (e - 1); + var c0 = min - k; + var t = System.Math.Log(((double)val - c0) / k); + + EditorGUI.BeginChangeCheck(); + var tSlider = GUI.HorizontalSlider(rSlider, (float)t, 0, 1); + var newVal = (float)(c0 + k * System.Math.Pow(e, tSlider)); + + + return EditorGUI.EndChangeCheck() ? Mathf.Clamp(newVal, min, max) : val; + } + public static float ExpSlider(string name, float val, float min, float max, bool showDecimal = true) => ExpSlider(ExpandWidthLabelRect(), name, val, min, max, showDecimal); + + + + + #endregion + + #region Colors + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + #endregion + + #region Shortcuts + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fotSize) + { + SetLabelFontSize(fotSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + + public static void SetGUIColor(Color c) + { + if (!_guiColorModified) + _defaultGuiColor = GUI.color; + + _guiColorModified = true; + + GUI.color = _defaultGuiColor * c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorModified ? _defaultGuiColor : Color.white; + + _guiColorModified = false; + + } + static bool _guiColorModified; + static Color _defaultGuiColor; + + + + #endregion + + #region Events + + + public class WrappedEvent + { + public Event e; + + public bool isRepaint => e.type == EventType.Repaint; + public bool isLayout => e.type == EventType.Layout; + public bool isUsed => e.type == EventType.Used; + public bool isMouseLeaveWindow => e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => e.type == EventType.MouseEnterWindow; + public bool isContextClick => e.type == EventType.ContextClick; + + public bool isKeyDown => e.type == EventType.KeyDown; + public bool isKeyUp => e.type == EventType.KeyUp; + public KeyCode keyCode => e.keyCode; + public char characted => e.character; + + public bool isExecuteCommand => e.type == EventType.ExecuteCommand; + public string commandName => e.commandName; + + public bool isMouse => e.isMouse; + public bool isMouseDown => e.type == EventType.MouseDown; + public bool isMouseUp => e.type == EventType.MouseUp; + public bool isMouseDrag => e.type == EventType.MouseDrag; + public bool isMouseMove => e.type == EventType.MouseMove; + public bool isScroll => e.type == EventType.ScrollWheel; + public int mouseButton => e.button; + public int clickCount => e.clickCount; + public Vector2 mousePosition => e.mousePosition; + public Vector2 mousePosition_screenSpace => GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => e.delta; + + public bool isDragUpdate => e.type == EventType.DragUpdated; + public bool isDragPerform => e.type == EventType.DragPerform; + public bool isDragExit => e.type == EventType.DragExited; + + public EventModifiers modifiers => e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => e.alt; + public bool holdingShift => e.shift; + public bool holdingCtrl => e.control; + public bool holdingCmd => e.command; + public bool holdingCmdOrCtrl => e.command || e.control; + + public bool holdingAltOnly => e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new(e); + + public static WrappedEvent curEvent => _curEvent ??= typeof(Event).GetFieldValue("s_Current").Wrap(); + static WrappedEvent _curEvent; + + + + + + #endregion + + #region Layout + + + + public static void BeginPanel(string title, float height, System.Action onClose = null, System.Action onApply = null) + { + + void bg() + { + GUI.enabled = false; + SetGUIColor(Greyscale(.75f)); + var r = ExpandWidthLabelRect(0).SetHeight(height); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + ResetGUIColor(); + GUI.enabled = true; + } + + void layout() + { + + GUILayout.BeginHorizontal(); + Space(7); + EditorGUIUtility.labelWidth -= 7; + GUILayout.BeginVertical(); + + } + + void title_() + { + Space(5); + EditorGUI.PrefixLabel(ExpandWidthLabelRect(), new GUIContent(title)); + // GUI.skin.label.fontStyle = FontStyle.Bold; + // GUILayout.Label(title); + + } + + void buttons() + { + var rClose = lastRect.SetWidthFromRight(16).MoveX(1).MoveY(-1); + + // if() + GUI.color = Greyscale(rClose.IsHovered() ? .9f : .6f); + GUI.Label(rClose, EditorGUIUtility.IconContent("CrossIcon")); + + GUI.color = Color.clear; + if (GUI.Button(rClose, "")) + onClose?.Invoke(); + + + var rApply = rClose.MoveX(-19).Resize(-1); + + GUI.color = Greyscale(rApply.IsHovered() ? .9f : .6f); + GUI.Label(rApply, EditorGUIUtility.IconContent("check")); + + GUI.color = Color.clear; + if (GUI.Button(rApply, "")) + onApply?.Invoke(); + + + GUI.color = Color.white; + + } + + + + bg(); + layout(); + title_(); + buttons(); + + Space(5); + + } + + public static void EndPanel() + { + GUILayout.EndVertical(); + Space(7); + EditorGUIUtility.labelWidth = 0; + GUILayout.EndHorizontal(); + } + + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new Stack(); + + + public static void Horizontal() { if (__hor) GUILayout.EndHorizontal(); else GUILayout.BeginHorizontal(); __hor = !__hor; } + public static void Vertical() { if (__v) GUILayout.EndVertical(); else GUILayout.BeginVertical(); __v = !__v; } + public static void Area(Rect r) { if (__a) GUILayout.EndArea(); else GUILayout.BeginArea(r); __a = !__a; } + public static void Area() { if (__a) GUILayout.EndArea(); __a = !__a; } + public static void ResetUIBools() { __a = __hor = __v = false; _prevGuiEnabled = true; } + static bool __hor, __a, __v; + + #endregion + + #region Drawing + + public static Rect Draw(this Rect r) { EditorGUI.DrawRect(r, Color.black); return r; } + public static Rect Draw(this Rect r, Color c) { EditorGUI.DrawRect(r, c); return r; } + + public static Rect DrawOutline(this Rect r) => r.DrawOutline(Color.black); + public static Rect DrawOutline(this Rect r, Color c) { OutlineRect(r, c, false, 1); return r; } + + public static void OutlineRect(Rect rect, Color col, bool greyedOut = false, int px = 1) + { + bool offset = false; + + int f = px; + Rect r; + // + Color tint = greyedOut ? Color.white * .74f : Color.white; + + r = rect; + r.height = f; + + if (offset) + { + r.x += 1; + r.width -= 2; + r.y += 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.width = f; + + if (offset) + { + r.y += 1; + r.height -= 2; + r.x += 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.y += r.height; + r.height = f; + r.y -= r.height; + + if (offset) + { + r.x += 1; + r.width -= 2; + r.y -= 1; + } + + EditorGUI.DrawRect(r, col * tint); + + r = rect; + r.x += r.width; + r.width = f; + r.x -= r.width; + + if (offset) + { + r.y += 1; + r.height -= 2; + r.x -= 1; + } + + EditorGUI.DrawRect(r, col * tint); + } + + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, float cornerRadius) => rect.DrawWithRoundedCorners(color, cornerRadius.RoundToInt()); + static Dictionary _roundedStylesByCornerRadius = new Dictionary(); + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = new GaussianKernel(false, blurRadiusScaled).Array2d(); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new Dictionary<(int, int), GUIStyle>(); + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + static Texture2D[] _gradientTextures; + + + + public static bool IsHovered(this Rect r) => r.Contains(curEvent.mousePosition); + + #endregion + + #region Spacing + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static void Divider(float space = 15, float yOffset = 0) + { + GUILayout.Label("", GUILayout.Height(space), GUILayout.ExpandWidth(true)); + lastRect.SetHeightFromMid(1).SetWidthFromMid(lastRect.width - 16).MoveY(yOffset).Draw(isDarkTheme ? Color.white * .42f : Color.white * .72f); + } + + public static Rect ExpandSpace() { GUILayout.Label("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); return lastRect; } + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + #endregion + + #region Icons + + + public static class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath) + { + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult)) return cachedResult; + + var icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + return icons_byName[iconNameOrPath] = icon; + + } + + static Dictionary icons_byName = new Dictionary(); + } + + + + + #endregion + + #region Other + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + #endregion + + } + +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs.meta new file mode 100644 index 00000000..68cfb99a --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd278e4ae9a8649f59f016c7d739ce1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs b/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs new file mode 100644 index 00000000..474ae5b6 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs @@ -0,0 +1,353 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + +namespace VHierarchy +{ + public class VHierarchyLightingWindow : EditorWindow + { + void OnGUI() + { + void updateSize() + { + var r = ExpandWidthLabelRect(); + + if (!curEvent.isRepaint) return; + + var curHeight = r.y; + + this.position = position.SetWidth(initWidth).SetHeight(curHeight); + + this.minSize = Vector2.zero; + this.maxSize = Vector2.one * 123212; + + } + + void header() + { + var height = 22f; + + var headerRect = Rect.zero.SetHeight(height).SetWidth(position.width); + var pinButtonRect = headerRect.SetWidthFromRight(17).SetHeightFromMid(17).Move(-21, .5f); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, .5f); + + void startDragging() + { + if (isDragged) return; + if (!curEvent.isMouseDrag) return; + if (!headerRect.IsHovered()) return; + + + isDragged = true; + + dragStartMousePos = EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition); + dragStartWindowPos = position.position; + + + isPinned = true; + + EditorApplication.RepaintHierarchyWindow(); + + + } + void updateDragging() + { + if (!isDragged) return; + + if (!curEvent.isRepaint) // ?? + position = position.SetPos(dragStartWindowPos + EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition) - dragStartMousePos); + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!isDragged) return; + if (!curEvent.isMouseUp) return; + + isDragged = false; + + EditorGUIUtility.hotControl = 0; + + } + + void background() + { + headerRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.185f) : Greyscale(.7f)); + } + void title_() + { + SetGUIColor(Greyscale(.8f)); + SetLabelAlignmentCenter(); + + GUI.Label(headerRect, "Lighting"); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void pinButton() + { + if (!isPinned && closeButtonRect.IsHovered()) return; + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.8f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + var activeColor = Color.white; + + + + SetGUIColor(isPinned ? activeColor : pinButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(pinButtonRect, EditorGUIUtility.IconContent("pinned")); + + ResetGUIColor(); + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(pinButtonRect, ""); + + ResetGUIColor(); + + + if (!clicked) return; + + isPinned = !isPinned; + + } + void closeButton() + { + + SetGUIColor(Color.clear); + + if (GUI.Button(closeButtonRect, "") || (curEvent.isKeyDown && curEvent.keyCode == KeyCode.Escape)) + Close(); + + ResetGUIColor(); + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.35f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + + + SetGUIColor(closeButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(closeButtonRect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetGUIColor(); + + + if (isPinned) return; + + var escRect = closeButtonRect.Move(-22, -1).SetWidth(70); + + SetGUIEnabled(false); + + if (closeButtonRect.IsHovered()) + GUI.Label(escRect, "Esc"); + + ResetGUIEnabled(); + + } + + + startDragging(); + updateDragging(); + stopDragging(); + + background(); + title_(); + pinButton(); + closeButton(); + + Space(height); + + } + void directionalLight() + { + var light = FindObjects().Where(r => r.type == LightType.Directional && r.gameObject.scene == EditorSceneManager.GetActiveScene()).FirstOrDefault(); + + if (!light) return; + + light.RecordUndo(); + light.transform.RecordUndo(); + + + ObjectFieldWidhoutPicker("Directional Light", light); + + + Space(2); + BeginIndent(8); + EditorGUIUtility.labelWidth += 2; + + var rotX = light.transform.eulerAngles.x.Loop(-180, 180).Round(); + var rotY = light.transform.eulerAngles.y.Loop(-180, 180).Round(); + rotX = EditorGUILayout.Slider("Rotation X", rotX, 0, 90); + rotY = EditorGUILayout.Slider("Rotation Y", rotY, -179, 180); + if (light.transform.rotation != Quaternion.Euler(rotX, rotY, light.transform.eulerAngles.z)) + light.transform.rotation = Quaternion.Euler(rotX, rotY, light.transform.eulerAngles.z); + + + Space(3); + light.intensity = EditorGUILayout.Slider("Intensity", light.intensity, 0, 2); + light.color = SmallColorField(ExpandWidthLabelRect().AddWidthFromMid(-1).MoveX(-.5f), "Color", light.color, true); + + EndIndent(); + + } + void ambientLight() + { + RenderSettings.ambientMode = (UnityEngine.Rendering.AmbientMode)EditorGUILayout.IntPopup("Ambient Light", (int)RenderSettings.ambientMode, new[] { "\u2009Skybox", "\u2009Gradient", "\u2009Color" }, new[] { 0, 1, 3 }); + + foreach (var r in FindObjects()) + r.RecordUndo(); + + + Space(2); + BeginIndent(8); + EditorGUIUtility.labelWidth += 4; + + if (RenderSettings.ambientMode == UnityEngine.Rendering.AmbientMode.Flat) + { + + + Color.RGBToHSV(RenderSettings.ambientSkyColor, out float h, out float s, out float v); + v = EditorGUILayout.Slider("Intensity", v, .01f, 2); + RenderSettings.ambientSkyColor = Color.HSVToRGB(h, s, v, true); + + RenderSettings.ambientSkyColor = SmallColorField("Color", RenderSettings.ambientSkyColor, false, true); + + + } + + if (RenderSettings.ambientMode == UnityEngine.Rendering.AmbientMode.Skybox) + RenderSettings.ambientIntensity = EditorGUILayout.Slider("Intensity", RenderSettings.ambientIntensity, 0, 2); + + if (RenderSettings.ambientMode == UnityEngine.Rendering.AmbientMode.Trilight) + { + RenderSettings.ambientSkyColor = SmallColorField("Color Sky", RenderSettings.ambientSkyColor, false, true); + RenderSettings.ambientEquatorColor = SmallColorField("Color Horizon", RenderSettings.ambientEquatorColor, false, true); + RenderSettings.ambientGroundColor = SmallColorField("Color Ground", RenderSettings.ambientGroundColor, false, true); + } + + EndIndent(); + + } + void fog() + { + var mode = EditorGUILayout.IntPopup("Fog", RenderSettings.fog ? (int)RenderSettings.fogMode : 0, new[] { "\u2009Off", "\u2009Linear", "\u2009Exponential", "\u2009Exponential Squared" }, new[] { 0, 1, 2, 3 }); + + if (RenderSettings.fog = mode != 0) + RenderSettings.fogMode = (FogMode)mode; + + if (!RenderSettings.fog) return; + + Space(2); + BeginIndent(8); + EditorGUIUtility.labelWidth += 4; + + if (RenderSettings.fogMode == FogMode.Linear) + { + RenderSettings.fogStartDistance = EditorGUILayout.FloatField("Start", RenderSettings.fogStartDistance); + RenderSettings.fogEndDistance = EditorGUILayout.FloatField("End", RenderSettings.fogEndDistance); + + } + else + RenderSettings.fogDensity = ExpSlider(ExpandWidthLabelRect().AddWidthFromRight(1.5f), "Density", RenderSettings.fogDensity, 0, .05f); + + + RenderSettings.fogColor = SmallColorField("Color", RenderSettings.fogColor, true, false); + + EndIndent(); + + } + + + header(); + + + BeginIndent(6); + + EditorGUIUtility.labelWidth = 115; + + Space(11); + directionalLight(); + + Space(18); + ambientLight(); + + Space(18); + fog(); + + EndIndent(6); + + Space(21); + + updateSize(); + + + if (Application.platform != RuntimePlatform.OSXEditor) + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + EditorGUIUtility.labelWidth = 0; + + Repaint(); + + + } + + bool isDragged; + Vector2 dragStartMousePos; + Vector2 dragStartWindowPos; + + + + void OnLostFocus() + { + if (isPinned) return; + + Close(); + + } + + public bool isPinned; + + + + + public static void CreateInstance(Vector2 position) + { + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + + instance.position = Rect.zero.SetPos(position).SetSize(initWidth, initHeight); + + instance.minSize = Vector2.zero; + instance.maxSize = Vector2.one * 123212; + + + } + + public static VHierarchyLightingWindow instance; + + + + + static float initWidth => 250; + static float initHeight => 320; + } +} + +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs.meta new file mode 100644 index 00000000..6a1a7b3a --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyLightingWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25324ddc1ebdc4ee5b1d9902a8efafa7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs b/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs new file mode 100644 index 00000000..b041e49e --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs @@ -0,0 +1,151 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + class VHierarchyMenu + { + + public static bool componentMinimapEnabled { get => EditorPrefsCached.GetBool("vHierarchy-componentMinimapEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-componentMinimapEnabled", value); } + public static bool hierarchyLinesEnabled { get => EditorPrefsCached.GetBool("vHierarchy-hierarchyLinesEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-hierarchyLinesEnabled", value); } + public static bool minimalModeEnabled { get => EditorPrefsCached.GetBool("vHierarchy-minimalModeEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-minimalModeEnabled", value); } + public static bool zebraStripingEnabled { get => EditorPrefsCached.GetBool("vHierarchy-zebraStripingEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-zebraStripingEnabled", value); } + public static bool activationToggleEnabled { get => EditorPrefsCached.GetBool("vHierarchy-acctivationToggleEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-acctivationToggleEnabled", value); } + public static bool collapseAllButtonEnabled { get => EditorPrefsCached.GetBool("vHierarchy-collapseAllButtonEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-collapseAllButtonEnabled", value); } + public static bool editLightingButtonEnabled { get => EditorPrefsCached.GetBool("vHierarchy-editLightingButtonEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-editLightingButtonEnabled", value); } + + public static bool toggleActiveEnabled { get => EditorPrefsCached.GetBool("vHierarchy-toggleActiveEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-toggleActiveEnabled", value); } + public static bool focusEnabled { get => EditorPrefsCached.GetBool("vHierarchy-focusEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-focusEnabled", value); } + public static bool deleteEnabled { get => EditorPrefsCached.GetBool("vHierarchy-deleteEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-deleteEnabled", value); } + public static bool toggleExpandedEnabled { get => EditorPrefsCached.GetBool("vHierarchy-toggleExpandedEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-toggleExpandedEnabled", value); } + public static bool collapseEverythingElseEnabled { get => EditorPrefsCached.GetBool("vHierarchy-collapseEverythingElseEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-collapseEverythingElseEnabled", value); } + public static bool collapseEverythingEnabled { get => EditorPrefsCached.GetBool("vHierarchy-collapseEverythingEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-collapseEverythingEnabled", value); } + + public static bool pluginDisabled { get => EditorPrefsCached.GetBool("vHierarchy-pluginDisabled", false); set => EditorPrefsCached.SetBool("vHierarchy-pluginDisabled", value); } + + + + + const string dir = "Tools/vHierarchy/"; + + const string componentMinimap = dir + "Component minimap"; + const string hierarchyLines = dir + "Hierarchy lines"; + const string minimalMode = dir + "Minimal mode"; + const string zebraStriping = dir + "Zebra striping"; + const string activationToggle = dir + "Activation toggle"; + const string collapseAllButton = dir + "Collapse All button"; + const string editLightingButton = dir + "Edit Lighting button"; + + const string toggleActive = dir + "A to toggle active"; + const string focus = dir + "F to focus"; + const string delete = dir + "X to delete"; + const string toggleExpanded = dir + "E to expand or collapse"; + const string collapseEverythingElse = dir + "Shift-E to isolate"; + const string collapseEverything = dir + "Ctrl-Shift-E to collapse all"; + + const string disablePlugin = dir + "Disable vHierarchy"; + + + + + + + [MenuItem(dir + "Features", false, 1)] static void daasddsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsdasas123() => false; + + [MenuItem(componentMinimap, false, 2)] static void daadsdsadasdadsas() { componentMinimapEnabled = !componentMinimapEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(componentMinimap, true, 2)] static bool dadsadasddasadsas() { Menu.SetChecked(componentMinimap, componentMinimapEnabled); return !pluginDisabled; } + + [MenuItem(hierarchyLines, false, 3)] static void dadsadadsadadasss() { hierarchyLinesEnabled = !hierarchyLinesEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(hierarchyLines, true, 3)] static bool dadsaddasdasaasddsas() { Menu.SetChecked(hierarchyLines, hierarchyLinesEnabled); return !pluginDisabled; } + + [MenuItem(minimalMode, false, 4)] static void dadsadadasdsdasadadasss() { minimalModeEnabled = !minimalModeEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(minimalMode, true, 4)] static bool dadsaddadsasdadsasaasddsas() { Menu.SetChecked(minimalMode, minimalModeEnabled); return !pluginDisabled; } + + [MenuItem(zebraStriping, false, 5)] static void dadsadadadssadsadass() { zebraStripingEnabled = !zebraStripingEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(zebraStriping, true, 5)] static bool dadsaddadaadsssadsaasddsas() { Menu.SetChecked(zebraStriping, zebraStripingEnabled); return !pluginDisabled; } + + [MenuItem(activationToggle, false, 6)] static void daadsdsadadsasdadsas() { activationToggleEnabled = !activationToggleEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(activationToggle, true, 6)] static bool dadsadasdsaddasadsas() { Menu.SetChecked(activationToggle, activationToggleEnabled); return !pluginDisabled; } + + [MenuItem(collapseAllButton, false, 7)] static void daadsdsadadsadadsas() { collapseAllButtonEnabled = !collapseAllButtonEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(collapseAllButton, true, 7)] static bool dadsadasdsaddasdsas() { Menu.SetChecked(collapseAllButton, collapseAllButtonEnabled); return !pluginDisabled; } + + [MenuItem(editLightingButton, false, 8)] static void daadsdsasdadadsadadsas() { editLightingButtonEnabled = !editLightingButtonEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(editLightingButton, true, 8)] static bool dadsadasdsadsaddasdsas() { Menu.SetChecked(editLightingButton, editLightingButtonEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Shortcuts", false, 101)] static void dadsas() { } + [MenuItem(dir + "Shortcuts", true, 101)] static bool dadsas123() => false; + + [MenuItem(toggleActive, false, 102)] static void dadsadadsas() => toggleActiveEnabled = !toggleActiveEnabled; + [MenuItem(toggleActive, true, 102)] static bool dadsaddasadsas() { Menu.SetChecked(toggleActive, toggleActiveEnabled); return !pluginDisabled; } + + [MenuItem(focus, false, 103)] static void dadsadasdadsas() => focusEnabled = !focusEnabled; + [MenuItem(focus, true, 103)] static bool dadsadsaddasadsas() { Menu.SetChecked(focus, focusEnabled); return !pluginDisabled; } + + [MenuItem(delete, false, 104)] static void dadsadsadasdadsas() => deleteEnabled = !deleteEnabled; + [MenuItem(delete, true, 104)] static bool dadsaddsasaddasadsas() { Menu.SetChecked(delete, deleteEnabled); return !pluginDisabled; } + + [MenuItem(toggleExpanded, false, 105)] static void dadsadsadasdsadadsas() => toggleExpandedEnabled = !toggleExpandedEnabled; + [MenuItem(toggleExpanded, true, 105)] static bool dadsaddsasadadsdasadsas() { Menu.SetChecked(toggleExpanded, toggleExpandedEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverythingElse, false, 106)] static void dadsadsasdadasdsadadsas() => collapseEverythingElseEnabled = !collapseEverythingElseEnabled; + [MenuItem(collapseEverythingElse, true, 106)] static bool dadsaddsdasasadadsdasadsas() { Menu.SetChecked(collapseEverythingElse, collapseEverythingElseEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverything, false, 107)] static void dadsadsdasadasdsadadsas() => collapseEverythingEnabled = !collapseEverythingEnabled; + [MenuItem(collapseEverything, true, 107)] static bool dadsaddssdaasadadsdasadsas() { Menu.SetChecked(collapseEverything, collapseEverythingEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "More", false, 1001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 1001)] static bool dadsadsdasas123() => false; + + + [MenuItem(dir + "Open manual", false, 1002)] + static void dadadsasdsadsas() => AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath(GetScriptPath("VHierarchy").GetParentPath().CombinePath("Manual.pdf"))); + + + [MenuItem(dir + "Join our Discord", false, 1003)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/4dG9KsbspG"); + + + + [MenuItem(dir + "Deals ending soon/Get vFolders 2 at 50% off", false, 1004)] + static void dadadssadasdsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/255470?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vInspector 2 at 50% off", false, 1005)] + static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/252297?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vTabs 2 at 50% off", false, 1006)] + static void dadadadsssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263645?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vFavorites 2 at 50% off", false, 1007)] + static void dadadadsssadsadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=deal50menu"); + + + + + + [MenuItem(disablePlugin, false, 10001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 10001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + + + + // [MenuItem(dir + "Clear cache", false, 10001)] + // static void dassaadsdc() => VHierarchyCache.Clear(); + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs.meta new file mode 100644 index 00000000..764244b2 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d7f0448bdeda4aad9cfdd9e7c7ee27f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs b/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs.meta new file mode 100644 index 00000000..70b2abbf --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ddf1decb62f94768bcaec3173017c87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs b/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs new file mode 100644 index 00000000..d2ebeadf --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs @@ -0,0 +1,217 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditorInternal; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + public class VHierarchyPalette : ScriptableObject + { + public List colors = new List(); + + public bool colorsEnabled; + + public void ResetColors() + { + colors.Clear(); + + for (int i = 0; i < colorsCount; i++) + colors.Add(GetDefaultColor(i)); + + colorsEnabled = true; + + this.Dirty(); + + } + + public static Color GetDefaultColor(int colorIndex) + { + Color color = default; + + void grey() + { + if (colorIndex >= greyColorsCount) return; + +#if UNITY_2022_1_OR_NEWER + color = Greyscale(isDarkTheme ? .16f : .9f); +#else + color = Greyscale(isDarkTheme ? .315f : .9f); +#endif + + } + void rainbowDarkTheme() + { + if (colorIndex < greyColorsCount) return; + if (!isDarkTheme) return; + + color = HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .45f, .35f); + + if (colorIndex == 1) + color *= 1.2f; + + if (colorIndex == 2) + color *= 1.1f; + + if (colorIndex == 6) + color *= 1.35f; + + if (colorIndex == 7) + color *= 1.3f; + + if (colorIndex == 8) + color *= 1.05f; + + + color.a = .1f; + + } + void rainbowLightTheme() + { + if (colorIndex < greyColorsCount) return; + if (isDarkTheme) return; + + color = HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .62f, .8f); + + color.a = .1f; + + } + + grey(); + rainbowDarkTheme(); + rainbowLightTheme(); + + return color; + + } + + public static int greyColorsCount = 1; + public static int rainbowColorsCount = 8; + public static int colorsCount => greyColorsCount + rainbowColorsCount; + + + + + public List iconRows = new List(); + + [System.Serializable] + public class IconRow + { + public List builtinIcons = new List(); // names + public List customIcons = new List(); // guids + + public bool enabled = true; + + public bool isCustom => !builtinIcons.Any() || customIcons.Any(); + public bool isEmpty => !builtinIcons.Any() && !customIcons.Any(); + public int iconCount => builtinIcons.Count + customIcons.Count; + + public IconRow(string[] builtinIcons) => this.builtinIcons = builtinIcons.ToList(); + public IconRow() { } + + } + + public void ResetIcons() + { + iconRows.Clear(); + + iconRows.Add(new IconRow(new[] + { + "Folder Icon", + "Canvas Icon", + "AvatarMask On Icon", + "cs Script Icon", + "StandaloneInputModule Icon", + "EventSystem Icon", + "Terrain Icon", + "ScriptableObject Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Camera Icon", + "ParticleSystem Icon", + "TrailRenderer Icon", + "Material Icon", + "ReflectionProbe Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Light Icon", + "DirectionalLight Icon", + "LightmapParameters Icon", + "LightProbes Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Rigidbody Icon", + "BoxCollider Icon", + "SphereCollider Icon", + "CapsuleCollider Icon", + "WheelCollider Icon", + "MeshCollider Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "AudioSource Icon", + "AudioClip Icon", + "AudioListener Icon", + "AudioEchoFilter Icon", + "AudioReverbZone Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "PreMatCube", + "PreMatSphere", + "PreMatCylinder", + "PreMatQuad", + "Favorite", + #if UNITY_2021_3_OR_NEWER + "Settings Icon", + #endif + + })); + + this.Dirty(); + + } + + + + + [ContextMenu("Export palette")] + public void Export() + { + var packagePath = EditorUtility.SaveFilePanel("Export vHierarchy Palette", "", this.GetPath().GetFilename(withExtension: false), "unitypackage"); + + var iconPaths = iconRows.SelectMany(r => r.customIcons).Select(r => r.ToPath()).Where(r => !r.IsNullOrEmpty()); + + AssetDatabase.ExportPackage(iconPaths.Append(this.GetPath()).ToArray(), packagePath); + + EditorUtility.RevealInFinder(packagePath); + + } + + + + + void Reset() { ResetColors(); ResetIcons(); } + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs.meta new file mode 100644 index 00000000..bec7d1c5 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPalette.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61290d425f1c94a8cbfb56f754ca6757 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs b/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs new file mode 100644 index 00000000..602030dd --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs @@ -0,0 +1,723 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEditorInternal; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +using static VHierarchy.VHierarchyPalette; + + + +namespace VHierarchy +{ + [CustomEditor(typeof(VHierarchyPalette))] + public class VHierarchyPaletteEditor : Editor + { + public override void OnInspectorGUI() + { + void colors() + { + var rowRect = ExpandWidthLabelRect(cellSize).SetX(rowsOffsetX).SetWidth(rowWidth); + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void toggle() + { + var toggleRect = rowRect.SetWidth(16).MoveX(5); + + var prevEnabled = palette.colorsEnabled; + var newEnabled = EditorGUI.Toggle(toggleRect, palette.colorsEnabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + palette.colorsEnabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void crossIcon() + { + var crossIconRect = rowRect.SetX(rowsOffsetX + iconsOffsetX + iconSpacing / 2).SetWidth(iconSize).SetHeightFromMid(iconSize); + + SetGUIColor(palette.colorsEnabled ? Color.white : disabledRowTint); + SetLabelAlignmentCenter(); + + GUI.Label(crossIconRect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetGUIColor(); + ResetLabelStyle(); + + } + void color(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + (i + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundPicking() + { + if (!pickingColor) return; + if (i != pickingColorAtIndex) return; + + cellRect.DrawWithRoundedCorners(pickingBackground, 2); + + } + void color() + { + var tint = palette.colorsEnabled ? Color.white : disabledRowTint; + + var brightness = i < VHierarchyPalette.greyColorsCount ? 1.02f : 1.35f; + var outlineColor = i < VHierarchyPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawWithRoundedCorners(outlineColor * tint, 4); + cellRect.Resize(4).DrawWithRoundedCorners(palette.colors[i].SetAlpha(1) * brightness * tint, 3); + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - palette.colors[i].a) * .5f)); + + } + void startPickingColorButton() + { + if (!palette.colorsEnabled) return; + if (!cellRect.IsHovered()) return; + if (pickingColor) return; + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + GUI.Label(cellRect.Resize(.5f), EditorGUIUtility.IconContent("Preset.Context")); + + + if (!clicked) return; + + colorPicker = OpenColorPicker((c) => { palette.RecordUndo(); palette.Dirty(); palette.colors[i] = c; }, palette.colors[i], true, false); + + colorPicker.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingColor = true; + pickingColorAtIndex = i; + + } + void updatePickingColor() + { + if (!pickingColor) return; + + EditorApplication.RepaintHierarchyWindow(); + + } + void stopPickingColor() + { + if (!pickingColor) return; + if (colorPicker) return; + + pickingColor = false; + + } + + + cellRect.MarkInteractive(); + + backgroundPicking(); + color(); + startPickingColorButton(); + updatePickingColor(); + stopPickingColor(); + + } + + backgroundHovered(); + toggle(); + crossIcon(); + + for (int i = 0; i < palette.colors.Count; i++) + color(i); + + Space(rowSpacing - 2); + + } + void icons() + { + void row(Rect rowRect, IconRow row) + { + var isLastRow = row == palette.iconRows.Last(); + var isDraggedRow = row == draggedRow; + var spaceForCrossIcon = 0f; + + + void startPickingIcon(int i, Rect cellRect) + { + iconPicker = OpenObjectPicker(AssetDatabase.LoadAssetAtPath(row.customIcons[i].ToPath()), controlID: 123); + + iconPicker.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingIcon = true; + pickingIconAtIndex = i; + pickingIconAtRow = row; + + } + void updatePickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (EditorGUIUtility.GetObjectPickerControlID() != 123) return; + if (pickingIconAtIndex >= row.customIcons.Count) return; // somehow happens if RecordUndo is used + + palette.RecordUndo(); + palette.Dirty(); + + row.customIcons[pickingIconAtIndex] = (EditorGUIUtility.GetObjectPickerObject() as Texture2D).GetPath().ToGuid(); + + } + void stopPickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (iconPicker) return; + + if (pickingIconAtIndex < row.customIcons.Count) + if (row.customIcons[pickingIconAtIndex] == null) + row.customIcons.RemoveAt(pickingIconAtIndex); + + pickingIcon = false; + + } + void dragndrop() + { + if (!rowRect.IsHovered()) return; + + if (curEvent.isDragUpdate && DragAndDrop.objectReferences.First() is Texture2D) + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + if (!curEvent.isDragPerform) return; + if (!(DragAndDrop.objectReferences.Any(r => r is Texture2D))) return; + + DragAndDrop.AcceptDrag(); + + palette.RecordUndo(); + palette.Dirty(); + + foreach (var icon in DragAndDrop.objectReferences.Where(r => r is Texture2D)) + row.customIcons.Add(icon.GetPath().ToGuid()); + + } + + void calcSpaceForCrossIcon() + { + if (row == curFirstEnabledRow) + spaceForCrossIcon = crossIconAnimationT * cellSize; + + if (row == crossIconAnimationSourceRow) + spaceForCrossIcon = (1 - crossIconAnimationT) * cellSize; + + } + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (pickingIcon) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void backgroundDragged() + { + if (!isDraggedRow) return; + + rowRect.DrawBlurred(Greyscale(0, .3f), 12); + rowRect.Draw(draggedRowBackground); + + } + void toggle() + { + var prevEnabled = row.enabled; + var newEnabled = EditorGUI.Toggle(rowRect.SetWidth(16).MoveX(5), row.enabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + row.enabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void addIconButton() + { + if (!row.isCustom) return; + + var cellRect = rowRect.MoveX(iconsOffsetX + row.customIcons.Count * cellSize + spaceForCrossIcon).SetWidth(cellSize).SetHeightFromMid(cellSize); + + + + SetGUIColor(Greyscale(1, row.enabled ? 1 : .5f)); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + SetGUIColor(Greyscale(1, row.enabled ? 1 : .5f)); + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.Resize(1), EditorGUIUtility.IconContent("Toolbar Plus")); + + ResetLabelStyle(); + ResetGUIColor(); + + + + if (!clicked) return; + + palette.RecordUndo(); + + row.customIcons.Add(null); + + startPickingIcon(row.customIcons.Count - 1, cellRect); + + } + void icon(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + var isCustomIcon = i > row.builtinIcons.Count - 1; + + void backgroundPicking() + { + if (!pickingIcon) return; + if (row != pickingIconAtRow) return; + if (i != pickingIconAtIndex) return; + + cellRect.Resize(1).DrawWithRoundedCorners(pickingBackground, 2); + + } + void drawBuiltin() + { + if (isCustomIcon) return; + + SetLabelAlignmentCenter(); + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent(row.builtinIcons[i])); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void drawCustom() + { + if (!isCustomIcon) return; + if (cellRect.IsHovered()) return; + if (!(AssetDatabase.LoadAssetAtPath(row.customIcons[i - row.builtinIcons.Count].ToPath()) is Texture2D texture)) return; + + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), texture); + + ResetGUIColor(); + + } + void editCustomButton() + { + if (!isCustomIcon) return; + if (!cellRect.IsHovered()) return; + if (pickingIcon) return; + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + GUI.Label(cellRect.Resize(.5f), EditorGUIUtility.IconContent("Preset.Context")); + + + + if (!clicked) return; + + GenericMenu menu = new GenericMenu(); + + menu.AddItem(new GUIContent("Replace icon"), false, () => { palette.RecordUndo(); palette.Dirty(); startPickingIcon(i, cellRect.MoveY(75)); }); + menu.AddItem(new GUIContent("Remove icon"), false, () => { palette.RecordUndo(); row.customIcons.RemoveAt(i); palette.Dirty(); }); + + menu.ShowAsContext(); + + + + } + + cellRect.MarkInteractive(); + + backgroundPicking(); + drawBuiltin(); + drawCustom(); + editCustomButton(); + + } + + + rowRect.MarkInteractive(); + + updatePickingIcon(); + stopPickingIcon(); + dragndrop(); + + calcSpaceForCrossIcon(); + backgroundHovered(); + backgroundDragged(); + toggle(); + addIconButton(); + + for (int i = 0; i < row.iconCount; i++) + icon(i); + + } + + void updateRowsCount() + { + palette.iconRows.RemoveAll(r => r.isEmpty && r != palette.iconRows.Last()); + + if (!palette.iconRows.Last().isEmpty) + palette.iconRows.Add(new IconRow()); + + } + void updateRowGapsCount() + { + while (rowGaps.Count < palette.iconRows.Count) + rowGaps.Add(0); + + while (rowGaps.Count > palette.iconRows.Count) + rowGaps.RemoveLast(); + + } + + void normalRow(int i) + { + Space(rowGaps[i] * (cellSize + rowSpacing)); + + if (i == 0 && lastRect.y != 0) + firstRowY = lastRect.y; + + Space(cellSize + rowSpacing); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, lastRect.y).SetSize(rowWidth, cellSize); + + if (curEvent.isRepaint) + if (rowRect.IsHovered()) + hoveredRow = palette.iconRows[i]; + + + row(rowRect, palette.iconRows[i]); + + } + void draggedRow_() + { + if (!draggingRow) return; + + draggedRowY = (curEvent.mousePosition.y + draggedRowHoldOffset).Clamp(firstRowY, firstRowY + (palette.iconRows.Count - 1) * (cellSize + rowSpacing)); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, draggedRowY).SetSize(rowWidth, cellSize); + + row(rowRect, draggedRow); + + } + void crossIcon() + { + if (!palette.iconRows.Any(r => r.enabled)) return; + + var rect = Rect.zero.SetPos(rowsOffsetX + iconsOffsetX, crossIconY).SetSize(cellSize, cellSize).Resize(iconSpacing / 2); + + SetLabelAlignmentCenter(); + + GUI.Label(rect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + + + updateRowsCount(); + updateRowGapsCount(); + + + if (curEvent.isRepaint) + hoveredRow = null; + + for (int i = 0; i < palette.iconRows.Count; i++) + normalRow(i); + + crossIcon(); + + draggedRow_(); + + + } + void tutor() + { + SetGUIEnabled(false); + + + GUILayout.Label("Click a color to edit it"); + + Space(4); + GUILayout.Label("Click '+' to add a custom icon"); + + Space(4); + GUILayout.Label("Click a custom icon to replace or remove it"); + + Space(4); + GUILayout.Label("Drag rows to reorder them"); + + + ResetGUIEnabled(); + + } + + + Space(15); + colors(); + + Space(15); + icons(); + + Space(25); + tutor(); + + UpdateAnimations(); + + UpdateDragging(); + + palette.Dirty(); + + if (draggingRow || animatingCrossIcon) + Repaint(); + + } + + float iconSize => 18; + float iconSpacing => 2; + float cellSize => iconSize + iconSpacing; + float rowSpacing = 1; + float rowsOffsetX => 14; + float iconsOffsetX => 27; + + Color hoveredRowBackground => Greyscale(isDarkTheme ? 1 : 0, .05f); + Color draggedRowBackground => Greyscale(isDarkTheme ? .3f : .9f); + Color pickingBackground => Greyscale(1, .17f); + Color disabledRowTint => Greyscale(1, .45f); + + float rowWidth => cellSize * Mathf.Max(palette.colors.Count, palette.iconRows.Max(r => r.iconCount + 1)) + 55; + + bool pickingColor; + int pickingColorAtIndex; + EditorWindow colorPicker; + + bool pickingIcon; + int pickingIconAtIndex; + IconRow pickingIconAtRow; + EditorWindow iconPicker; + + IconRow hoveredRow; + + float firstRowY = 51; + + + + + + + void UpdateAnimations() + { + void calcDeltaTime() + { + if (!curEvent.isLayout) return; + + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + + void lerpRowGaps() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = draggingRow ? 12 : 12321; + + for (int i = 0; i < rowGaps.Count; i++) + rowGaps[i] = Lerp(rowGaps[i], draggingRow && i == insertDraggedRowAtIndex ? 1 : 0, lerpSpeed, deltaTime);// todo deltatime + + for (int i = 0; i < rowGaps.Count; i++) + if (rowGaps[i].Approx(0)) + rowGaps[i] = 0; + else if (rowGaps[i].Approx(1)) + rowGaps[i] = 1; + + + } + + void lerpCrossIconAnimationT() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = 12; + + Lerp(ref crossIconAnimationT, 1, lerpSpeed, deltaTime); + + } + void startCrossIconAnimation() + { + if (prevFirstEnabledRow == null) { prevFirstEnabledRow = curFirstEnabledRow; return; } + if (prevFirstEnabledRow == curFirstEnabledRow) return; + + crossIconAnimationT = 0; + crossIconAnimationSourceRow = prevFirstEnabledRow; + + prevFirstEnabledRow = curFirstEnabledRow; + + } + void stopCrossIconAnimation() + { + if (!crossIconAnimationT.Approx(1)) return; + + crossIconAnimationT = 1; + crossIconAnimationSourceRow = null; + + } + void calcCrossIconY() + { + var indexOfFirstEnabled = palette.iconRows.IndexOfFirst(r => r.enabled); + var yOfFirstEnabled = firstRowY + indexOfFirstEnabled * (cellSize + rowSpacing); + for (int i = 0; i < indexOfFirstEnabled + 1; i++) + yOfFirstEnabled += rowGaps[i] * (cellSize + rowSpacing); + + + var indexOfSourceRow = palette.iconRows.IndexOf(crossIconAnimationSourceRow); + var yOfSourceRow = firstRowY + indexOfSourceRow * (cellSize + rowSpacing); + for (int i = 0; i < indexOfSourceRow + 1; i++) + yOfSourceRow += rowGaps[i] * (cellSize + rowSpacing); + + if (crossIconAnimationSourceRow == draggedRow) + yOfSourceRow = draggedRowY; + + + crossIconY = Lerp(yOfSourceRow, yOfFirstEnabled, crossIconAnimationT); + + if (indexOfFirstEnabled == indexOfSourceRow) { crossIconAnimationT = 1; } + + } + + + calcDeltaTime(); + + lerpRowGaps(); + + lerpCrossIconAnimationT(); + startCrossIconAnimation(); + stopCrossIconAnimation(); + calcCrossIconY(); + + } + + List rowGaps = new List(); + + float deltaTime; + double lastLayoutTime; + + float crossIconY = 51; + float crossIconAnimationT = 1; + IconRow crossIconAnimationSourceRow; + bool animatingCrossIcon => crossIconAnimationT != 1; + + IconRow prevFirstEnabledRow; + IconRow curFirstEnabledRow => palette.iconRows.FirstOrDefault(r => r.enabled); + + + + + + + void UpdateDragging() + { + void startDragging() + { + if (draggingRow) return; + if (!curEvent.isMouseDrag) return; + if (hoveredRow == null) return; + if (hoveredRow == palette.iconRows.Last()) return; + + palette.RecordUndo(); + + draggingRow = true; + draggedRow = hoveredRow; + draggingRowFromIndex = palette.iconRows.IndexOf(hoveredRow); + draggedRowHoldOffset = firstRowY + draggingRowFromIndex * (cellSize + rowSpacing) - curEvent.mousePosition.y; + + palette.iconRows.Remove(hoveredRow); + rowGaps[draggingRowFromIndex] = 1; + + } + void updateDragging() + { + if (!draggingRow) return; + + insertDraggedRowAtIndex = ((curEvent.mousePosition.y - firstRowY) / (cellSize + rowSpacing)).FloorToInt().Clamp(0, palette.iconRows.Count - 1); + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!draggingRow) return; + if (!curEvent.isMouseUp) return; + + palette.RecordUndo(); + palette.Dirty(); + + palette.iconRows.AddAt(draggedRow, insertDraggedRowAtIndex); + + rowGaps[insertDraggedRowAtIndex] = 0; + + draggingRow = false; + draggedRow = null; + + EditorGUIUtility.hotControl = 0; + + } + + + startDragging(); + updateDragging(); + stopDragging(); + + } + + IconRow draggedRow; + bool draggingRow; + int draggingRowFromIndex; + float draggedRowHoldOffset; + float draggedRowY; + int insertDraggedRowAtIndex; + + + + + + + VHierarchyPalette palette => target as VHierarchyPalette; + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs.meta new file mode 100644 index 00000000..a1fcf368 --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPaletteEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b7dce55e9e9b476fb5d1d669c006123 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs b/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs new file mode 100644 index 00000000..d45ecdec --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs @@ -0,0 +1,550 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyPalette; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + + +namespace VHierarchy +{ + public class VHierarchyPaletteWindow : EditorWindow + { + void OnGUI() + { + if (!palette) { Close(); return; } + + int hoveredColorIndex = -1; + string hoveredIconNameOrGuid = null; + + void background() + { + position.SetPos(0, 0).Draw(windowBackground); + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + void colors() + { + if (!palette.colorsEnabled) { Space(-spaceAfterColors); return; } + + var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); + + void color(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundSelected() + { + if (!initialColorIndexes.Contains(i)) return; + + cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (i != 0) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + void color() + { + if (i == 0) return; + + var brightness = i <= VHierarchyPalette.greyColorsCount ? 1.02f : 1.35f; + var outlineColor = i <= VHierarchyPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawWithRoundedCorners(outlineColor, 4); + cellRect.Resize(4).DrawWithRoundedCorners((palette.colors[i - 1] * brightness).SetAlpha(1), 3); + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha((1 - palette.colors[i - 1].a) * .45f)); + + } + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredColorIndex = i; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseDown) return; + + Close(); + + } + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + color(); + setHovered(); + closeOnClick(); + + } + + + for (int i = 0; i < palette.colors.Count + 1; i++) + color(i); + + } + void icons() + { + void row(IconRow iconRow) + { + if (!iconRow.enabled) return; + if (iconRow.isEmpty) return; + + var rowRect = ExpandWidthLabelRect(height: cellSize).SetX(paddingX); + var isFirstEnabledRow = palette.iconRows.First(r => r.enabled) == iconRow; + + void icon(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var isCrossIcon = isFirstEnabledRow && i == 0; + var actualIconIndex = isFirstEnabledRow ? i - 1 : i; + var isBuiltinIcon = !isCrossIcon && actualIconIndex < iconRow.builtinIcons.Count; + var isCustomIcon = !isCrossIcon && actualIconIndex >= iconRow.builtinIcons.Count; + var iconNameOrGuid = isCrossIcon ? "" : isCustomIcon ? iconRow.customIcons[actualIconIndex - iconRow.builtinIcons.Count] : iconRow.builtinIcons[actualIconIndex]; + + void backgroundSelected() + { + if (!initialIconNamesOrGuids.Contains(iconNameOrGuid)) return; + + cellRect.Resize(1).DrawWithRoundedCorners(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawWithRoundedCorners(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (!isCrossIcon) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent("CrossIcon")); + + ResetLabelStyle(); + + } + void builtinIcon() + { + if (!isBuiltinIcon) return; + + SetLabelAlignmentCenter(); + + GUI.Label(cellRect.SetSizeFromMid(iconSize), EditorGUIUtility.IconContent(iconNameOrGuid)); + + ResetLabelStyle(); + + } + void customIcon() + { + if (!isCustomIcon) return; + + var texture = AssetDatabase.LoadAssetAtPath(iconNameOrGuid.ToPath()); + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), texture ?? Texture2D.blackTexture); + + } + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredIconNameOrGuid = iconNameOrGuid; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseDown) return; + + Close(); + + } + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + builtinIcon(); + customIcon(); + setHovered(); + closeOnClick(); + + } + + for (int i = 0; i < iconRow.iconCount + (isFirstEnabledRow ? 1 : 0); i++) + icon(i); + + Space(rowSpacing - 2); + + } + + for (int i = 0; i < palette.iconRows.Count; i++) + row(palette.iconRows[i]); + + } + + void setColorsAndIcons() + { + if (!curEvent.isRepaint) return; + + + if (palette.iconRows.Any(r => r.enabled)) + if (hoveredIconNameOrGuid != null) + SetIcon(hoveredIconNameOrGuid); + else + SetInitialIcons(); + + + if (palette.colorsEnabled) + if (hoveredColorIndex != -1) + SetColor(hoveredColorIndex); + else + SetInitialColors(); + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + var speed = 9; + + SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + position = position.SetPos(currentPosition); + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + if (!currentPosition.y.Approx(targetPosition.y)) + Repaint(); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + SetInitialColors(); + SetInitialIcons(); + + Close(); + + } + + + RecordUndoOnDatas(); + + background(); + outline(); + + Space(paddingY); + colors(); + + Space(spaceAfterColors); + icons(); + + setColorsAndIcons(); + updatePosition(); + closeOnEscape(); + + EditorApplication.RepaintHierarchyWindow(); + + } + + static float iconSize => 18; + static float iconSpacing => 2; + static float cellSize => iconSize + iconSpacing; + static float spaceAfterColors => 11; + public float rowSpacing = 1; + static float paddingX => 12; + static float paddingY => 12; + + Color windowBackground => isDarkTheme ? Greyscale(.23f) : Greyscale(.7f); + Color selectedBackground => isDarkTheme ? new Color(.3f, .5f, .7f, .8f) : new Color(.3f, .5f, .7f, .4f) * 1.35f; + Color hoveredBackground = Greyscale(1, .3f); + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + + + + + + void SetIcon(string iconNameOrGuid) + { + foreach (var r in goDatas) + r.iconNameOrGuid = iconNameOrGuid; + } + void SetColor(int colorIndex) + { + foreach (var r in goDatas) + r.colorIndex = colorIndex; + } + + void SetInitialIcons() + { + for (int i = 0; i < goDatas.Count; i++) + goDatas[i].iconNameOrGuid = initialIconNamesOrGuids[i]; + + } + void SetInitialColors() + { + for (int i = 0; i < goDatas.Count; i++) + goDatas[i].colorIndex = initialColorIndexes[i]; + + } + + void RemoveEmptyGoDatas() + { + var toRemove = goDatas.Where(r => r.iconNameOrGuid == "" && r.colorIndex == 0); + + foreach (var goData in toRemove) + goData.sceneData.goDatas_byGlobalId.RemoveValue(goData); + + if (toRemove.Any()) + Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); + + } + + void RecordUndoOnDatas() + { + if (usingDataSO) + if (data) + data.RecordUndo(); + + foreach (var r in usedDataComponents) + r.RecordUndo(); + + } + void MarkDatasDirty() + { + if (usingDataSO) + if (data) + data.Dirty(); + + foreach (var r in usedDataComponents) + r.Dirty(); + } + void SaveData() + { + if (usingDataSO) + data.Save(); + } + + bool usingDataSO => !gameObjects.Select(r => r.scene).All(r => VHierarchy.dataComponents_byScene.GetValueOrDefault(r) != null); + IEnumerable usedDataComponents => VHierarchy.dataComponents_byScene.Where(kvp => kvp.Value && gameObjects.Select(r => r.scene).Contains(kvp.Key)).Select(kvp => kvp.Value); + + + + + + + + void OnLostFocus() + { + if (curEvent.holdingAlt && EditorWindow.focusedWindow?.GetType().Name == "SceneHierarchyWindow") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + + + + + + + public void Init(List gameObjects) + { + void createData() + { + if (VHierarchy.data) return; + + VHierarchy.data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VHierarchy.data, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Data.asset")); + + } + void createPalette() + { + if (VHierarchy.palette) return; + + VHierarchy.palette = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VHierarchy.palette, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Palette.asset")); + + } + void setSize() + { + var rowCellCounts = new List(); + + if (palette.colorsEnabled) + rowCellCounts.Add(palette.colors.Count + 1); + + foreach (var r in palette.iconRows.Where(r => r.enabled)) + rowCellCounts.Add(r.iconCount + (r == palette.iconRows.First(r => r.enabled) ? 1 : 0)); + + var width = rowCellCounts.Max() * cellSize + paddingX * 2; + + + + var iconRowCount = palette.iconRows.Count(r => r.enabled && !r.isEmpty); + var rowCount = iconRowCount + (palette.colorsEnabled ? 1 : 0); + + var height = rowCount * (cellSize + rowSpacing) + (palette.colorsEnabled && palette.iconRows.Any(r => r.enabled && !r.isEmpty) ? spaceAfterColors : 0) + paddingY * 2; + + + + position = position.SetSize(width, height).SetPos(targetPosition); + + } + void getDatas() + { + goDatas.Clear(); + + foreach (var r in gameObjects) + goDatas.Add(VHierarchy.GetGameObjectData(r, createDataIfDoesntExist: true)); + + } + void getInitColorsAndIcons() + { + initialColorIndexes.Clear(); + initialIconNamesOrGuids.Clear(); + + foreach (var r in goDatas) + initialColorIndexes.Add(r.colorIndex); + + foreach (var r in goDatas) + initialIconNamesOrGuids.Add(r.iconNameOrGuid); + + } + + + this.gameObjects = gameObjects; + + RecordUndoOnDatas(); + + createData(); + createPalette(); + setSize(); + getDatas(); + getInitColorsAndIcons(); + + } + + void OnDestroy() + { + RemoveEmptyGoDatas(); + MarkDatasDirty(); + SaveData(); + + } + + public List gameObjects = new List(); + public List goDatas = new List(); + + public List initialColorIndexes = new List(); + public List initialIconNamesOrGuids = new List(); + + static VHierarchyPalette palette => VHierarchy.palette; + static VHierarchyData data => VHierarchy.data; + + + + + + + public static void CreateInstance(Vector2 position) + { + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + + instance.position = instance.position.SetPos(position).SetSize(200, 300); + instance.targetPosition = position; + + } + + public static VHierarchyPaletteWindow instance; + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs.meta b/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs.meta new file mode 100644 index 00000000..c978574c --- /dev/null +++ b/Assets/Third Parties/vHierarchy/VHierarchyPaletteWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ae240588f29744208e627125db9c9e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs.meta b/Assets/Third Parties/vTabs.meta new file mode 100644 index 00000000..b21a85b8 --- /dev/null +++ b/Assets/Third Parties/vTabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98436d729d10ab240aea4ae08fcedb45 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/Manual.pdf b/Assets/Third Parties/vTabs/Manual.pdf new file mode 100644 index 00000000..24beae79 Binary files /dev/null and b/Assets/Third Parties/vTabs/Manual.pdf differ diff --git a/Assets/Third Parties/vTabs/Manual.pdf.meta b/Assets/Third Parties/vTabs/Manual.pdf.meta new file mode 100644 index 00000000..cb55b072 --- /dev/null +++ b/Assets/Third Parties/vTabs/Manual.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 67456a8acc76240a8a8ad9986c0a6a10 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/VTabs.asmdef b/Assets/Third Parties/vTabs/VTabs.asmdef new file mode 100644 index 00000000..5a2a8d35 --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabs.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VTabs", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Third Parties/vTabs/VTabs.asmdef.meta b/Assets/Third Parties/vTabs/VTabs.asmdef.meta new file mode 100644 index 00000000..79f05369 --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabs.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 06ab1c341392b4a318f67b84e0da6aab +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/VTabs.cs b/Assets/Third Parties/vTabs/VTabs.cs new file mode 100644 index 00000000..c22cf90a --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabs.cs @@ -0,0 +1,1800 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using System.Diagnostics; +using Type = System.Type; +using Delegate = System.Delegate; +using Action = System.Action; +using static VTabs.Libs.VUtils; +using static VTabs.Libs.VGUI; + + + + + +namespace VTabs +{ + public static class VTabs + { + static void Update() + { + var lastEvent = typeof(Event).GetFieldValue("s_Current"); + + + void scrollInteractions() + { + if (isKeyPressed) { sidesscrollPosition = 0; return; } + if (lastEvent.delta == Vector2.zero) return; + if (lastEvent.type == EventType.MouseMove) { sidesscrollPosition = 0; return; } + if (lastEvent.type == EventType.MouseDrag) { sidesscrollPosition = 0; return; } + if (lastEvent.type != EventType.ScrollWheel && delayedMousePosition_screenSpace != EditorGUIUtility.GUIToScreenPoint(lastEvent.mousePosition)) return; // uncaptured mouse move/drag check + if (lastEvent.type != EventType.ScrollWheel && Application.platform == RuntimePlatform.OSXEditor && lastEvent.delta.x == (int)lastEvent.delta.x) return; // osx uncaptured mouse move/drag in sceneview ang gameview workaround + + + void switchTab(int dir) + { + if (!VTabsMenu.switchTabsEnabled) return; + if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return; + if (!hoveredWindow.docked) return; + if (hoveredWindow.maximized) return; + + + var tabs = GetTabList(hoveredWindow); + + var i0 = tabs.IndexOf(hoveredWindow); + var i1 = Mathf.Clamp(i0 + dir, 0, tabs.Count - 1); + + tabs[i1].Focus(); + + UpdateTitle(tabs[i1]); + + } + void moveTab(int dir) + { + if (!VTabsMenu.moveTabsEnabled) return; + if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return; + + + var tabs = GetTabList(hoveredWindow); + + var i0 = tabs.IndexOf(hoveredWindow); + var i1 = Mathf.Clamp(i0 + dir, 0, tabs.Count - 1); + + var r = tabs[i0]; + tabs[i0] = tabs[i1]; + tabs[i1] = r; + tabs[i1].Focus(); + + } + + void shiftscroll() + { + if (!lastEvent.shift) return; + + + var scrollDelta = Application.platform == RuntimePlatform.OSXEditor ? lastEvent.delta.x // osx sends delta.y as delta.x when shift is pressed + : lastEvent.delta.x - lastEvent.delta.y; // some software on windows (eg logitech options) may do that too + if (VTabsMenu.reverseScrollDirectionEnabled) + scrollDelta *= -1; + + + if (scrollDelta != 0) + if (lastEvent.control || lastEvent.command) + moveTab(scrollDelta > 0 ? 1 : -1); + else + switchTab(scrollDelta > 0 ? 1 : -1); + + } + void sidescroll() + { + if (lastEvent.shift) return; + if (lastEvent.delta.x == 0) return; + if (lastEvent.delta.x.Abs() <= 0.06f) return; + if (lastEvent.delta.x.Abs() * 1.1f < lastEvent.delta.y.Abs()) { sidesscrollPosition = 0; return; } + if (!VTabsMenu.sidescrollEnabled) return; + + + var dampenK = 5; // the larger this k is - the smaller big deltas are, and the less is sidescroll's dependency on scroll speed + var a = lastEvent.delta.x.Abs() * dampenK; + var deltaDampened = (a < 1 ? a : Mathf.Log(a) + 1) / dampenK * lastEvent.delta.x.Sign(); + + var sensitivityK = .22f; + var sidescrollDelta = deltaDampened * VTabsMenu.sidescrollSensitivity * sensitivityK; + + if (VTabsMenu.reverseScrollDirectionEnabled) + sidescrollDelta *= -1; + + if (sidesscrollPosition.RoundToInt() != (sidesscrollPosition += sidescrollDelta).RoundToInt()) + if (lastEvent.control || lastEvent.command) + moveTab(sidescrollDelta < 0 ? 1 : -1); + else + switchTab(sidescrollDelta < 0 ? 1 : -1); + + } + + + shiftscroll(); + sidescroll(); + + } + void scrollAnimation() + { + if (!EditorWindow.focusedWindow) return; + if (!EditorWindow.focusedWindow.docked) return; + if (EditorWindow.focusedWindow.maximized) return; + + + var dockArea = EditorWindow.focusedWindow.GetMemberValue("m_Parent"); + + if (dockArea.GetType() != t_DockArea) return; // happens on 2021.1.28 + + + var curScroll = dockArea.GetFieldValue("m_ScrollOffset"); + + if (!curScroll.Approx(0)) + curScroll -= nonZeroTabScrollOffset; + + if (curScroll == 0 && prevFocusedDockArea == dockArea) + curScroll = prevScroll; + + + + + var targScroll = GetOptimalTabScrollerPosition(EditorWindow.focusedWindow); + + var animationSpeed = 7f; + + var deltaTime = (EditorApplication.timeSinceStartup - prevTime).ToFloat().Min(.03f); + + var newScroll = SmoothDamp(curScroll, targScroll, animationSpeed, ref scrollDeriv, deltaTime); + + if (newScroll < .5f) + newScroll = 0; + + + + + prevScroll = newScroll; + prevFocusedDockArea = dockArea; + prevTime = EditorApplication.timeSinceStartup; + + + + + if (newScroll.Approx(curScroll)) return; + + if (!newScroll.Approx(0)) + newScroll += nonZeroTabScrollOffset; + + dockArea.SetFieldValue("m_ScrollOffset", newScroll); + + EditorWindow.focusedWindow.Repaint(); + + } + void createWindowDelayed() + { + if (createWindowDelayedAction == null) return; + if ((System.DateTime.UtcNow - lastDragndropTime).TotalSeconds < .05f) return; + + createWindowDelayedAction.Invoke(); + createWindowDelayedAction = null; + + } + void dragndrop() + { + if (!VTabsMenu.dragndropEnabled) return; + if (lastEvent.type != EventType.DragUpdated && lastEvent.type != EventType.DragPerform) return; + if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return; + if (!hoveredWindow.position.SetPos(0, 0).SetHeight(hoveredWindow.GetType() == t_SceneHierarchyWindow ? 5 : 40).Contains(lastEvent.mousePosition)) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + + + if (lastEvent.type != EventType.DragPerform) return; + if (lastDragndropPosition == curEvent.mousePosition) return; + + DragAndDrop.AcceptDrag(); + + var lockToObject = DragAndDrop.objectReferences.First(); + var dockArea = hoveredWindow.GetMemberValue("m_Parent"); + + createWindowDelayedAction = () => new TabInfo(lockToObject).CreateWindow(dockArea, false); // not creating window right away to avoid scroll animation stutter + + lastDragndropPosition = curEvent.mousePosition; + lastDragndropTime = System.DateTime.UtcNow; + + } + + + scrollInteractions(); + scrollAnimation(); + createWindowDelayed(); + dragndrop(); + + CheckIfFocusedWindowChanged(); + CheckIfWindowWasUnmaximized(); + + } + + static float sidesscrollPosition; + + static float scrollDeriv; + static float prevScroll; + static object prevFocusedDockArea; + + static double deltaTime; + static double prevTime = 0; + + static Vector2 lastDragndropPosition; + static System.DateTime lastDragndropTime; + static Action createWindowDelayedAction; + + + + + + + + + static void CheckShortcuts() // globalEventHandler + { + void set_isKeyPressed() + { + if (curEvent.keyCode == KeyCode.LeftShift) return; + if (curEvent.keyCode == KeyCode.LeftControl) return; + if (curEvent.keyCode == KeyCode.LeftCommand) return; + if (curEvent.keyCode == KeyCode.RightShift) return; + if (curEvent.keyCode == KeyCode.RightControl) return; + if (curEvent.keyCode == KeyCode.RightCommand) return; + + if (Event.current.type == EventType.KeyDown) + isKeyPressed = true; + + if (Event.current.type == EventType.KeyUp) + isKeyPressed = false; + + } + + void addTab() + { + if (!curEvent.isKeyDown) return; + if (!curEvent.holdingCmdOnly && !curEvent.holdingCtrlOnly) return; + if (curEvent.keyCode != KeyCode.T) return; + + if (!EditorWindow.mouseOverWindow) return; + if (!VTabsMenu.addTabEnabled) return; + + + curEvent.Use(); + + addTabMenu_openedOverWindow = EditorWindow.mouseOverWindow; + + + + List defaultTabList; + List customTabList; + + var customTabListKey = "vTabs-CustomTabList-" + GetProjectId(); + + void loadDefaultTabList() + { + defaultTabList = new List(); + + defaultTabList.Add(new TabInfo("SceneView", "Scene")); + defaultTabList.Add(new TabInfo("GameView", "Game")); + defaultTabList.Add(new TabInfo("ProjectBrowser", "Project")); + defaultTabList.Add(new TabInfo("InspectorWindow", "Inspector")); + defaultTabList.Add(new TabInfo("ConsoleWindow", "Console")); + defaultTabList.Add(new TabInfo("ProfilerWindow", "Profiler")); + // defaultTabList.Add(new TabInfo("LightingWindow", "Lighting")); + // defaultTabList.Add(new TabInfo("ProjectSettingsWindow", "Project Settings")); + + } + void loadSavedTabList() + { + var json = EditorPrefs.GetString(customTabListKey); + + customTabList = JsonUtility.FromJson(json)?.list ?? new List(); + + } + void saveSavedTabsList() + { + var json = JsonUtility.ToJson(new TabInfoList { list = customTabList }); + + EditorPrefs.SetString(customTabListKey, json); + + } + + loadDefaultTabList(); + loadSavedTabList(); + + + + + GenericMenu menu = new GenericMenu(); + Vector2 menuPosition; + + void rememberClickPosition() + { + addTabMenu_lastClickPosition_screenSpace = curEvent.mousePosition_screenSpace; + } + void set_menuPosition() + { + if (curEvent.mousePosition_screenSpace.DistanceTo(addTabMenu_lastClickPosition_screenSpace) < 2) + menuPosition = EditorGUIUtility.ScreenToGUIPoint(addTabMenu_lastOpenPosition_screenSpace); + else + menuPosition = curEvent.mousePosition - Vector2.up * 9; + + addTabMenu_lastOpenPosition_screenSpace = EditorGUIUtility.GUIToScreenPoint(menuPosition); + +#if !UNITY_2021_2_OR_NEWER + if (EditorWindow.focusedWindow) + menuPosition += EditorWindow.focusedWindow.position.position; +#endif + } + + void defaultTabs() + { + menu.AddDisabledItem(new GUIContent("Default tabs")); + + foreach (var tabInfo in defaultTabList) + menu.AddItem(new GUIContent(tabInfo.menuItemName), false, () => + { + tabInfo.CreateWindow(GetDockArea(addTabMenu_openedOverWindow), false); + EditorApplication.delayCall += rememberClickPosition; + + }); + + } + void savedTabs() + { + if (!customTabList.Any()) return; + + menu.AddSeparator(""); + + menu.AddDisabledItem(new GUIContent("Saved tabs")); + + + foreach (var tabInfo in customTabList) + if (tabInfo.isPropertyEditor && !tabInfo.globalId.GetObject()) + menu.AddDisabledItem(new GUIContent(tabInfo.menuItemName)); + else + menu.AddItem(new GUIContent(tabInfo.menuItemName), false, () => + { + tabInfo.CreateWindow(GetDockArea(addTabMenu_openedOverWindow), false); + EditorApplication.delayCall += rememberClickPosition; + + }); + + } + void remove() + { + if (!customTabList.Any()) return; + + + foreach (var tabInfo in customTabList) + menu.AddItem(new GUIContent("Remove/" + tabInfo.menuItemName), false, () => + { + customTabList.Remove(tabInfo); + saveSavedTabsList(); + EditorApplication.delayCall += rememberClickPosition; + + }); + + + menu.AddSeparator("Remove/"); + + menu.AddItem(new GUIContent("Remove/Remove all"), false, () => + { + customTabList.Clear(); + saveSavedTabsList(); + EditorApplication.delayCall += rememberClickPosition; + + }); + + } + void saveCurrentTab() + { + var menuItemName = addTabMenu_openedOverWindow.titleContent.text.Replace("/", " \u2215 ").Trim(' '); + + if (defaultTabList.Any(r => r.menuItemName == menuItemName)) return; + if (customTabList.Any(r => r.menuItemName == menuItemName)) return; + + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Save current tab"), false, () => + { + customTabList.Add(new TabInfo(addTabMenu_openedOverWindow)); + saveSavedTabsList(); + EditorApplication.delayCall += rememberClickPosition; + + }); + + } + + + set_menuPosition(); + + defaultTabs(); + savedTabs(); + remove(); + saveCurrentTab(); + + menu.DropDown(Rect.zero.SetPos(menuPosition)); + + } + void closeTab() + { + if (!curEvent.isKeyDown) return; + if (!curEvent.holdingCmdOnly && !curEvent.holdingCtrlOnly) return; + if (curEvent.keyCode != KeyCode.W) return; + + if (!VTabsMenu.closeTabEnabled) return; + if (!EditorWindow.mouseOverWindow) return; + if (!EditorWindow.mouseOverWindow.docked) return; + if (GetTabList(EditorWindow.mouseOverWindow).Count <= 1) return; + + + Event.current.Use(); + + tabInfosForReopening.Push(new TabInfo(EditorWindow.mouseOverWindow)); + + EditorWindow.mouseOverWindow.Close(); + + } + void reopenTab() + { + if (!curEvent.isKeyDown) return; + + if (curEvent.modifiers != (EventModifiers.Command | EventModifiers.Shift) + && curEvent.modifiers != (EventModifiers.Control | EventModifiers.Shift)) return; + + if (curEvent.keyCode != KeyCode.T) return; + + if (!EditorWindow.mouseOverWindow) return; + if (!VTabsMenu.reopenTabEnabled) return; + if (!tabInfosForReopening.Any()) return; + + + Event.current.Use(); + + var window = tabInfosForReopening.Pop().CreateWindow(); + + UpdateTitle(window); + + } + + + set_isKeyPressed(); + + addTab(); + closeTab(); + reopenTab(); + + } + + static Vector2 addTabMenu_lastClickPosition_screenSpace; + static Vector2 addTabMenu_lastOpenPosition_screenSpace; + static EditorWindow addTabMenu_openedOverWindow; + + static bool isKeyPressed; + + static Stack tabInfosForReopening = new Stack(); + + + + + + + [System.Serializable] + class TabInfo + { + public EditorWindow CreateWindow(object dockArea = null, bool atOriginalTabIndex = true) + { + if (dockArea == null) dockArea = originalDockArea; + if (dockArea == null) return null; + if (dockArea.GetType() != t_DockArea) return null; // happens in 2023.2, no idea why + + var lastInteractedBrowser = t_ProjectBrowser.GetFieldValue("s_LastInteractedProjectBrowser"); // changes on new browser creation // tomove to seutup + + var window = (EditorWindow)ScriptableObject.CreateInstance(typeName); + + void notifyVFavorites() + { + mi_VFavorites_BeforeWindowCreated?.Invoke(null, new object[] { dockArea }); + } + void addToDockArea() + { + if (atOriginalTabIndex) + dockArea.InvokeMethod("AddTab", originalTabIndex, window, true); + else + dockArea.InvokeMethod("AddTab", window, true); + + } + + void setupBrowser() + { + if (!isBrowser) return; + + + void setSavedGridSize() + { + if (!isGridSizeSaved) return; + + window.GetFieldValue("m_ListArea")?.SetMemberValue("gridSize", savedGridSize); + + } + void setLastUsedGridSize() + { + if (isGridSizeSaved) return; + if (lastInteractedBrowser == null) return; + + var listAreaSource = lastInteractedBrowser.GetFieldValue("m_ListArea"); + var listAreaDest = window.GetFieldValue("m_ListArea"); + + if (listAreaSource != null && listAreaDest != null) + listAreaDest.SetPropertyValue("gridSize", listAreaSource.GetPropertyValue("gridSize")); + + } + + void setSavedLayout() + { + if (!isLayoutSaved) return; + + var layoutEnum = System.Enum.ToObject(t_ProjectBrowser.GetField("m_ViewMode", maxBindingFlags).FieldType, savedLayout); + + window.InvokeMethod("SetViewMode", layoutEnum); + + } + void setLastUsedLayout() + { + if (isLayoutSaved) return; + if (lastInteractedBrowser == null) return; + + window.InvokeMethod("SetViewMode", lastInteractedBrowser.GetMemberValue("m_ViewMode")); + + } + + void setLastUsedListWidth() + { + if (lastInteractedBrowser == null) return; + + window.SetFieldValue("m_DirectoriesAreaWidth", lastInteractedBrowser.GetFieldValue("m_DirectoriesAreaWidth")); + + } + + void lockToFolder_twoColumns() + { + if (!isLocked) return; + if (window.GetMemberValue("m_ViewMode") != 1) return; + if (folderGuid.IsNullOrEmpty()) return; + + + var iid = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(folderGuid)).GetInstanceID(); + + window.GetFieldValue("m_ListAreaState").SetFieldValue("m_SelectedInstanceIDs", new List { iid }); + + t_ProjectBrowser.InvokeMethod("OpenSelectedFolders"); + + + window.SetPropertyValue("isLocked", true); + + } + void lockToFolder_oneColumn() + { + if (!isLocked) return; + if (window.GetMemberValue("m_ViewMode") != 0) return; + if (folderGuid.IsNullOrEmpty()) return; + + if (!(window.GetMemberValue("m_AssetTree") is object m_AssetTree)) return; + if (!(m_AssetTree.GetMemberValue("data") is object data)) return; + + + var folderPath = folderGuid.ToPath(); + var folderIid = AssetDatabase.LoadAssetAtPath(folderPath).GetInstanceID(); + + data.SetMemberValue("m_rootInstanceID", folderIid); + + m_AssetTree.InvokeMethod("ReloadData"); + + window.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { folderPath }); + + + window.SetPropertyValue("isLocked", true); + + } + + + window.InvokeMethod("Init"); + + setSavedGridSize(); + setLastUsedGridSize(); + + setSavedLayout(); + setLastUsedLayout(); + + setLastUsedListWidth(); + + lockToFolder_twoColumns(); + lockToFolder_oneColumn(); + + UpdateBrowserTitle(window); + + } + void setupPropertyEditor() + { + if (!isPropertyEditor) return; + if (globalId.isNull) return; + + + var lockTo = globalId.GetObject(); + + if (lockedPrefabAssetObject) + lockTo = lockedPrefabAssetObject; // globalId api doesn't work for prefab asset objects, so we use direct object reference in such cases + + if (!lockTo) return; + + + window.GetMemberValue("tracker").InvokeMethod("SetObjectsLockedByThisTracker", (new List { lockTo })); + + window.SetMemberValue("m_GlobalObjectId", globalId.ToString()); + window.SetMemberValue("m_InspectedObject", lockTo); + + UpdatePropertyEditorTitle(window); + + } + + void setCustomEditorWindowTitle() + { + if (window.titleContent.text != window.GetType().FullName) return; + if (originalTitle.IsNullOrEmpty()) return; + + window.titleContent.text = originalTitle; + + // custom EditorWindows often have their titles set in EditorWindow.GetWindow + // and when such windows are created via ScriptableObject.CreateInstance, their titles default to window type name + // so we have to set original window title in such cases + + } + + + notifyVFavorites(); + addToDockArea(); + + setupBrowser(); + setupPropertyEditor(); + + setCustomEditorWindowTitle(); + + + window.Focus(); + + + + return window; + } + + + + public TabInfo(EditorWindow window) + { + typeName = window.GetType().Name; + originalDockArea = GetDockArea(window); + originalTabIndex = GetTabList(window).IndexOf(window); + originalTitle = window.titleContent.text; + menuItemName = window.titleContent.text.Replace("/", " \u2215 ").Trim(' '); + + if (isBrowser) + { + isLocked = window.GetPropertyValue("isLocked"); + + + savedGridSize = window.GetFieldValue("m_StartGridSize"); + + isGridSizeSaved = true; + + + savedLayout = window.GetMemberValue("m_ViewMode"); + + isLayoutSaved = true; + + + var folderPath = savedLayout == 0 ? window.GetMemberValue("m_SearchFilter")?.GetMemberValue("m_Folders")?.FirstOrDefault() ?? "Assets" // one column + : window.InvokeMethod("GetActiveFolderPath"); // two columns + folderGuid = folderPath.ToGuid(); + + } + + if (isPropertyEditor) + globalId = new GlobalID(window.GetMemberValue("m_GlobalObjectId")); + + } + public TabInfo(Object lockTo) + { + isLocked = true; + typeName = lockTo is DefaultAsset ? t_ProjectBrowser.Name : t_PropertyEditor.Name; + + + if (isBrowser) + folderGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(lockTo)); + + if (isPropertyEditor) + globalId = lockTo.GetGlobalID(); + +#if UNITY_2021_2_OR_NEWER + if (isPropertyEditor) + if (StageUtility.GetCurrentStage() is PrefabStage && globalId.ToString().Contains("00000000000000000000000000000000")) + lockedPrefabAssetObject = lockTo; +#endif + + } + public TabInfo(string typeName, string menuItemName) { this.typeName = typeName; this.menuItemName = menuItemName; } + + public string typeName; + public string menuItemName; + public object originalDockArea; + public int originalTabIndex; + public string originalTitle; + + public bool isBrowser => typeName == t_ProjectBrowser.Name; + public bool isLocked; + public string folderGuid = ""; + public int savedGridSize; + public int savedLayout; + public bool isGridSizeSaved = false; + public bool isLayoutSaved = false; + + public bool isPropertyEditor => typeName == t_PropertyEditor.Name; + public GlobalID globalId; + public Object lockedPrefabAssetObject; + + } + + [System.Serializable] + class TabInfoList { public List list = new List(); } + + + + + + + + + + + + static void UpdateTitle(EditorWindow window) + { + if (window == null) return; + + var isPropertyEditor = window.GetType() == t_PropertyEditor; + var isBrowser = window.GetType() == t_ProjectBrowser; + + if (!isPropertyEditor && !isBrowser) return; + + + if (isPropertyEditor) + UpdatePropertyEditorTitle(window); + + if (isBrowser) + if (window.GetPropertyValue("isLocked")) + UpdateBrowserTitle(window); + + } + + static void UpdateBrowserTitle(EditorWindow browser) + { + if (mi_VFavorites_CanBrowserBeWrapped != null && mi_VFavorites_CanBrowserBeWrapped.Invoke(null, new[] { browser }).Equals(false)) return; + + var isLocked = browser.GetMemberValue("isLocked"); + var isTitleDefault = browser.titleContent.text == "Project"; + + void setLockedTitle() + { + if (!isLocked) return; + + var isOneColumn = browser.GetMemberValue("m_ViewMode") == 0; + + var path = isOneColumn ? browser.GetMemberValue("m_SearchFilter")?.GetMemberValue("m_Folders")?.FirstOrDefault() ?? "Assets" : browser.InvokeMethod("GetActiveFolderPath"); + var guid = path.ToGuid(); + + var name = path.GetFilename(); + var icon = EditorGUIUtility.FindTexture("Project"); + + + void getIconFromVFolders() + { + if (mi_VFolders_GetIcon == null) return; + + if (mi_VFolders_GetIcon.Invoke(null, new[] { guid }) is Texture2D iconFromVFolders) + icon = iconFromVFolders; + + } + + getIconFromVFolders(); + + + browser.titleContent = new GUIContent(name, icon); + + t_DockArea.GetFieldValue("s_GUIContents").Clear(); + + } + void setDefaultTitle() + { + if (isLocked) return; + if (isTitleDefault) return; + + var name = "Project"; + var icon = EditorGUIUtility.FindTexture("Project@2x"); + + browser.titleContent = new GUIContent(name, icon); + + t_DockArea.GetFieldValue("s_GUIContents").Clear(); + + } + + setLockedTitle(); + setDefaultTitle(); + + } + static void UpdateBrowserTitles() => allBrowsers.ToList().ForEach(r => UpdateBrowserTitle(r)); + + static void UpdatePropertyEditorTitle(EditorWindow propertyEditor) + { + var obj = propertyEditor.GetMemberValue("m_InspectedObject"); + + if (!obj) return; + + + var name = obj is Component component ? GetComponentName(component) : obj.name; + var sourceIcon = AssetPreview.GetMiniThumbnail(obj); + var adjustedIcon = sourceIcon; + + + void getSourceIconFromVHierarchy() + { + if (mi_VHierarchy_GetIcon == null) return; + if (!(obj is GameObject gameObject)) return; + + if (mi_VHierarchy_GetIcon.Invoke(null, new[] { gameObject }) is Texture2D iconFromVHierarchy) + sourceIcon = iconFromVHierarchy; + + } + void getAdjustedIcon() + { + if (adjustedObjectIconsBySourceIid.TryGetValue(sourceIcon.GetInstanceID(), out adjustedIcon)) return; + + + adjustedIcon = new Texture2D(sourceIcon.width, sourceIcon.height, sourceIcon.format, sourceIcon.mipmapCount, false); + adjustedIcon.hideFlags = HideFlags.DontSave; + adjustedIcon.SetPropertyValue("pixelsPerPoint", (sourceIcon.width / 16f).RoundToInt()); + + Graphics.CopyTexture(sourceIcon, adjustedIcon); + + + adjustedObjectIconsBySourceIid[sourceIcon.GetInstanceID()] = adjustedIcon; + + } + + + getSourceIconFromVHierarchy(); + getAdjustedIcon(); + + propertyEditor.titleContent = new GUIContent(name, adjustedIcon); + + propertyEditor.SetMemberValue("m_InspectedObject", null); // prevents further title updates from both internal code and vTabs + + t_DockArea.GetFieldValue("s_GUIContents").Clear(); + + } + static void UpdatePropertyEditorTitles() => allPropertyEditors.ForEach(r => UpdatePropertyEditorTitle(r)); + + static Dictionary adjustedObjectIconsBySourceIid = new Dictionary(); + + + + + static void UpdateGUIWrappingForBrowser(EditorWindow browser) + { + if (!browser.hasFocus) return; + if (mi_VFavorites_CanBrowserBeWrapped != null && mi_VFavorites_CanBrowserBeWrapped.Invoke(null, new[] { browser }).Equals(false)) return; + + var isLocked = browser.GetMemberValue("isLocked"); + var isWrapped = browser.GetMemberValue("m_Parent").GetMemberValue("m_OnGUI").Method.Name == nameof(WrappedBrowserOnGUI); + + void wrap() + { + if (!isLocked) return; + if (isWrapped) return; + + var hostView = browser.GetMemberValue("m_Parent"); + + var newDelegate = typeof(VTabs).GetMethod(nameof(WrappedBrowserOnGUI), maxBindingFlags).CreateDelegate(t_EditorWindowDelegate, browser); + + hostView.SetMemberValue("m_OnGUI", newDelegate); + + browser.Repaint(); + + + browser.SetMemberValue("useTreeViewSelectionInsteadOfMainSelection", false); + + } + void unwrap() + { + if (isLocked) return; + if (!isWrapped) return; + + var hostView = browser.GetMemberValue("m_Parent"); + + var originalDelegate = hostView.InvokeMethod("CreateDelegate", "OnGUI"); + + hostView.SetMemberValue("m_OnGUI", originalDelegate); + + browser.Repaint(); + + } + + wrap(); + unwrap(); + + } + static void UpdateGUIWrappingForAllBrowsers() => allBrowsers.ForEach(r => UpdateGUIWrappingForBrowser(r)); + + static void WrappedBrowserOnGUI(EditorWindow browser) + { + var headerHeight = 26; + var footerHeight = 21; + var breadcrubsYOffset = .5f; + + var headerRect = browser.position.SetPos(0, 0).SetHeight(headerHeight); + var footerRect = browser.position.SetPos(0, 0).SetHeightFromBottom(footerHeight); + var listAreaRect = browser.position.SetPos(0, 0).AddHeight(-footerHeight).AddHeightFromBottom(-headerHeight); + + var breadcrumbsRect = headerRect.AddHeightFromBottom(-breadcrubsYOffset * 2); + var topGapRect = headerRect.SetHeight(breadcrubsYOffset * 2); + + var breadcrumbsTint = isDarkTheme ? Greyscale(0, .05f) : Greyscale(0, .02f); + var topGapColor = isDarkTheme ? Greyscale(.24f, 1) : Greyscale(.8f, 1); + + var isOneColumn = browser.GetMemberValue("m_ViewMode") == 0; + + + void setRootForOneColumn() + { + if (!isOneColumn) return; + if (curEvent.isRepaint) return; + if (!(browser.GetMemberValue("m_AssetTree") is object m_AssetTree)) return; + if (!(m_AssetTree.GetMemberValue("data") is object data)) return; + + var m_rootInstanceID = data.GetMemberValue("m_rootInstanceID"); + + void setInitial() + { + if (m_rootInstanceID != 0) return; + + var folderPath = browser.GetMemberValue("m_SearchFilter")?.GetMemberValue("m_Folders")?.FirstOrDefault() ?? "Assets"; + var folderIid = AssetDatabase.LoadAssetAtPath(folderPath).GetInstanceID(); + + data.SetMemberValue("m_rootInstanceID", folderIid); + + m_AssetTree.InvokeMethod("ReloadData"); + + } + void update() + { + if (m_rootInstanceID == 0) return; + + var folderIid = m_rootInstanceID; + var folderPath = EditorUtility.InstanceIDToObject(folderIid).GetPath(); + + browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { folderPath }); + + } + void reset() + { + if (browser.GetMemberValue("isLocked")) return; + + data.SetMemberValue("m_rootInstanceID", 0); + browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_Folders", new[] { "Assets" }); + + m_AssetTree.InvokeMethod("ReloadData"); + + // returns the browser to normal state on unlock + + } + + setInitial(); + update(); + reset(); + + } + void handleFolderChange() + { + if (isOneColumn) return; + + void onBreadcrumbsClick() + { + if (!curEvent.isMouseUp) return; + if (!breadcrumbsRect.IsHovered()) return; + + browser.RecordUndo(); + + toCallInGUI += () => UpdateBrowserTitle(browser); + toCallInGUI += () => browser.Repaint(); + + } + void onDoubleclick() + { + if (!curEvent.isMouseDown) return; + if (curEvent.clickCount != 2) return; + + browser.RecordUndo(); + + EditorApplication.delayCall += () => UpdateBrowserTitle(browser); + EditorApplication.delayCall += () => browser.Repaint(); + + } + void onUndoRedo() + { + if (!curEvent.isKeyDown) return; + if (!curEvent.holdingCmdOrCtrl) return; + if (curEvent.keyCode != KeyCode.Z) return; + + var curFolderGuid = browser.InvokeMethod("GetActiveFolderPath").ToGuid(); + + EditorApplication.delayCall += () => + { + var delayedFolderGuid = browser.InvokeMethod("GetActiveFolderPath").ToGuid(); + + if (delayedFolderGuid == curFolderGuid) return; + + + var folderIid = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(delayedFolderGuid)).GetInstanceID(); + + browser.InvokeMethod("SetFolderSelection", new[] { folderIid }, false); + + UpdateBrowserTitle(browser); + + }; + + } + + onBreadcrumbsClick(); + onDoubleclick(); + onUndoRedo(); + + } + + void oneColumn() + { + if (!isOneColumn) return; + + + + + if (!browser.InvokeMethod("Initialized")) + browser.InvokeMethod("Init"); + + + + var m_TreeViewKeyboardControlID = GUIUtility.GetControlID(FocusType.Keyboard); + + + + + + + + browser.InvokeMethod("OnEvent"); + + if (curEvent.isMouseDown && browser.position.SetPos(0, 0).IsHovered()) + t_ProjectBrowser.SetFieldValue("s_LastInteractedProjectBrowser", browser); + + + + + // header + browser.SetFieldValue("m_ListHeaderRect", breadcrumbsRect); + + if (curEvent.isRepaint) + browser.InvokeMethod("BreadCrumbBar"); + + breadcrumbsRect.Draw(breadcrumbsTint); + topGapRect.Draw(topGapColor); + + + + + // footer + browser.SetFieldValue("m_BottomBarRect", footerRect); + browser.InvokeMethod("BottomBar"); + + + + + // tree + browser.GetMemberValue("m_AssetTree")?.InvokeMethod("OnGUI", listAreaRect, m_TreeViewKeyboardControlID); + + + + + + + + + + + + + browser.InvokeMethod("HandleCommandEvents"); + + + + } + void twoColumns() + { + if (isOneColumn) return; + + + + if (!browser.InvokeMethod("Initialized")) + browser.InvokeMethod("Init"); + + + + var m_ListKeyboardControlID = GUIUtility.GetControlID(FocusType.Keyboard); + + var startGridSize = browser.GetFieldValue("m_ListArea")?.GetMemberValue("gridSize"); + + + + + + browser.InvokeMethod("OnEvent"); + + if (curEvent.isMouseDown && browser.position.SetPos(0, 0).IsHovered()) + t_ProjectBrowser.SetFieldValue("s_LastInteractedProjectBrowser", browser); + + + + + // header + browser.SetFieldValue("m_ListHeaderRect", breadcrumbsRect); + + + browser.InvokeMethod("BreadCrumbBar"); + + breadcrumbsRect.Draw(breadcrumbsTint); + topGapRect.Draw(topGapColor); + + + + + // footer + browser.SetFieldValue("m_BottomBarRect", footerRect); + browser.InvokeMethod("BottomBar"); + + + + + // list area + browser.GetFieldValue("m_ListArea").InvokeMethod("OnGUI", listAreaRect, m_ListKeyboardControlID); + + // block grid size changes when ctrl-shift-scrolling + if (curEvent.holdingCmdOrCtrl) + browser.GetFieldValue("m_ListArea").SetMemberValue("gridSize", startGridSize); + + + + + + browser.SetFieldValue("m_StartGridSize", browser.GetFieldValue("m_ListArea").GetMemberValue("gridSize")); + + browser.InvokeMethod("HandleContextClickInListArea", listAreaRect); + browser.InvokeMethod("HandleCommandEvents"); + + + + } + + + setRootForOneColumn(); + handleFolderChange(); + + oneColumn(); + twoColumns(); + + } + + + + + + + static void ReplaceTabScrollerButtonsWithGradients() + { + void getStyles() + { + if (leftScrollerStyle != null && rightScrollerStyle != null) return; + + if (!guiStylesInitialized) TryInitializeGuiStyles(); + if (!guiStylesInitialized) return; + + if (typeof(GUISkin).GetFieldValue("current")?.GetFieldValue>("m_Styles")?.ContainsKey("dragtab scroller prev") != true) return; + if (typeof(GUISkin).GetFieldValue("current")?.GetFieldValue>("m_Styles")?.ContainsKey("dragtab scroller next") != true) return; + + + var t_Styles = typeof(Editor).Assembly.GetType("UnityEditor.DockArea+Styles"); + + leftScrollerStyle = t_Styles.GetFieldValue("tabScrollerPrevButton"); + rightScrollerStyle = t_Styles.GetFieldValue("tabScrollerNextButton"); + + } + void createTextures() + { + if (leftScrollerGradient != null && rightScrollerGradient != null && clearTexture != null) return; + + + clearTexture = new Texture2D(1, 1); + clearTexture.hideFlags = HideFlags.DontSave; + clearTexture.SetPixel(0, 0, Color.clear); + clearTexture.Apply(); + + + var res = 16; + var greyscale = EditorGUIUtility.isProSkin ? .16f : .65f; + + leftScrollerGradient = new Texture2D(res, 1); + leftScrollerGradient.hideFlags = HideFlags.DontSave; + leftScrollerGradient.SetPixels(Enumerable.Range(0, res).Select(r => Greyscale(greyscale, r / (res - 1f))).Reverse().ToArray(), 0); + leftScrollerGradient.Apply(); + + rightScrollerGradient = new Texture2D(res, 1); + rightScrollerGradient.hideFlags = HideFlags.DontSave; + rightScrollerGradient.SetPixels(Enumerable.Range(0, res).Select(r => Greyscale(greyscale, r / (res - 1f))).ToArray(), 0); + rightScrollerGradient.Apply(); + + } + void assignTextures() + { + if (leftScrollerStyle == null) return; + if (rightScrollerStyle == null) return; + + // var hideLeftGradient = EditorWindow.focusedWindow && EditorWindow.focusedWindow.docked && GetTabList(EditorWindow.focusedWindow).First() == EditorWindow.focusedWindow; + // var hideRightGradient = EditorWindow.focusedWindow && EditorWindow.focusedWindow.docked && GetTabList(EditorWindow.focusedWindow).Last() == EditorWindow.focusedWindow; + var hideLeftGradient = false; + var hideRightGradient = false; + + leftScrollerStyle.normal.background = hideLeftGradient ? clearTexture : leftScrollerGradient; + rightScrollerStyle.normal.background = hideRightGradient ? clearTexture : rightScrollerGradient; + } + + getStyles(); + createTextures(); + assignTextures(); + + } + + static GUIStyle leftScrollerStyle; + static GUIStyle rightScrollerStyle; + + static Texture2D leftScrollerGradient; + static Texture2D rightScrollerGradient; + static Texture2D clearTexture; + + + + + + + static void ClosePropertyEditorsWithNonLoadableObjects() + { + foreach (var propertyEditor in allPropertyEditors) + if (propertyEditor.GetMemberValue("m_InspectedObject") == null) + propertyEditor.Close(); + + } + + static void LoadPropertyEditorInspectedObjects() + { + foreach (var propertyEditor in allPropertyEditors) + propertyEditor.InvokeMethod("LoadPersistedObject"); + + } + + + + + static void EnsureTabVisibleOnScroller(EditorWindow window) + { + var pos = GetOptimalTabScrollerPosition(window); + + if (!pos.Approx(0)) + pos += nonZeroTabScrollOffset; + + GetDockArea(window).SetFieldValue("m_ScrollOffset", pos); + + } + static void EnsureActiveTabsVisibleOnScroller() => allEditorWindows.Where(r => r.hasFocus && !r.maximized && r.docked).ForEach(r => EnsureTabVisibleOnScroller(r)); + + static float GetOptimalTabScrollerPosition(EditorWindow activeTab) + { + + var dockArea = activeTab.GetMemberValue("m_Parent"); + var tabAreaWidth = dockArea.GetFieldValue("m_TabAreaRect").width; + + if (tabAreaWidth == 0) + tabAreaWidth = activeTab.position.width - 38; + + if (tabStyle == null) + if (guiStylesInitialized) + tabStyle = new GUIStyle("dragtab"); + else return 0; + + + + + var activeTabXMin = 0f; + var activeTabXMax = 0f; + + var tabWidthSum = 0f; + + var activeTabReached = false; + + foreach (var tab in GetTabList(activeTab)) + { + var tabWidth = dockArea.InvokeMethod("GetTabWidth", tabStyle, tab); + + tabWidthSum += tabWidth; + + + if (activeTabReached) continue; + + activeTabXMin = activeTabXMax; + activeTabXMax += tabWidth; + + if (tab == activeTab) + activeTabReached = true; + + } + + + + + var optimalScrollPos = 0f; + + var visibleAreaPadding = 65f; + + var visibleAreaXMin = activeTabXMin - visibleAreaPadding; + var visibleAreaXMax = activeTabXMax + visibleAreaPadding; + + optimalScrollPos = Mathf.Max(optimalScrollPos, visibleAreaXMax - tabAreaWidth); + optimalScrollPos = Mathf.Min(optimalScrollPos, tabWidthSum - tabAreaWidth + 4); + + optimalScrollPos = Mathf.Min(optimalScrollPos, visibleAreaXMin); + optimalScrollPos = Mathf.Max(optimalScrollPos, 0); + + + + + return optimalScrollPos; + + } + + static GUIStyle tabStyle; + + static float nonZeroTabScrollOffset = 3f; + + + + + + + + + [UnityEditor.Callbacks.PostProcessBuild] + static void OnBuild(BuildTarget _, string __) + { + EditorApplication.delayCall += LoadPropertyEditorInspectedObjects; + EditorApplication.delayCall += UpdatePropertyEditorTitles; + } + + static void OnDomainReloaded() + { + toCallInGUI += UpdateGUIWrappingForAllBrowsers; + toCallInGUI += UpdateBrowserTitles; + + } + + static void OnSceneOpened(Scene _, OpenSceneMode __) + { + LoadPropertyEditorInspectedObjects(); + ClosePropertyEditorsWithNonLoadableObjects(); + UpdatePropertyEditorTitles(); + + } + + static void OnProjectLoaded() + { + toCallInGUI += EnsureActiveTabsVisibleOnScroller; + + UpdatePropertyEditorTitles(); + + } + + static void OnFocusedWindowChanged() + { + if (EditorWindow.focusedWindow?.GetType() == t_ProjectBrowser) + UpdateGUIWrappingForBrowser(EditorWindow.focusedWindow); + + } + + static void OnWindowUnmaximized() + { + UpdatePropertyEditorTitles(); + UpdateBrowserTitles(); + + UpdateGUIWrappingForAllBrowsers(); + + + EnsureActiveTabsVisibleOnScroller(); + + } + + + + + + + + static void CheckIfFocusedWindowChanged() + { + if (prevFocusedWindow != EditorWindow.focusedWindow) + OnFocusedWindowChanged(); + + prevFocusedWindow = EditorWindow.focusedWindow; + } + + static EditorWindow prevFocusedWindow; + + + + static void CheckIfWindowWasUnmaximized() + { + var isMaximized = EditorWindow.focusedWindow?.maximized == true; + + if (!isMaximized && wasMaximized) + OnWindowUnmaximized(); + + wasMaximized = isMaximized; + + } + + static bool wasMaximized; + + + + + static void OnSomeGUI() + { + toCallInGUI?.Invoke(); + toCallInGUI = null; + + CheckIfFocusedWindowChanged(); + + } + + static void ProjectWindowItemOnGUI(string _, Rect __) => OnSomeGUI(); + static void HierarchyWindowItemOnGUI(int _, Rect __) => OnSomeGUI(); + + static Action toCallInGUI; + + + + + + + static void DelayCallLoop() + { + UpdateBrowserTitles(); + UpdateGUIWrappingForAllBrowsers(); + UpdateDelayedMousePosition(); + ReplaceTabScrollerButtonsWithGradients(); + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + } + + + + + static void UpdateDelayedMousePosition() + { + var lastEvent = typeof(Event).GetFieldValue("s_Current"); + + delayedMousePosition_screenSpace = EditorGUIUtility.GUIToScreenPoint(lastEvent.mousePosition); + + } + + static Vector2 delayedMousePosition_screenSpace; + + + + + + + + + + + + + + + + static void ComponentTabHeaderGUI(Editor editor) + { + if (!(editor.target is Component component)) return; + + var headerRect = ExpandWidthLabelRect(height: 0).MoveY(-48).SetHeight(50).AddWidthFromMid(8); + var nameRect = headerRect.MoveX(43).MoveY(5).SetHeight(20).SetXMax(headerRect.xMax - 50); + var subtextRect = headerRect.MoveX(43).MoveY(22).SetHeight(20); + + + void hideName() + { + var maskRect = headerRect.AddWidthFromRight(-45).AddWidth(-50); + + var maskColor = Greyscale(isDarkTheme ? .24f : .8f); + + maskRect.Draw(maskColor); + + } + void name() + { + SetLabelFontSize(13); + + GUI.Label(nameRect, GetComponentName(component)); + + ResetLabelStyle(); + + } + void componentOf() + { + SetGUIEnabled(false); + + GUI.Label(subtextRect, "Component of"); + + ResetGUIEnabled(); + + } + void goName() + { + var goNameRect = subtextRect.MoveX("Component of ".GetLabelWidth() - 3).SetWidth(component.gameObject.name.GetLabelWidth(isBold: true)); + + goNameRect.MarkInteractive(); + + SetGUIEnabled(goNameRect.IsHovered() && !mousePressedOnGoName); + SetLabelBold(); + + GUI.Label(goNameRect, component.gameObject.name); + + ResetGUIEnabled(); + ResetLabelStyle(); + + + + if (curEvent.isMouseDown && goNameRect.IsHovered()) + { + mousePressedOnGoName = true; + curEvent.Use(); + } + + if (curEvent.isMouseUp) + { + if (mousePressedOnGoName) + EditorGUIUtility.PingObject(component.gameObject); + + mousePressedOnGoName = false; + curEvent.Use(); + } + + if (curEvent.isMouseLeaveWindow || (!curEvent.isLayout && !goNameRect.Resize(1).IsHovered())) + mousePressedOnGoName = false; + + } + + + + hideName(); + name(); + componentOf(); + goName(); + + Space(-4); + + } + + static bool mousePressedOnGoName; + + static string GetComponentName(Component component) + { + if (!component) return ""; + + var name = new GUIContent(EditorGUIUtility.ObjectContent(component, component.GetType())).text; + + name = name.Substring(name.LastIndexOf('(') + 1); + name = name.Substring(0, name.Length - 1); + + return name; + + } + + + + + + + + + static void PreventGameViewZoomOnShiftScroll() // called from Update + { + if (!curEvent.holdingShift) return; + if (Application.isPlaying) return; // zoom by scrolling is disabled in playmode anyway + if (!(EditorWindow.mouseOverWindow is EditorWindow hoveredWindow)) return; + if (hoveredWindow.GetType() != t_GameView) return; + if (!(hoveredWindow.GetMemberValue("m_ZoomArea", false) is object zoomArea)) return; + + var isScroll = !curEvent.isMouseMove && curEvent.mouseDelta != Vector2.zero; + + if (isScroll) + { + zoomArea.SetMemberValue("m_Scale", lastGameViewScale); + zoomArea.SetMemberValue("m_Translation", lastGameViewTranslation); + } + else + { + lastGameViewScale = zoomArea.GetMemberValue("m_Scale"); + lastGameViewTranslation = zoomArea.GetMemberValue("m_Translation"); + } + } + + static Vector2 lastGameViewScale = Vector2.one; + static Vector2 lastGameViewTranslation = Vector2.zero; + + + + + + + + + static void TryInitializeGuiStyles() => EditorWindow.focusedWindow?.SendEvent(EditorGUIUtility.CommandEvent("")); + + static bool guiStylesInitialized => typeof(GUI).GetFieldValue("s_Skin") != null; + + + + + + static object GetDockArea(EditorWindow window) => window.GetFieldValue("m_Parent"); + + static List GetTabList(EditorWindow window) => GetDockArea(window).GetFieldValue>("m_Panes"); + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VTabsMenu.pluginDisabled) return; + + + + // dragndrop and scrolling + EditorApplication.delayCall += () => EditorApplication.update -= Update; + EditorApplication.delayCall += () => EditorApplication.update += Update; + + EditorApplication.delayCall -= UpdateDelayedMousePosition; + EditorApplication.delayCall += UpdateDelayedMousePosition; + + EditorApplication.update -= PreventGameViewZoomOnShiftScroll; + EditorApplication.update += PreventGameViewZoomOnShiftScroll; + + + + // shortcuts + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", (globalEventHandler - CheckShortcuts) + CheckShortcuts); + + + // component tabs + Editor.finishedDefaultHeaderGUI -= ComponentTabHeaderGUI; + Editor.finishedDefaultHeaderGUI += ComponentTabHeaderGUI; + + + + + // state change detectors + var projectWasLoaded = typeof(EditorApplication).GetFieldValue("projectWasLoaded"); + typeof(EditorApplication).SetFieldValue("projectWasLoaded", (projectWasLoaded - OnProjectLoaded) + OnProjectLoaded); + + UnityEditor.SceneManagement.EditorSceneManager.sceneOpened -= OnSceneOpened; + UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += OnSceneOpened; + + EditorApplication.projectWindowItemOnGUI -= ProjectWindowItemOnGUI; + EditorApplication.projectWindowItemOnGUI += ProjectWindowItemOnGUI; + + EditorApplication.hierarchyWindowItemOnGUI -= HierarchyWindowItemOnGUI; + EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + + + + OnDomainReloaded(); + + } + + + + + + + + + static IEnumerable allBrowsers => _allBrowsers ??= t_ProjectBrowser.GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allBrowsers; + + static IEnumerable allPropertyEditors => Resources.FindObjectsOfTypeAll(t_PropertyEditor).Where(r => r.GetType().BaseType == typeof(EditorWindow)).Cast(); + + static List allEditorWindows => Resources.FindObjectsOfTypeAll().ToList(); + + + + + static Type t_EditorWindow = typeof(EditorWindow); + static Type t_DockArea = typeof(Editor).Assembly.GetType("UnityEditor.DockArea"); + static Type t_PropertyEditor = typeof(Editor).Assembly.GetType("UnityEditor.PropertyEditor"); + static Type t_ProjectBrowser = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + static Type t_GameView = typeof(Editor).Assembly.GetType("UnityEditor.GameView"); + static Type t_SceneHierarchyWindow = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); + static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView"); + static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags); + + + static Type t_VHierarchy = Type.GetType("VHierarchy.VHierarchy") ?? Type.GetType("VHierarchy.VHierarchy, VHierarchy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFolders = Type.GetType("VFolders.VFolders") ?? Type.GetType("VFolders.VFolders, VFolders, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFavorites = Type.GetType("VFavorites.VFavorites") ?? Type.GetType("VFavorites.VFavorites, VFavorites, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + static MethodInfo mi_VFolders_GetIcon = t_VFolders?.GetMethod("GetSmallFolderIcon_forVTabs", maxBindingFlags); + static MethodInfo mi_VHierarchy_GetIcon = t_VHierarchy?.GetMethod("GetIcon_forVTabs", maxBindingFlags); + static MethodInfo mi_VFavorites_BeforeWindowCreated = t_VFavorites?.GetMethod("BeforeWindowCreated_byVTabs", maxBindingFlags); + static MethodInfo mi_VFavorites_CanBrowserBeWrapped = t_VFavorites?.GetMethod("CanBrowserBeWrapped_byVTabs", maxBindingFlags); + + + + + + const string version = "2.0.10"; + + } +} +#endif diff --git a/Assets/Third Parties/vTabs/VTabs.cs.meta b/Assets/Third Parties/vTabs/VTabs.cs.meta new file mode 100644 index 00000000..045b3150 --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc570418b0cd44cd49a39403a3ba1cf7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/VTabsLibs.cs b/Assets/Third Parties/vTabs/VTabsLibs.cs new file mode 100644 index 00000000..063608ea --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsLibs.cs @@ -0,0 +1,1152 @@ + +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Reflection; +using System.Linq; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Experimental.Rendering; +using UnityEditor; +using Type = System.Type; +using static VTabs.Libs.VUtils; + + + +namespace VTabs.Libs +{ + + public static class VUtils + { + #region Text + + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + + + + + #endregion + + #region IEnumerables + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + public static bool None(this IEnumerable ie, System.Func f) => !ie.Any(f); + public static bool None(this IEnumerable ie) => !ie.Any(); + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + + public static void ForEach(this IEnumerable sequence, System.Action action) { foreach (T item in sequence) action(item); } + + + + + + #endregion + + #region Reflection + + public static object GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + + public static void SetFieldValue(this object o, string fieldName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) // todo handle null params (can't get their type) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in type '{type.Name}', its parent types and interfaces"); + + } + + + + static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + static Dictionary> fieldInfoCache = new Dictionary>(); + + static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + static Dictionary> propertyInfoCache = new Dictionary>(); + + static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + static Dictionary> methodInfoCache = new Dictionary>(); + + + + public static T GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) => (T)o.GetFieldValue(fieldName, exceptionIfNotFound); + public static T GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) => (T)o.GetPropertyValue(propertyName, exceptionIfNotFound); + public static T GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) => (T)o.GetMemberValue(memberName, exceptionIfNotFound); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + #endregion + + #region Math + + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + public static float DistanceTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float DistanceTo(this Vector2 f1, Vector2 f2) => (f1 - f2).magnitude; + public static float DistanceTo(this Vector3 f1, Vector3 f2) => (f1 - f2).magnitude; + public static float Dist(float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Avg(float f1, float f2) => (f1 + f2) / 2; + public static float Abs(this float f) => Mathf.Abs(f); + public static int Abs(this int f) => Mathf.Abs(f); + public static float Sign(this float f) => Mathf.Sign(f); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new Vector2(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new Vector3(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + public static float ToFloat(this double f) => (float)f; + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + + + public static Color Greyscale(float brightness, float alpha = 1) => new Color(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region GlobalID + + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && globalObjectIdString != null && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + } + + public static GlobalID GetGlobalID(this Object o) => new GlobalID(o); + + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + } + + + + + + + #endregion + + #region Paths + + + public static string GetParentPath(this string path) => path.Substring(0, path.LastIndexOf('/')); + public static bool HasParentPath(this string path) => path.Contains('/') && path.GetParentPath() != ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + + + #endregion + + #region AssetDatabase + + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + public static bool IsValidGuid(this string guid) => AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(guid), AssetPathToGUIDOptions.OnlyExistingAssets) != ""; + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + + + + + + + #endregion + + #region Editor + + public static int GetProjectId() => Application.dataPath.GetHashCode(); + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", (BindingFlags)62).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", (BindingFlags)62).Invoke(null, null); + } + + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void RecordUndo(this Object so) => Undo.RecordObject(so, ""); + + + + #endregion + + } + + + public static class VGUI + { + + #region Colors + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + #endregion + + #region Shortcuts + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fotSize) + { + SetLabelFontSize(fotSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + + public static void SetGUIColor(Color c) + { + if (!_guiColorModified) + _defaultGuiColor = GUI.color; + + _guiColorModified = true; + + GUI.color = _defaultGuiColor * c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorModified ? _defaultGuiColor : Color.white; + + _guiColorModified = false; + + } + static bool _guiColorModified; + static Color _defaultGuiColor; + + + + #endregion + + #region Events + + public struct WrappedEvent + { + public Event e; + + public bool isNull => e == null; + public bool isRepaint => isNull ? default : e.type == EventType.Repaint; + public bool isLayout => isNull ? default : e.type == EventType.Layout; + public bool isUsed => isNull ? default : e.type == EventType.Used; + public bool isMouseLeaveWindow => isNull ? default : e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => isNull ? default : e.type == EventType.MouseEnterWindow; + public bool isContextClick => isNull ? default : e.type == EventType.ContextClick; + + public bool isKeyDown => isNull ? default : e.type == EventType.KeyDown; + public bool isKeyUp => isNull ? default : e.type == EventType.KeyUp; + public KeyCode keyCode => isNull ? default : e.keyCode; + public char characted => isNull ? default : e.character; + + public bool isExecuteCommand => isNull ? default : e.type == EventType.ExecuteCommand; + public string commandName => isNull ? default : e.commandName; + + public bool isMouse => isNull ? default : e.isMouse; + public bool isMouseDown => isNull ? default : e.type == EventType.MouseDown; + public bool isMouseUp => isNull ? default : e.type == EventType.MouseUp; + public bool isMouseDrag => isNull ? default : e.type == EventType.MouseDrag; + public bool isMouseMove => isNull ? default : e.type == EventType.MouseMove; + public bool isScroll => isNull ? default : e.type == EventType.ScrollWheel; + public int mouseButton => isNull ? default : e.button; + public int clickCount => isNull ? default : e.clickCount; + public Vector2 mousePosition => isNull ? default : e.mousePosition; + public Vector2 mousePosition_screenSpace => isNull ? default : GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => isNull ? default : e.delta; + + public bool isDragUpdate => isNull ? default : e.type == EventType.DragUpdated; + public bool isDragPerform => isNull ? default : e.type == EventType.DragPerform; + public bool isDragExit => isNull ? default : e.type == EventType.DragExited; + + public EventModifiers modifiers => isNull ? default : e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => isNull ? default : e.alt; + public bool holdingShift => isNull ? default : e.shift; + public bool holdingCtrl => isNull ? default : e.control; + public bool holdingCmd => isNull ? default : e.command; + public bool holdingCmdOrCtrl => isNull ? default : e.command || e.control; + + public bool holdingAltOnly => isNull ? default : e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => isNull ? default : e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => isNull ? default : e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => isNull ? default : e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => isNull ? default : (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new WrappedEvent(e); + public static WrappedEvent curEvent => (Event.current ?? _fi_s_Current.GetValue(null) as Event).Wrap(); + static FieldInfo _fi_s_Current = typeof(Event).GetField("s_Current", maxBindingFlags); + + + + + + #endregion + + #region Drawing + + public static Rect Draw(this Rect r) { EditorGUI.DrawRect(r, Color.black); return r; } + public static Rect Draw(this Rect r, Color c) { EditorGUI.DrawRect(r, c); return r; } + + + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, float cornerRadius) => rect.DrawWithRoundedCorners(color, cornerRadius.RoundToInt()); + static Dictionary _roundedStylesByCornerRadius = new Dictionary(); + + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + static Texture2D[] _gradientTextures; + + + + public static bool IsHovered(this Rect r) => !curEvent.isNull && r.Contains(curEvent.mousePosition); + + #endregion + + #region Spacing + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static void Divider(float space = 15, float yOffset = 0) + { + GUILayout.Label("", GUILayout.Height(space), GUILayout.ExpandWidth(true)); + lastRect.SetHeightFromMid(1).SetWidthFromMid(lastRect.width - 16).MoveY(yOffset).Draw(isDarkTheme ? Color.white * .42f : Color.white * .72f); + } + + public static Rect ExpandSpace() { GUILayout.Label("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); return lastRect; } + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + #endregion + + #region Other + + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + + + #endregion + + + } + + +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vTabs/VTabsLibs.cs.meta b/Assets/Third Parties/vTabs/VTabsLibs.cs.meta new file mode 100644 index 00000000..85406508 --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91d4456e469254096af9035b29263ca5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/VTabsMenu.cs b/Assets/Third Parties/vTabs/VTabsMenu.cs new file mode 100644 index 00000000..f579724d --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsMenu.cs @@ -0,0 +1,141 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using static VTabs.Libs.VUtils; + + + +namespace VTabs +{ + public static class VTabsMenu + { + + public static bool dragndropEnabled { get => EditorPrefs.GetBool("vTabs-dragndropEnabled", true); set => EditorPrefs.SetBool("vTabs-dragndropEnabled", value); } + public static bool switchTabsEnabled { get => EditorPrefs.GetBool("vTabs-switchTabsEnabled", true); set => EditorPrefs.SetBool("vTabs-switchTabsEnabled", value); } + public static bool moveTabsEnabled { get => EditorPrefs.GetBool("vTabs-moveTabsEnabled", true); set => EditorPrefs.SetBool("vTabs-moveTabsEnabled", value); } + + public static bool addTabEnabled { get => EditorPrefs.GetBool("vTabs-addTabEnabled", true); set => EditorPrefs.SetBool("vTabs-addTabEnabled", value); } + public static bool closeTabEnabled { get => EditorPrefs.GetBool("vTabs-closeTabEnabled", true); set => EditorPrefs.SetBool("vTabs-closeTabEnabled", value); } + public static bool reopenTabEnabled { get => EditorPrefs.GetBool("vTabs-reopenTabEnabled", true); set => EditorPrefs.SetBool("vTabs-reopenTabEnabled", value); } + + public static bool sidescrollEnabled { get => EditorPrefs.GetBool("vTabs-sidescrollEnabled", Application.platform == RuntimePlatform.OSXEditor); set => EditorPrefs.SetBool("vTabs-sidescrollEnabled", value); } + public static bool reverseScrollDirectionEnabled { get => EditorPrefs.GetBool("vTabs-reverseScrollDirectionDirection", false); set => EditorPrefs.SetBool("vTabs-reverseScrollDirectionDirection", value); } + public static float sidescrollSensitivity { get => EditorPrefs.GetFloat("vTabs-sidescrollSensitivity", 1); set => EditorPrefs.SetFloat("vTabs-sidescrollSensitivity", value); } + + public static bool pluginDisabled { get => EditorPrefs.GetBool("vTabs-pluginDisabled", false); set => EditorPrefs.SetBool("vTabs-pluginDisabled", value); } + + + + + const string dir = "Tools/vTabs/"; +#if UNITY_EDITOR_OSX + const string cmd = "Cmd"; +#else + const string cmd = "Ctrl"; +#endif + + const string dragndrop = dir + "Create tabs with Drag-and-Drop"; + const string switchTabs = dir + "Switch tabs with Shift-Scroll"; + const string moveTabs = dir + "Move tabs with " + cmd + "-Shift-Scroll"; + + const string addTab = dir + cmd + "-T to add tab"; + const string closeTab = dir + cmd + "-W to close tab"; + const string reopenTab = dir + cmd + "-Shift-T to reopen closed tab"; + + const string sidescroll = dir + "Horizontal Scroll as Shift-Scroll"; + const string reverseScrollDirection = dir + "Reverse direction"; + const string increaseSensitivity = dir + "Increase sensitivity"; + const string decreaseSensitivity = dir + "Decrease sensitivity"; + + const string disablePlugin = dir + "Disable vTabs"; + + + + + + + + [MenuItem(dir + "Features", false, 1)] static void dadsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsas123() => false; + + [MenuItem(dragndrop, false, 2)] static void dadsadsadasdsadadsas() => dragndropEnabled = !dragndropEnabled; + [MenuItem(dragndrop, true, 2)] static bool dadsaddsasadadsdasadsas() { Menu.SetChecked(dragndrop, dragndropEnabled); return !pluginDisabled; } + + [MenuItem(switchTabs, false, 3)] static void dadsadsadsadsadasdsadadsas() => switchTabsEnabled = !switchTabsEnabled; + [MenuItem(switchTabs, true, 3)] static bool dadsadasdasddsasadadsdasadsas() { Menu.SetChecked(switchTabs, switchTabsEnabled); return !pluginDisabled; } + + [MenuItem(moveTabs, false, 4)] static void dadsadsadsadasdsadadsas() => moveTabsEnabled = !moveTabsEnabled; + [MenuItem(moveTabs, true, 4)] static bool dadsadasddsasadadsdasadsas() { Menu.SetChecked(moveTabs, moveTabsEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Shortcuts", false, 101)] static void daaadsas() { } + [MenuItem(dir + "Shortcuts", true, 101)] static bool daadsdsas123() => false; + + [MenuItem(addTab, false, 102)] static void dadsadadsas() => addTabEnabled = !addTabEnabled; + [MenuItem(addTab, true, 102)] static bool dadsaddasadsas() { Menu.SetChecked(addTab, addTabEnabled); return !pluginDisabled; } + + [MenuItem(closeTab, false, 103)] static void dadsadasdadsas() => closeTabEnabled = !closeTabEnabled; + [MenuItem(closeTab, true, 103)] static bool dadsadsaddasadsas() { Menu.SetChecked(closeTab, closeTabEnabled); return !pluginDisabled; } + + [MenuItem(reopenTab, false, 104)] static void dadsadsadasdadsas() => reopenTabEnabled = !reopenTabEnabled; + [MenuItem(reopenTab, true, 104)] static bool dadsaddsasaddasadsas() { Menu.SetChecked(reopenTab, reopenTabEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Scrolling", false, 1001)] static void daadsdsadsas() { } + [MenuItem(dir + "Scrolling", true, 1001)] static bool dadsasasdads() => false; + + [MenuItem(sidescroll, false, 1002)] static void dadsadsadsadsadasdadssadadsas() => sidescrollEnabled = !sidescrollEnabled; + [MenuItem(sidescroll, true, 1002)] static bool dadsadasdasddsadassadadsdasadsas() { Menu.SetChecked(sidescroll, sidescrollEnabled); return !pluginDisabled; } + + [MenuItem(reverseScrollDirection, false, 1003)] static void dadsadadssadsadsadasdadssadadsas() => reverseScrollDirectionEnabled = !reverseScrollDirectionEnabled; + [MenuItem(reverseScrollDirection, true, 1003)] static bool dadsadasdadsasddsadassadadsdasadsas() { Menu.SetChecked(reverseScrollDirection, reverseScrollDirectionEnabled); return !pluginDisabled; } + + [MenuItem(increaseSensitivity, false, 1004)] static void qdadadsssa() { sidescrollSensitivity += .2f; Debug.Log("vTabs: scrolling sensitivity increased to " + sidescrollSensitivity * 100 + "%"); } + [MenuItem(increaseSensitivity, true, 1004)] static bool qdaddasadsssa() => !pluginDisabled; + + [MenuItem(decreaseSensitivity, false, 1005)] static void qdasadsssa() { sidescrollSensitivity -= .2f; Debug.Log("vTabs: scrolling sensitivity decreased to " + sidescrollSensitivity * 100 + "%"); } + [MenuItem(decreaseSensitivity, true, 1005)] static bool qdaddasdsaadsssa() => !pluginDisabled; + + + + + [MenuItem(dir + "More", false, 10001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 10001)] static bool dadsadsdasas123() => false; + + [MenuItem(dir + "Join our Discord", false, 10002)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/4dG9KsbspG"); + + + [MenuItem(dir + "Check out vFavorites 2", false, 10003)] + static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=checkoutvfav"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vHierarchy 2", false, 10003)] + // static void dadadssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/251320?aid=1100lGLBn&pubref=menucheckout"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vFolders 2", false, 10004)] + // static void dadadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263644?aid=1100lGLBn&pubref=menucheckout"); + + // [MenuItem(dir + "Get more Editor Enhancers/Get vFavorites 2", false, 10005)] + // static void dadadsadssaasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=menucheckout"); + + + + + + [MenuItem(disablePlugin, false, 100001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 100001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/Third Parties/vTabs/VTabsMenu.cs.meta b/Assets/Third Parties/vTabs/VTabsMenu.cs.meta new file mode 100644 index 00000000..2b50803a --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3cf48ada98a1147c4881faf7b79475e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Third Parties/vTabs/VTabsMenuItems.cs b/Assets/Third Parties/vTabs/VTabsMenuItems.cs new file mode 100644 index 00000000..8fb6b187 --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/Third Parties/vTabs/VTabsMenuItems.cs.meta b/Assets/Third Parties/vTabs/VTabsMenuItems.cs.meta new file mode 100644 index 00000000..fa3de45b --- /dev/null +++ b/Assets/Third Parties/vTabs/VTabsMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 102836a637c684c11b7581d444fecb04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: