2022-09-06 13:47:45 +08:00

413 lines
16 KiB
C#

// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Reflection;
using FluffyUnderware.DevTools.Extensions;
namespace FluffyUnderware.DevToolsEditor
{
public static class DTToolbar
{
const int ITEMSPACE = 10;
const int PROJECTSPACE = 20;
public static bool RecalcItemSize = true;
public static bool MouseOverToolbarElement { get; private set; }
static Vector2[] _MaxItemDimension = new Vector2[4]; // each Orientation has it's own max. Dimension
/// <summary>
/// Starting position for each side
/// </summary>
static Rect[] _InitialRects;
/// <summary>
/// Last item position for each side
/// </summary>
static Rect[] _ItemRect;
static Event _handleEvent;
internal static void Initialize()
{
RecalcItemSize = true;
_handleEvent = null;
loadItems();
DTSelection.OnSelectionChange -= OnSelectionChange;
DTSelection.OnSelectionChange += OnSelectionChange;
SceneView.
#if UNITY_2019_1_OR_NEWER
duringSceneGui
#else
onSceneGUIDelegate
#endif
-= Render;
SceneView.
#if UNITY_2019_1_OR_NEWER
duringSceneGui
#else
onSceneGUIDelegate
#endif
+= Render;
EditorApplication.hierarchyWindowItemOnGUI -= onHierarchy;
EditorApplication.hierarchyWindowItemOnGUI += onHierarchy;
EditorApplication.update -= onUpdate;
EditorApplication.update += onUpdate;
}
static void loadItems()
{
foreach (DTProject prj in DT.Projects)
prj.ToolbarItems.Clear();
TypeCache.TypeCollection toolbarItemTypes = TypeCache.GetTypesDerivedFrom(typeof(DTToolbarItem));
toolbarItemTypes.Intersect(TypeCache.GetTypesWithAttribute<ToolbarItemAttribute>()).ForEach(type => Activator.CreateInstance(type));
foreach (DTProject prj in DT.Projects)
prj.ToolbarItems.Sort();
}
static void OnSelectionChange()
{
foreach (DTProject project in DT.Projects)
foreach (DTToolbarItem item in project.ToolbarItems)
item.OnSelectionChange();
RecalcItemSize = true;
}
static void onHierarchy(int instanceID, Rect selectionRect)
{
if (Selection.instanceIDs.Contains(instanceID))
_handleEvent = new Event(Event.current);
}
static void onUpdate()
{
if (Event.current != null || _handleEvent != null)
{
DTSelection.MuteEvents = true;
foreach (DTProject project in DT.Projects)
foreach (DTToolbarItem item in project.ToolbarItems)
{
if (_handleEvent != null && item.Visible && item.Enabled)
item.HandleEvents(_handleEvent);
if (Event.current != null && item.Visible && item.Enabled)
item.HandleEvents(Event.current);
}
DTSelection.MuteEvents = false;
}
_handleEvent = null;
}
static Rect getStatusBarRect()
{
Vector2 v = GUIUtility.GUIToScreenPoint(Vector2.zero);
Rect r = SceneView.currentDrawingSceneView.position;
// If SceneView is on another monitor, r.x doesn't start at 0, but GUIToScreenPoint gives the offset
r.x -= v.x;
r.y = _ItemRect[(int)DTToolbarOrientation.Bottom].y - 25;
r.height = 20;
return r;
}
public static bool detailOpen;
static void Render(SceneView view)
{
Event ev = Event.current;
MouseOverToolbarElement = false;
// Only let certain hotkeys pass
if (!EditorGUIUtility.editingTextField)
{
GUIUtility.keyboardControl = GUIUtility.GetControlID(FocusType.Passive);
}
List<DTProject> projects = DT.Projects;
projects.Sort();
Handles.BeginGUI();
GUI.skin = null; // to ensure light-skin is used if set in preferences (or not Pro)
// Get largest item for each side
if (RecalcItemSize)
calcMaxItemDimension();
// Get starting position for each side
_InitialRects = getInitialItemRect();
_ItemRect = new Rect[4];
_InitialRects.CopyTo(_ItemRect, 0);
DTToolbarItem lastItem = null;
DTSelection.MuteEvents = true;
DTToolbarItem hovering = null;
foreach (DTProject project in projects)
{
int side = (int)project.ToolbarOrientation;
if (lastItem && lastItem.Project.ToolbarOrientation != project.ToolbarOrientation)
lastItem = null;
List<DTToolbarItem> items = project.ToolbarItems;
// Render items
for (int i = 0; i < items.Count; i++)
{
if (items[i].Visible)
{
Vector2 itemSize = items[i].GetItemSize(); // size of current item
_ItemRect[side] = advanceItemRect(lastItem, items[i], itemSize); // advance by using the last itemRect and the new(current) size
items[i].mItemRect = _ItemRect[side]; // Store current item rect
if (items[i].Enabled)
{
Handles.EndGUI();
EditorKeyBinding.BindingsEnabled = false;
if (items[i].mItemRect.Contains(DTGUI.MousePosition))
{
hovering = items[i];
MouseOverToolbarElement = true;
}
items[i].OnSceneGUI();
EditorKeyBinding.BindingsEnabled = true;
Handles.BeginGUI();
}
GUI.enabled = items[i].Enabled;
items[i].Render(_ItemRect[side]);
GUI.enabled = true;
if (ev != null && items[i].Enabled && (DTToolbarItem.FocusedItem == null || DTToolbarItem.FocusedItem == items[i]))
items[i].HandleEvents(ev);
lastItem = items[i];
}
}
}
DTSelection.MuteEvents = false;
detailOpen = false;
// Render items client area (Note: itemRect contains the last rendered item, a.k.a. a way to get the number of rows/cols needed
foreach (DTProject project in projects)
{
List<DTToolbarItem> items = project.ToolbarItems;
for (int i = 0; i < items.Count; i++)
{
if (items[i].Visible && items[i].ShowClientArea)
{
detailOpen = true;
Rect clientRect = items[i].mItemRect;
int side = (int)project.ToolbarOrientation;
switch (project.ToolbarOrientation)
{
case DTToolbarOrientation.Left:
clientRect.x = _ItemRect[side].x + _MaxItemDimension[side].x + 5;
break;
case DTToolbarOrientation.Right:
clientRect.x = _ItemRect[side].x - 5;
break;
case DTToolbarOrientation.Top:
clientRect.y = _ItemRect[side].y + _MaxItemDimension[side].y + 5;
break;
case DTToolbarOrientation.Bottom:
clientRect.y = _ItemRect[side].y - 5;
break;
}
if (clientRect.width > 0 && clientRect.height > 0)
{
items[i].mBackgroundRects.Clear();
MouseOverToolbarElement = MouseOverToolbarElement || clientRect.Contains(DTGUI.MousePosition);
EditorKeyBinding.BindingsEnabled = false;
items[i].RenderClientArea(clientRect);
EditorKeyBinding.BindingsEnabled = true;
if (DTGUI.IsClick)
foreach (Rect r in items[i].mBackgroundRects)
if (r.Contains(ev.mousePosition))
DTGUI.UseEvent(items[i].GetHashCode(), ev);
}
}
}
}
// Handle statusbar info when hovering over an item
if (hovering != null)
DTToolbarItem._StatusBar.Set(hovering.StatusBarInfo, "Info");
else
DTToolbarItem._StatusBar.Clear("Info");
// Render Statusbar
DTToolbarItem._StatusBar.Render(getStatusBarRect(), null, true);
Handles.EndGUI();
}
static Rect[] getInitialItemRect()
{
Rect[] res = new Rect[4];
for (int side = 0; side < 4; side++)
{
Rect r = new Rect();
switch (side)
{
case (int)DTToolbarOrientation.Left:
r.x = 5; r.y = 10;
break;
case (int)DTToolbarOrientation.Right:
r.x = SceneView.currentDrawingSceneView.position.width - 10 - _MaxItemDimension[side].x;
r.y = 115;
break;
case (int)DTToolbarOrientation.Top:
float lft = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
r.x = 5; r.y = 10;
if (lft > 0)
r.x += 10 + lft;
break;
default: // Bottom
lft = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
r.x = 5; r.y = SceneView.currentDrawingSceneView.position.height - _MaxItemDimension[side].y - 30;
if (lft > 0)
r.x += 10 + lft;
break;
}
res[side] = r;
}
return res;
}
static Rect advanceItemRect(DTToolbarItem lastItem, DTToolbarItem newItem, Vector2 newItemSize)
{
Rect itemRect;
float space = 0;
int side = (int)newItem.Project.ToolbarOrientation;
if (lastItem != null)
{
itemRect = lastItem.mItemRect;
if (lastItem.Project != newItem.Project)
space = PROJECTSPACE;
else
space = (newItem.Order - lastItem.Order >= 10) ? ITEMSPACE : 0;
}
else
itemRect = _InitialRects[side];
switch (newItem.Project.ToolbarOrientation)
{
case DTToolbarOrientation.Left:
itemRect.y += itemRect.height + 3 + space;
if (itemRect.y + newItemSize.y > SceneView.currentDrawingSceneView.position.height - 30)
{
itemRect.y = 10;
itemRect.x += _MaxItemDimension[(int)DTToolbarOrientation.Left].x + 5;
}
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
itemRect.height = newItemSize.y;
break;
case DTToolbarOrientation.Right:
itemRect.y += itemRect.height + 3 + space;
if (itemRect.y + newItemSize.y > SceneView.currentDrawingSceneView.position.height - 30)
{
itemRect.y = 10;
itemRect.x -= _MaxItemDimension[(int)DTToolbarOrientation.Right].x + 5;
}
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Right].x;
itemRect.height = newItemSize.y;
break;
case DTToolbarOrientation.Top:
itemRect.x += 3 + itemRect.width + space;
if (itemRect.x + newItemSize.x > SceneView.currentDrawingSceneView.position.width - _MaxItemDimension[(int)DTToolbarOrientation.Left].x - _MaxItemDimension[(int)DTToolbarOrientation.Right].x)
{
itemRect.x = _InitialRects[(int)DTToolbarOrientation.Top].x;
itemRect.y += _MaxItemDimension[(int)DTToolbarOrientation.Top].y + 5;
}
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Top].x;
itemRect.height = newItemSize.y;
break;
case DTToolbarOrientation.Bottom:
itemRect.x += 3 + itemRect.width + space;
if (itemRect.x + newItemSize.x > SceneView.currentDrawingSceneView.position.width - _MaxItemDimension[(int)DTToolbarOrientation.Left].x - _MaxItemDimension[(int)DTToolbarOrientation.Right].x)
{
itemRect.x = _InitialRects[(int)DTToolbarOrientation.Top].x;
itemRect.y -= _MaxItemDimension[(int)DTToolbarOrientation.Bottom].y + 5;
}
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Bottom].x;
itemRect.height = newItemSize.y;
break;
}
return itemRect;
}
/// <summary>
/// For each side, find the item with the largest dimensions
/// </summary>
static void calcMaxItemDimension()
{
RecalcItemSize = false;
_MaxItemDimension = new Vector2[4];
int side;
foreach (DTProject prj in DT.Projects)
{
side = (int)prj.ToolbarOrientation;
foreach (DTToolbarItem item in prj.ToolbarItems)
if (item.Visible)
_MaxItemDimension[side] = Vector2.Max(_MaxItemDimension[side], item.GetItemSize());
}
}
internal static void SetRadioGroupState(DTToolbarRadioButton active)
{
active.Project.SetRadioGroupState(active);
}
}
public enum DTToolbarMode : int
{
Text = 1,
Icon = 2,
Full = 15,
}
public enum DTToolbarOrientation : int
{
Left = 0,
Right = 1,
Top = 2,
Bottom = 3
}
public class DTToolbarStatus : DTStatusbar
{
protected override void GetColors()
{
GUI.contentColor = new Color(0, 0, 0, 0.75f);
}
protected override GUIStyle GetStyle()
{
GUIStyle style = base.GetStyle();
style.alignment = TextAnchor.MiddleCenter;
Texture2D bgTex = new Texture2D(1, 1);
bgTex.SetPixel(0, 0, new Color(1, 1, 1, 0.5f));
bgTex.Apply();
bgTex.hideFlags = HideFlags.DontSave;
style.normal.background = bgTex;
return style;
}
}
}