OvertakingLegend/Assets/Plugins/vHierarchy/VHierarchyComponentWindow.cs

483 lines
13 KiB
C#

#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 (isResizing) 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()
{
BeginIndent(16);
editor?.OnInspectorGUI();
EndIndent(0);
}
void updateHeight()
{
var r = ExpandWidthLabelRect();
if (curEvent.isRepaint)
position = position.SetHeight(lastRect.y);
}
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 resizeArea = this.position.SetPos(0, 0).SetWidthFromRight(5).AddHeightFromBottom(-20);
void startResize()
{
if (isDragged) return;
if (isResizing) return;
if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return;
if (!resizeArea.IsHovered()) return;
isResizing = true;
resizeStartMousePos = EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition);
resizeStartWindowSize = this.position.size;
}
void updateResize()
{
if (!isResizing) return;
var resizedWidth = resizeStartWindowSize.x + EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition).x - resizeStartMousePos.x;
var width = resizedWidth.Max(minWidth);
if (!curEvent.isRepaint)
position = position.SetWidth(width);
EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive);
}
void stopResize()
{
if (!isResizing) return;
if (!curEvent.isMouseUp) return;
isResizing = false;
EditorGUIUtility.hotControl = 0;
}
EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeHorizontal);
startResize();
updateResize();
stopResize();
}
background();
outline();
header();
Space(3);
body();
Space(7);
updateHeight();
updatePosition();
closeOnEscape();
horizontalResize();
if (!isPinned)
Repaint();
}
public Vector2 targetPosition;
public Vector2 currentPosition;
Vector2 positionDeriv;
float deltaTime;
double lastLayoutTime;
bool isDragged;
Vector2 dragStartMousePos;
Vector2 dragStartWindowPos;
bool isResizing;
Vector2 resizeStartMousePos;
Vector2 resizeStartWindowSize;
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<VHierarchyComponentWindow>();
floatingInstance.ShowPopup();
var savedWidth = EditorPrefs.GetFloat("vHierarchy-componentWindowWidth", minWidth);
var width = savedWidth.Max(minWidth);
floatingInstance.position = Rect.zero.SetPos(position).SetWidth(width).SetHeight(200);
floatingInstance.targetPosition = position;
}
public static VHierarchyComponentWindow floatingInstance;
public static float minWidth => 300;
}
}
#endif