前言
分享一个大佬写的 Splitter 控件, 主要是实现了和 Unity 原生 Console 窗口一样的效果, 有一条可以拖动的分割线, 用于将一个窗口分割成两部分.
这就是最终实现的效果, 看一下是否满足你的需要, 如果正好满足, 那你就可以继续往下看了.
代码结构
主要由一个公共基类和一个自定义的窗口类构成.
基类: Splitter
基类主要负责的是控制分割条的拖动, 以及重新计算拖动分割条之后主区域和子区域的尺寸.
下面是源码, 本质就只是一个 OnGUI 方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
| using UnityEngine; using UnityEditor; using System;
namespace Kuroha.GUI.Editor.Splitter { [Serializable] public abstract class Splitter { internal enum SplitMode { Horizontal, Vertical }
private EditorWindow editorWindow; private MouseCursor mouseCursor; private SplitMode splitMode; private float lockSize; private bool isResizing; private bool isFreeze;
protected float barSize; protected float mainAreaSize;
private Rect mouseCursorRect;
private float mainAreaRatio = 0.5f;
private static readonly Color splitterColorPro = Color.black;
private static readonly Color splitterColorFree = Color.gray;
internal Splitter(EditorWindow window, SplitMode splitMode, float mainAreaSize, float minSize, float barSize, bool isFreeze) { editorWindow = window; this.mainAreaSize = mainAreaSize; this.splitMode = splitMode; lockSize = minSize; this.barSize = barSize; this.isFreeze = isFreeze; mouseCursor = this.splitMode == SplitMode.Vertical ? MouseCursor.ResizeHorizontal : MouseCursor.ResizeVertical; }
protected abstract Rect MainRect(Rect rect);
protected abstract Rect SubRect(Rect rect);
protected abstract Rect BarRect(Rect rect);
protected abstract RectOffset BarRectOffset();
public void OnGUI(Rect windowRect, Action<Rect> mainGUI, Action<Rect> subGUI) { var current = Event.current;
mainGUI(MainRect(windowRect));
subGUI(SubRect(windowRect));
mouseCursorRect = BarRect(windowRect); EditorGUIUtility.AddCursorRect(mouseCursorRect, mouseCursor); if (isFreeze == false) { var clampMax = splitMode == SplitMode.Vertical ? windowRect.width - lockSize : windowRect.height - lockSize; var targetSplitterValue = splitMode == SplitMode.Vertical ? windowRect.width : windowRect.height; mainAreaRatio = splitMode == SplitMode.Vertical ? mainAreaSize / windowRect.width : mainAreaSize / windowRect.height; if (current.type == EventType.MouseDown) { if (mouseCursorRect.Contains(current.mousePosition)) { isResizing = true; } }
if (current.type == EventType.MouseUp) { isResizing = false; }
if (isResizing) { if (current.type == EventType.MouseDrag) { var targetValue = splitMode == SplitMode.Vertical ? current.mousePosition.x : current.mousePosition.y; var diffValue = splitMode == SplitMode.Vertical ? windowRect.width : windowRect.height; mainAreaRatio = targetValue / diffValue; } } else if (current.type != EventType.Layout && current.type != EventType.Used) { mainAreaRatio = targetSplitterValue * mainAreaRatio / targetSplitterValue; } mainAreaSize = Mathf.Clamp(targetSplitterValue * mainAreaRatio, lockSize, clampMax); } var color = EditorGUIUtility.isProSkin ? splitterColorPro : splitterColorFree; EditorGUI.DrawRect(BarRectOffset().Remove(mouseCursorRect), color);
if (isResizing) { editorWindow.Repaint(); } } } }
|
自定义窗口类
自定义窗口类就是我们实际构建的窗口, 它需要继承自基类 Splitter, 并且重写基类中的 4 个区域方法: MainRect
, SubRect
, BarRect
, BarRectOffset
.
下面是动图中横向分割窗口的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| using UnityEditor; using UnityEngine; using System;
namespace Kuroha.GUI.Editor.Splitter { [Serializable] public class HorizontalSplitter : Splitter { private static RectOffset barRectOffset;
private const int BAR_SIZE = 16; public HorizontalSplitter(EditorWindow window, float mainSize, float minSize, bool isFreeze) : base(window, SplitMode.Horizontal, mainSize, minSize, BAR_SIZE, isFreeze) { }
protected override RectOffset BarRectOffset() { return barRectOffset ??= new RectOffset(0, 0, 7, 8); }
protected override Rect MainRect(Rect rect) { return new Rect(rect) { x = 0, y = 0, height = mainAreaSize }; }
protected override Rect SubRect(Rect rect) { return new Rect(rect) { x = 0, y = mainAreaSize + 5, height = rect.height - mainAreaSize - 15 }; }
protected override Rect BarRect(Rect rect) { return new Rect(rect) { x = 0, y = mainAreaSize - barSize / 2, height = barSize }; } } }
|
下面是动图中纵向分割窗口的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| using System; using UnityEditor; using UnityEngine;
namespace Kuroha.GUI.Editor.Splitter { [Serializable] public class VerticalSplitter : Splitter { private static RectOffset barRectOffset; private const int BAR_SIZE = 16;
public VerticalSplitter(EditorWindow window, float mainSize, float minSize, bool isFreeze) : base(window, SplitMode.Vertical, mainSize, minSize, BAR_SIZE, isFreeze) { }
protected override RectOffset BarRectOffset() { return barRectOffset ??= new RectOffset(7, 8, 0, 0); }
protected override Rect MainRect(Rect rect) { return new Rect(rect) { x = 0, y = 0, width = mainAreaSize }; }
protected override Rect SubRect(Rect rect) { return new Rect(rect) { x = mainAreaSize + 5, y = 0, width = rect.width - mainAreaSize - 15 }; }
protected override Rect BarRect(Rect rect) { return new Rect(rect) { x = mainAreaSize - barSize / 2, y = 0, width = barSize }; } } }
|
应用
最后再分享一个我实际项目中的应用:
参考链接
Console Window で利用されているような Splitter を作る