├── .gitignore ├── AutoSelect.cs ├── AutoSelect.cs.meta ├── Editor.meta ├── Editor ├── Common.cs ├── Common.cs.meta ├── EditorAutoSelect.cs ├── EditorAutoSelect.cs.meta ├── EditorGUIHelper.cs ├── EditorGUIHelper.cs.meta ├── HorizontalPanes.cs ├── HorizontalPanes.cs.meta ├── Toolbar.cs ├── Toolbar.cs.meta ├── VerticalPanes.cs └── VerticalPanes.cs.meta ├── GUIHelper.cs ├── GUIHelper.cs.meta ├── LICENSE ├── LICENSE.meta ├── README.md └── README.md.meta /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /AutoSelect.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | // Extensions in the spirit/style of GUI.* 4 | public static class GUIAutoSelect { 5 | public static string TextArea(string name, Rect pos, string text) { 6 | CoreAutoSelect.Pre(name); 7 | string tmp = GUI.TextArea(pos, text); 8 | CoreAutoSelect.Post(name); 9 | return tmp; 10 | } 11 | 12 | public static string TextArea(string name, Rect pos, string text, int maxLength) { 13 | CoreAutoSelect.Pre(name); 14 | string tmp = GUI.TextArea(pos, text, maxLength); 15 | CoreAutoSelect.Post(name); 16 | return tmp; 17 | } 18 | 19 | public static string TextArea(string name, Rect pos, string text, GUIStyle style) { 20 | CoreAutoSelect.Pre(name); 21 | string tmp = GUI.TextArea(pos, text, style); 22 | CoreAutoSelect.Post(name); 23 | return tmp; 24 | } 25 | 26 | public static string TextArea(string name, Rect pos, string text, int maxLength, GUIStyle style) { 27 | CoreAutoSelect.Pre(name); 28 | string tmp = GUI.TextArea(pos, text, maxLength, style); 29 | CoreAutoSelect.Post(name); 30 | return tmp; 31 | } 32 | } 33 | 34 | // Extensions in the spirit/style of GUILayout.* 35 | public static class GUILayoutAutoSelect { 36 | public static string TextArea(string name, string text, params GUILayoutOption[] options) { 37 | CoreAutoSelect.Pre(name); 38 | string tmp = GUILayout.TextArea(text, options); 39 | CoreAutoSelect.Post(name); 40 | return tmp; 41 | } 42 | 43 | public static string TextArea(string name, string text, int maxLength, params GUILayoutOption[] options) { 44 | CoreAutoSelect.Pre(name); 45 | string tmp = GUILayout.TextArea(text, maxLength, options); 46 | CoreAutoSelect.Post(name); 47 | return tmp; 48 | } 49 | 50 | public static string TextArea(string name, string text, GUIStyle style, params GUILayoutOption[] options) { 51 | CoreAutoSelect.Pre(name); 52 | string tmp = GUILayout.TextArea(text, style, options); 53 | CoreAutoSelect.Post(name); 54 | return tmp; 55 | } 56 | 57 | public static string TextArea(string name, string text, int maxLength, GUIStyle style, params GUILayoutOption[] options) { 58 | CoreAutoSelect.Pre(name); 59 | string tmp = GUILayout.TextArea(text, maxLength, style, options); 60 | CoreAutoSelect.Post(name); 61 | return tmp; 62 | } 63 | } 64 | 65 | // Helper/support stuff. 66 | public static class CoreAutoSelect { 67 | // Internal gubbins for auto-select controls. 68 | public static string lastFocusedControl = null; 69 | 70 | public static void Pre(string name) { 71 | // Each widget needs a unique name so we can differentiate them. 72 | GUI.SetNextControlName(name); 73 | } 74 | 75 | public static void Post(string name) { 76 | // And now, the magic: 77 | // Check to see if keyboard focus has changed on us... 78 | string focusedControl = GUI.GetNameOfFocusedControl(); 79 | if(lastFocusedControl != focusedControl) { 80 | // It has! Now, check to see if the focused control is this text area... 81 | if(focusedControl == name) { 82 | // It is! Now, get the editor state (spooky voodo!), and tweak it. 83 | TextEditor t = (TextEditor)GUIUtility.GetStateObject(typeof(TextEditor), GUIUtility.keyboardControl); 84 | t.SelectAll(); 85 | 86 | // Update this here or state gets mangled when there's multiple 87 | // AutoSelectTextArea objects and you switch between them. 88 | lastFocusedControl = focusedControl; 89 | } else if(focusedControl == "") { 90 | // Update this here or switching back and forth between normal TextArea 91 | // and AutoSelectTextArea widgets will have problems. 92 | lastFocusedControl = focusedControl; 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /AutoSelect.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ba405b57223084df1ac891cae7bdd5a4 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 61ac0280ab9814f1da3030d665f8e284 3 | -------------------------------------------------------------------------------- /Editor/Common.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | // Helper/support stuff. 4 | internal static class EditorGUICommon { 5 | internal static string ClampLength(string str, int maxLength) { 6 | if(!String.IsNullOrEmpty(str) && str.Length > maxLength) 7 | str = str.Substring(0, maxLength); 8 | return str; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Editor/Common.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71cfc5d1da28f457e9abdf4b61f46b6e 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor/EditorAutoSelect.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | // Extensions in the spirit/style of EditorGUI.* 5 | public static class EditorGUIAutoSelect { 6 | public static string TextArea(string name, Rect pos, string text) { 7 | CoreAutoSelect.Pre(name); 8 | string tmp = EditorGUI.TextArea(pos, text); 9 | CoreAutoSelect.Post(name); 10 | return tmp; 11 | } 12 | 13 | public static string TextArea(string name, Rect pos, string text, int maxLength) { 14 | CoreAutoSelect.Pre(name); 15 | string tmp = EditorGUICommon.ClampLength(EditorGUI.TextArea(pos, text), maxLength); 16 | CoreAutoSelect.Post(name); 17 | return tmp; 18 | } 19 | 20 | public static string TextArea(string name, Rect pos, string text, GUIStyle style) { 21 | CoreAutoSelect.Pre(name); 22 | string tmp = EditorGUI.TextArea(pos, text, style); 23 | CoreAutoSelect.Post(name); 24 | return tmp; 25 | } 26 | 27 | public static string TextArea(string name, Rect pos, string text, int maxLength, GUIStyle style) { 28 | CoreAutoSelect.Pre(name); 29 | string tmp = EditorGUICommon.ClampLength(EditorGUI.TextArea(pos, text, style), maxLength); 30 | CoreAutoSelect.Post(name); 31 | return tmp; 32 | } 33 | } 34 | 35 | // Extensions in the spirit/style of EditorGUILayout.* 36 | public static class EditorGUILayoutAutoSelect { 37 | public static string TextArea(string name, string text, params GUILayoutOption[] options) { 38 | CoreAutoSelect.Pre(name); 39 | string tmp = EditorGUILayout.TextArea(text, options); 40 | CoreAutoSelect.Post(name); 41 | return tmp; 42 | } 43 | 44 | public static string TextArea(string name, string text, int maxLength, params GUILayoutOption[] options) { 45 | CoreAutoSelect.Pre(name); 46 | string tmp = EditorGUICommon.ClampLength(EditorGUILayout.TextArea(text, options), maxLength); 47 | CoreAutoSelect.Post(name); 48 | return tmp; 49 | } 50 | 51 | public static string TextArea(string name, string text, GUIStyle style, params GUILayoutOption[] options) { 52 | CoreAutoSelect.Pre(name); 53 | string tmp = EditorGUILayout.TextArea(text, style, options); 54 | CoreAutoSelect.Post(name); 55 | return tmp; 56 | } 57 | 58 | public static string TextArea(string name, string text, int maxLength, GUIStyle style, params GUILayoutOption[] options) { 59 | CoreAutoSelect.Pre(name); 60 | string tmp = EditorGUICommon.ClampLength(EditorGUILayout.TextArea(text, style, options), maxLength); 61 | CoreAutoSelect.Post(name); 62 | return tmp; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Editor/EditorAutoSelect.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4b70f526b8eda424da9954d9056ae3fd 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor/EditorGUIHelper.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | public static class EditorGUIStyleExtensions { 5 | private static bool wasProSkin = EditorGUIUtility.isProSkin; 6 | 7 | public static void InvalidateOnSkinChange(GUIStyle[] styles) { 8 | if(EditorGUIUtility.isProSkin != wasProSkin) { 9 | wasProSkin = EditorGUIUtility.isProSkin; 10 | for(int i = 0; i < styles.Length; i++) 11 | styles[i] = null; 12 | } 13 | } 14 | 15 | public static GUIStyle BaseTextColor(this GUIStyle style, Color normalSkin, Color proSkin) { 16 | // *INDENT-OFF* 17 | style.normal.textColor = 18 | style.active.textColor = 19 | style.hover.textColor = 20 | style.focused.textColor = 21 | style.onNormal.textColor = 22 | style.onActive.textColor = 23 | style.onHover.textColor = 24 | style.onFocused.textColor = 25 | EditorGUIUtility.isProSkin ? proSkin : normalSkin; 26 | // *INDENT-ON* 27 | return style; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Editor/EditorGUIHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5ac219a8eb2ff4cea8ce83549ab1cb2c 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor/HorizontalPanes.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | public class HorizontalPaneState { 5 | public const int SPLITTER_WIDTH = 9; 6 | public int id = 0; 7 | public bool isDraggingSplitter = false, 8 | isPaneWidthChanged = false; 9 | public float leftPaneWidth = -1, 10 | initialLeftPaneWidth = -1, 11 | lastAvailableWidth = -1, 12 | availableWidth = 0, 13 | minPaneWidthLeft = 75, 14 | minPaneWidthRight = 75; 15 | 16 | /* 17 | * Unity can, apparently, recycle state objects. In that event we want to 18 | * wipe the slate clean and just start over to avoid wackiness. 19 | */ 20 | protected virtual void Reset(int newId) { 21 | id = newId; 22 | isDraggingSplitter = false; 23 | isPaneWidthChanged = false; 24 | leftPaneWidth = -1; 25 | initialLeftPaneWidth = -1; 26 | lastAvailableWidth = -1; 27 | availableWidth = 0; 28 | minPaneWidthLeft = 75; 29 | minPaneWidthRight = 75; 30 | } 31 | 32 | /* 33 | * Some aspects of our state are really just static configuration that 34 | * shouldn't be modified by the control, so we blindly set them if we have a 35 | * prototype from which to do so. 36 | */ 37 | protected virtual void InitFromPrototype(int newId, HorizontalPaneState prototype) { 38 | id = newId; 39 | initialLeftPaneWidth = prototype.initialLeftPaneWidth; 40 | minPaneWidthLeft = prototype.minPaneWidthLeft; 41 | minPaneWidthRight = prototype.minPaneWidthRight; 42 | } 43 | 44 | /* 45 | * This method takes care of guarding against state object recycling, and 46 | * ensures we pick up what we need, when we need to, from the prototype state 47 | * object. 48 | */ 49 | public void ResolveStateToCurrentContext(int currentId, HorizontalPaneState prototype) { 50 | if(id != currentId) 51 | Reset(currentId); 52 | else if(prototype != null) 53 | InitFromPrototype(currentId, prototype); 54 | } 55 | } 56 | 57 | public static class EditorGUILayoutHorizontalPanes { 58 | // TODO: This makes it impossible to nest pane sets! 59 | private static HorizontalPaneState hState; 60 | 61 | public static void Begin() { Begin(null); } 62 | 63 | public static void Begin(HorizontalPaneState prototype) { 64 | int id = GUIUtility.GetControlID(FocusType.Passive); 65 | hState = (HorizontalPaneState)GUIUtility.GetStateObject(typeof(HorizontalPaneState), id); 66 | hState.ResolveStateToCurrentContext(id, prototype); 67 | 68 | // *INDENT-OFF* 69 | Rect totalArea = EditorGUILayout.BeginHorizontal(); 70 | hState.availableWidth = totalArea.width - HorizontalPaneState.SPLITTER_WIDTH; 71 | hState.isPaneWidthChanged = false; 72 | if(totalArea.width > 0) { 73 | if(hState.leftPaneWidth < 0) { 74 | if(hState.initialLeftPaneWidth < 0) 75 | hState.leftPaneWidth = hState.availableWidth * 0.5f; 76 | else 77 | hState.leftPaneWidth = hState.initialLeftPaneWidth; 78 | hState.isPaneWidthChanged = true; 79 | } 80 | if(hState.lastAvailableWidth < 0) 81 | hState.lastAvailableWidth = hState.availableWidth; 82 | if(hState.lastAvailableWidth != hState.availableWidth) { 83 | hState.leftPaneWidth = hState.availableWidth * (hState.leftPaneWidth / hState.lastAvailableWidth); 84 | hState.isPaneWidthChanged = true; 85 | } 86 | hState.lastAvailableWidth = hState.availableWidth; 87 | } 88 | 89 | GUILayout.BeginHorizontal(GUILayout.Width(hState.leftPaneWidth)); 90 | // *INDENT-ON* 91 | } 92 | 93 | public static void Splitter() { 94 | GUILayout.EndHorizontal(); 95 | 96 | float availableWidthForOnePanel = hState.availableWidth - (1 + hState.minPaneWidthRight); 97 | Rect drawableSplitterArea = GUILayoutUtility.GetRect(GUIHelper.NoContent, HorizontalPaneStyles.Splitter, GUILayout.Width(1f), GUIHelper.ExpandHeight); 98 | Rect splitterArea = new Rect(drawableSplitterArea.xMin - (int)(HorizontalPaneState.SPLITTER_WIDTH * 0.5f), drawableSplitterArea.yMin, HorizontalPaneState.SPLITTER_WIDTH, drawableSplitterArea.height); 99 | switch(Event.current.type) { 100 | case EventType.MouseDown: 101 | if(splitterArea.Contains(Event.current.mousePosition)) { 102 | hState.isDraggingSplitter = true; 103 | GUIUtility.hotControl = hState.id; 104 | Event.current.Use(); 105 | } 106 | break; 107 | case EventType.MouseDrag: 108 | if(hState.isDraggingSplitter && hState.id == GUIUtility.hotControl) { 109 | hState.leftPaneWidth += Event.current.delta.x; 110 | hState.leftPaneWidth = Mathf.Round(hState.leftPaneWidth); 111 | hState.isPaneWidthChanged = true; 112 | Event.current.Use(); 113 | } 114 | break; 115 | case EventType.MouseUp: 116 | hState.isDraggingSplitter = false; 117 | if(hState.id == GUIUtility.hotControl) { 118 | GUIUtility.hotControl = 0; 119 | Event.current.Use(); 120 | } 121 | break; 122 | } 123 | 124 | if(hState.isPaneWidthChanged) { 125 | if(hState.leftPaneWidth < hState.minPaneWidthLeft) 126 | hState.leftPaneWidth = hState.minPaneWidthLeft; 127 | if(hState.leftPaneWidth >= availableWidthForOnePanel) 128 | hState.leftPaneWidth = availableWidthForOnePanel; 129 | if(EditorWindow.focusedWindow != null) 130 | EditorWindow.focusedWindow.Repaint(); 131 | } 132 | GUI.Label(drawableSplitterArea, GUIHelper.NoContent, HorizontalPaneStyles.Splitter); 133 | EditorGUIUtility.AddCursorRect(splitterArea, MouseCursor.ResizeHorizontal); 134 | } 135 | 136 | public static void End() { EditorGUILayout.EndHorizontal(); } 137 | } 138 | 139 | public static class HorizontalPaneStyles { 140 | private static Texture2D SplitterImage; 141 | 142 | static HorizontalPaneStyles() { 143 | // TODO: Change the image color based on chosen editor skin. 144 | SplitterImage = new Texture2D(1, 1, TextureFormat.ARGB32, false) { 145 | hideFlags = HideFlags.HideAndDontSave, 146 | anisoLevel = 0, 147 | filterMode = FilterMode.Point, 148 | wrapMode = TextureWrapMode.Clamp 149 | }; 150 | SplitterImage.SetPixels(new Color[] { Color.gray }); 151 | SplitterImage.Apply(); 152 | } 153 | 154 | private static GUIStyle _Splitter = null; 155 | 156 | public static GUIStyle Splitter { 157 | get { 158 | if(_Splitter == null) { 159 | // *INDENT-OFF* 160 | _Splitter = new GUIStyle() { 161 | normal = new GUIStyleState() { background = SplitterImage }, 162 | imagePosition = ImagePosition.ImageOnly, 163 | wordWrap = false, 164 | alignment = TextAnchor.MiddleCenter, 165 | } 166 | .Named("HSplitter") 167 | .Size(1, 0, false, true) 168 | .ResetBoxModel() 169 | .Margin(3, 3, 0, 0) 170 | .ClipText(); 171 | // *INDENT-ON* 172 | } 173 | return _Splitter; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Editor/HorizontalPanes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: edd248795bb144eaeac770b1e219742d 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor/Toolbar.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | // Extensions in the spirit/style of GUILayout.* 5 | public static class EditorGUILayoutToolbar { 6 | public const int BUTTON_SPACE = 6; 7 | 8 | public static void Space() { GUILayout.Space(BUTTON_SPACE); } 9 | public static void FlexibleSpace() { GUILayout.Label(GUIHelper.NoContent, GUIHelper.NoStyle, GUIHelper.ExpandWidth); } 10 | public static void Begin() { GUILayout.BeginHorizontal(EditorStyles.toolbar, GUIHelper.ExpandWidth); } 11 | public static void End() { GUILayout.EndHorizontal(); } 12 | } 13 | -------------------------------------------------------------------------------- /Editor/Toolbar.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 976253540cd8f404fa8469b2ffd972d7 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /Editor/VerticalPanes.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | public class VerticalPaneState { 5 | public const int SPLITTER_HEIGHT = 9; 6 | 7 | public int id = 0; 8 | public bool isDraggingSplitter = false, 9 | isPaneHeightChanged = false; 10 | public float topPaneHeight = -1, 11 | initialTopPaneHeight = -1, 12 | lastAvailableHeight = -1, 13 | availableHeight = 0, 14 | minPaneHeightTop = 75, 15 | minPaneHeightBottom = 75; 16 | 17 | /* 18 | * Unity can, apparently, recycle state objects. In that event we want to 19 | * wipe the slate clean and just start over to avoid wackiness. 20 | */ 21 | protected virtual void Reset(int newId) { 22 | id = newId; 23 | isDraggingSplitter = false; 24 | isPaneHeightChanged = false; 25 | topPaneHeight = -1; 26 | initialTopPaneHeight = -1; 27 | lastAvailableHeight = -1; 28 | availableHeight = 0; 29 | minPaneHeightTop = 75; 30 | minPaneHeightBottom = 75; 31 | } 32 | 33 | /* 34 | * Some aspects of our state are really just static configuration that 35 | * shouldn't be modified by the control, so we blindly set them if we have a 36 | * prototype from which to do so. 37 | */ 38 | protected virtual void InitFromPrototype(int newId, VerticalPaneState prototype) { 39 | id = newId; 40 | initialTopPaneHeight = prototype.initialTopPaneHeight; 41 | minPaneHeightTop = prototype.minPaneHeightTop; 42 | minPaneHeightBottom = prototype.minPaneHeightBottom; 43 | } 44 | 45 | /* 46 | * This method takes care of guarding against state object recycling, and 47 | * ensures we pick up what we need, when we need to, from the prototype state 48 | * object. 49 | */ 50 | public void ResolveStateToCurrentContext(int currentId, VerticalPaneState prototype) { 51 | if(id != currentId) 52 | Reset(currentId); 53 | else if(prototype != null) 54 | InitFromPrototype(currentId, prototype); 55 | } 56 | } 57 | 58 | public static class EditorGUILayoutVerticalPanes { 59 | // TODO: This makes it impossible to nest pane sets! 60 | private static VerticalPaneState vState; 61 | 62 | public static void Begin() { Begin(null); } 63 | 64 | public static void Begin(VerticalPaneState prototype) { 65 | int id = GUIUtility.GetControlID(FocusType.Passive); 66 | vState = (VerticalPaneState)GUIUtility.GetStateObject(typeof(VerticalPaneState), id); 67 | vState.ResolveStateToCurrentContext(id, prototype); 68 | 69 | // *INDENT-OFF* 70 | Rect totalArea = EditorGUILayout.BeginVertical(); 71 | vState.availableHeight = totalArea.height - VerticalPaneState.SPLITTER_HEIGHT; 72 | vState.isPaneHeightChanged = false; 73 | if(totalArea.height > 0) { 74 | if(vState.topPaneHeight < 0) { 75 | if(vState.initialTopPaneHeight < 0) 76 | vState.topPaneHeight = vState.availableHeight * 0.5f; 77 | else 78 | vState.topPaneHeight = vState.initialTopPaneHeight; 79 | vState.isPaneHeightChanged = true; 80 | } 81 | if(vState.lastAvailableHeight < 0) 82 | vState.lastAvailableHeight = vState.availableHeight; 83 | if(vState.lastAvailableHeight != vState.availableHeight) { 84 | vState.topPaneHeight = vState.availableHeight * (vState.topPaneHeight / vState.lastAvailableHeight); 85 | vState.isPaneHeightChanged = true; 86 | } 87 | vState.lastAvailableHeight = vState.availableHeight; 88 | } 89 | 90 | GUILayout.BeginVertical(GUILayout.Height(vState.topPaneHeight)); 91 | // *INDENT-ON* 92 | } 93 | 94 | public static void Splitter() { 95 | GUILayout.EndVertical(); 96 | 97 | float availableHeightForOnePanel = vState.availableHeight - (1 + vState.minPaneHeightBottom); 98 | Rect drawableSplitterArea = GUILayoutUtility.GetRect(GUIHelper.NoContent, VerticalPaneStyles.Splitter, GUILayout.Height(1f), GUIHelper.ExpandWidth); 99 | Rect splitterArea = new Rect(drawableSplitterArea.xMin, drawableSplitterArea.yMin - (int)(VerticalPaneState.SPLITTER_HEIGHT * 0.5f), drawableSplitterArea.width, VerticalPaneState.SPLITTER_HEIGHT); 100 | switch(Event.current.type) { 101 | case EventType.MouseDown: 102 | if(splitterArea.Contains(Event.current.mousePosition)) { 103 | vState.isDraggingSplitter = true; 104 | GUIUtility.hotControl = vState.id; 105 | Event.current.Use(); 106 | } 107 | break; 108 | case EventType.MouseDrag: 109 | if(vState.isDraggingSplitter && vState.id == GUIUtility.hotControl) { 110 | vState.topPaneHeight += Event.current.delta.y; 111 | vState.topPaneHeight = Mathf.Round(vState.topPaneHeight); 112 | vState.isPaneHeightChanged = true; 113 | Event.current.Use(); 114 | } 115 | break; 116 | case EventType.MouseUp: 117 | vState.isDraggingSplitter = false; 118 | if(vState.id == GUIUtility.hotControl) { 119 | GUIUtility.hotControl = 0; 120 | Event.current.Use(); 121 | } 122 | break; 123 | } 124 | 125 | if(vState.isPaneHeightChanged) { 126 | if(vState.topPaneHeight < vState.minPaneHeightTop) 127 | vState.topPaneHeight = vState.minPaneHeightTop; 128 | if(vState.topPaneHeight >= availableHeightForOnePanel) 129 | vState.topPaneHeight = availableHeightForOnePanel; 130 | if(EditorWindow.focusedWindow != null) 131 | EditorWindow.focusedWindow.Repaint(); 132 | } 133 | GUI.Label(drawableSplitterArea, GUIHelper.NoContent, VerticalPaneStyles.Splitter); 134 | EditorGUIUtility.AddCursorRect(splitterArea, MouseCursor.ResizeVertical); 135 | } 136 | 137 | public static void End() { EditorGUILayout.EndVertical(); } 138 | } 139 | 140 | public static class VerticalPaneStyles { 141 | private static Texture2D SplitterImage; 142 | 143 | static VerticalPaneStyles() { 144 | // TODO: Change the image color based on chosen editor skin. 145 | SplitterImage = new Texture2D(1, 1, TextureFormat.ARGB32, false) { 146 | hideFlags = HideFlags.HideAndDontSave, 147 | anisoLevel = 0, 148 | filterMode = FilterMode.Point, 149 | wrapMode = TextureWrapMode.Clamp 150 | }; 151 | SplitterImage.SetPixels(new Color[] { Color.gray }); 152 | SplitterImage.Apply(); 153 | } 154 | 155 | private static GUIStyle _Splitter = null; 156 | 157 | public static GUIStyle Splitter { 158 | get { 159 | if(_Splitter == null) { 160 | // *INDENT-OFF* 161 | _Splitter = new GUIStyle() { 162 | normal = new GUIStyleState() { background = SplitterImage }, 163 | imagePosition = ImagePosition.ImageOnly, 164 | wordWrap = false, 165 | alignment = TextAnchor.MiddleCenter 166 | } 167 | .Named("VSplitter") 168 | .Size(0, 1, true, false) 169 | .ResetBoxModel() 170 | .Margin(0, 0, 3, 3) 171 | .ClipText(); 172 | // *INDENT-ON* 173 | } 174 | return _Splitter; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Editor/VerticalPanes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de9e6d406d0f54f35aad35f352036242 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /GUIHelper.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | public static class GUIHelper { 4 | // Don't create new instances every time we need an option -- just pre-create the permutations: 5 | public static GUILayoutOption ExpandWidth = GUILayout.ExpandWidth(true), 6 | NoExpandWidth = GUILayout.ExpandWidth(false), 7 | ExpandHeight = GUILayout.ExpandHeight(true), 8 | NoExpandHeight = GUILayout.ExpandHeight(false); 9 | 10 | // Provided for consistency of interface, but not actually a savings/win: 11 | public static GUILayoutOption Width(float w) { return GUILayout.Width(w); } 12 | 13 | // Again, don't create instances when we don't need to: 14 | public static GUIStyle NoStyle = GUIStyle.none; 15 | public static GUIContent NoContent = GUIContent.none; 16 | } 17 | 18 | public static class GUIStyleExtensions { 19 | public static GUIStyle NoBackgroundImages(this GUIStyle style) { 20 | style.normal.background = 21 | style.active.background = 22 | style.hover.background = 23 | style.focused.background = 24 | style.onNormal.background = 25 | style.onActive.background = 26 | style.onHover.background = 27 | style.onFocused.background = 28 | null; 29 | return style; 30 | } 31 | 32 | public static GUIStyle BaseTextColor(this GUIStyle style, Color c) { 33 | // *INDENT-OFF* 34 | style.normal.textColor = 35 | style.active.textColor = 36 | style.hover.textColor = 37 | style.focused.textColor = 38 | style.onNormal.textColor = 39 | style.onActive.textColor = 40 | style.onHover.textColor = 41 | style.onFocused.textColor = 42 | c; 43 | // *INDENT-ON* 44 | return style; 45 | } 46 | 47 | public static GUIStyle ResetBoxModel(this GUIStyle style) { 48 | style.border = new RectOffset(); 49 | style.margin = new RectOffset(); 50 | style.padding = new RectOffset(); 51 | style.overflow = new RectOffset(); 52 | style.contentOffset = Vector2.zero; 53 | 54 | return style; 55 | } 56 | 57 | public static GUIStyle Padding(this GUIStyle style, int left, int right, int top, int bottom) { 58 | style.padding = new RectOffset(left, right, top, bottom); 59 | 60 | return style; 61 | } 62 | 63 | public static GUIStyle Margin(this GUIStyle style, int left, int right, int top, int bottom) { 64 | style.margin = new RectOffset(left, right, top, bottom); 65 | 66 | return style; 67 | } 68 | 69 | public static GUIStyle Border(this GUIStyle style, int left, int right, int top, int bottom) { 70 | style.border = new RectOffset(left, right, top, bottom); 71 | 72 | return style; 73 | } 74 | 75 | public static GUIStyle Named(this GUIStyle style, string name) { 76 | style.name = name; 77 | 78 | return style; 79 | } 80 | 81 | public static GUIStyle ClipText(this GUIStyle style) { 82 | style.clipping = TextClipping.Clip; 83 | 84 | return style; 85 | } 86 | 87 | public static GUIStyle Size(this GUIStyle style, int width, int height, bool stretchWidth, bool stretchHeight) { 88 | style.fixedWidth = width; 89 | style.fixedHeight = height; 90 | style.stretchWidth = stretchWidth; 91 | style.stretchHeight = stretchHeight; 92 | 93 | return style; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /GUIHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6608241b783de4e12a5ca986ff062adc 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2010-2015 Jon Frisby 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b4ea0c679ea35432a963418ef5244c29 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityGUIExtensions 2 | 3 | This package provides you with a set of classes providing extended GUI widgets 4 | for Unity -- both for runtime usage and for editor usage -- 5 | 6 | Widgets (including where they can be used from): 7 | 8 | * AutoSelect TextArea (_Runtime & Editor_) - A variant of the TextArea that 9 | auto-selects all content when the widget gets focus, and which provides 10 | length-clamping for the Editor variant. 11 | 12 | * Toolbar (_Editor_) - Some helpers for drawing toolbars in the same manner Unity 13 | itself does. 14 | 15 | * Vertical Split-Panes (_Editor_) - Vertically stacked split-panes with draggable 16 | resizing. 17 | 18 | * Horizontal Split-Panes (_Editor_) - Horizontally stacked split-panes with 19 | draggable resizing. 20 | 21 | 22 | ## Requirements 23 | 24 | * Unity 3.1 or 3.0. This will not work on Unity 2.x, or Unity/iPhone 1.x. 25 | 26 | 27 | ## Install 28 | 29 | Grab the unitypackage and install it into your project: 30 | 31 | http://github.com/MrJoy/UnityGUIExtensions.Examples/downloads 32 | 33 | OR, if you are using Git for your Unity project, you can add this as a sub-module: 34 | 35 | mkdir -p Assets/Editor/ 36 | git submodule add git://github.com/MrJoy/UnityGUIExtensions.git Assets/Editor/UnityGUIExtensions 37 | git submodule init 38 | git submodule update 39 | 40 | 41 | ## Source 42 | 43 | UnityGUIExtensions' Git repo is available on GitHub, which can be browsed at: 44 | 45 | http://github.com/MrJoy/UnityGUIExtensions 46 | 47 | and cloned with: 48 | 49 | git clone git://github.com/MrJoy/UnityGUIExtensions.git 50 | 51 | 52 | An example Unity project (which refers to this project as a sub-module) is 53 | available here: 54 | 55 | http://github.com/MrJoy/UnityGUIExtensions.Examples 56 | 57 | and can be cloned with: 58 | 59 | git clone git://github.com/MrJoy/UnityGUIExtensions.Examples.git 60 | git submodule init 61 | git submodule update 62 | 63 | 64 | ## Usage 65 | 66 | ### AutoSelect TextArea 67 | 68 | Instead of: 69 | 70 | myString = GUILayout.TextArea(myString); 71 | 72 | You just do: 73 | 74 | myString = GUILayoutAutoSelect.TextArea("uniqueWidgetName", myString); 75 | 76 | Variants exist corresponding to all the method signatures of GUI.TextArea, 77 | GUILayout.TextArea, EditorGUI.TextArea, and EditorGUILayout.TextArea. 78 | Additionally, the EditorGUIExt/EditorGUILayoutExt versions have an additional 79 | set of signatures that include the maxLength attribute. 80 | 81 | ### Vertical Split-Panes 82 | 83 | _This feature may only be used from editor classes_ 84 | 85 | Simplest possible usage: 86 | 87 | public void OnGUI() { 88 | EditorGUILayoutVerticalPanes.Begin(); 89 | // Draw your upper pane here. 90 | EditorGUILayoutVerticalPanes.Splitter(); 91 | // Draw your lower pane here. 92 | EditorGUILayoutVerticalPanes.End(); 93 | } 94 | 95 | However, if you wish to exert more control over this, you can specify a 96 | configuration for the panes: 97 | 98 | private VerticalPaneState paneConfiguration = new VerticalPaneState() { 99 | initialTopPaneHeight = 80, 100 | minPaneHeightTop = 65, 101 | minPaneHeightBottom = 100 102 | }; 103 | 104 | public void OnGUI() { 105 | EditorGUILayoutVerticalPanes.Begin(paneConfiguration); 106 | // Draw your upper pane here. 107 | EditorGUILayoutVerticalPanes.Splitter(); 108 | // Draw your lower pane here. 109 | EditorGUILayoutVerticalPanes.End(); 110 | } 111 | 112 | ### Horizontal Split-Panes 113 | 114 | _This feature may only be used from editor classes_ 115 | 116 | Simplest possible usage: 117 | 118 | public void OnGUI() { 119 | EditorGUILayoutHorizontalPanes.Begin(); 120 | // Draw your left pane here. 121 | EditorGUILayoutHorizontalPanes.Splitter(); 122 | // Draw your right pane here. 123 | EditorGUILayoutHorizontalPanes.End(); 124 | } 125 | 126 | However, if you wish to exert more control over this, you can specify a 127 | configuration for the panes: 128 | 129 | private HorizontalPaneState paneConfiguration = new HorizontalPaneState() { 130 | initialLeftPaneWidth = 80, 131 | minPaneWidthLeft = 65, 132 | minPaneWidthRight = 100 133 | }; 134 | 135 | public void OnGUI() { 136 | EditorGUILayoutHorizontalPanes.Begin(paneConfiguration); 137 | // Draw your left pane here. 138 | EditorGUILayoutHorizontalPanes.Splitter(); 139 | // Draw your right pane here. 140 | EditorGUILayoutHorizontalPanes.End(); 141 | } 142 | 143 | 144 | ### Toolbars 145 | 146 | _This feature may only be used from editor classes_ 147 | 148 | (Documentation coming soon.) 149 | 150 | 151 | ## License 152 | 153 | Copyright (c) 2010-2015 Jon Frisby 154 | 155 | Dual licensed under the terms of the MIT X11 or GNU GPL. 156 | 157 | 158 | ## Contributing 159 | 160 | If you'd like to contribute to UnityGUIExtensions, I ask that you fork 161 | MrJoy/UnityGUIExtensions on GitHub, and push up a topic branch for each feature 162 | you add or bug you fix. Then create a pull request and explain what your code 163 | does. This allows us to discuss and merge each change separately. 164 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 982d6acb99e6e42b88778db1672bb7e6 3 | --------------------------------------------------------------------------------