前言
分享一个大佬写的 Splitter 控件, 主要是实现了和 Unity 原生 Console 窗口一样的效果, 有一条可以拖动的分割线, 用于将一个窗口分割成两部分.
这就是最终实现的效果, 看一下是否满足你的需要, 如果正好满足, 那你就可以继续往下看了.
![Splitter]()
代码结构
主要由一个公共基类和一个自定义的窗口类构成.
基类: Splitter
基类主要负责的是控制分割条的拖动, 以及重新计算拖动分割条之后主区域和子区域的尺寸.
下面是源码, 本质就只是一个 OnGUI 方法.
| 12
 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.
下面是动图中横向分割窗口的源码:
| 12
 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
 };
 }
 }
 }
 
 | 
下面是动图中纵向分割窗口的源码:
| 12
 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
 };
 }
 }
 }
 
 | 
应用
最后再分享一个我实际项目中的应用:
![Asset Check Tool]()
参考链接
Console Window で利用されているような Splitter を作る