├── .gitignore ├── AbstractLayout.cs ├── AbstractWidget.cs ├── Button.cs ├── Checkbox.cs ├── DateTimePicker.cs ├── DropdownBox.cs ├── EventBinding.cs ├── Examples ├── Assets │ ├── Example1.cs │ ├── Example1.cs.meta │ ├── Example2.cs │ ├── Example2.cs.meta │ ├── RSG.Toolkit.dll │ ├── RSG.Toolkit.dll.meta │ ├── UnityEditorUI_Editor.dll │ └── UnityEditorUI_Editor.dll.meta └── ProjectSettings │ ├── AudioManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── Physics2DSettings.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityAdsSettings.asset │ └── UnityAnalyticsManager.asset ├── GUI.cs ├── HorizontalLayout.cs ├── ILayout.cs ├── IWidget.cs ├── LICENSE ├── Label.cs ├── LayerPicker.cs ├── Properties └── AssemblyInfo.cs ├── PropertyBinding.cs ├── README.md ├── RootLayout.cs ├── Spacer.cs ├── TextBox.cs ├── UnityEditorUI.csproj ├── UnityEditorUI.sln ├── Vector3Field.cs ├── VerticalLayout.cs ├── dlls ├── UnityEditor.dll └── UnityEngine.dll ├── packages.config └── packages ├── RSG.Toolkit.1.0.0.0 ├── RSG.Toolkit.1.0.0.0.nupkg └── lib │ └── net35 │ └── RSG.Toolkit.dll └── repositories.config /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files 2 | .DS_Store 3 | .DS_Store? 4 | ehthumbs.db 5 | Thumbs.db 6 | desktop.ini 7 | 8 | # Visual studio user specific files 9 | *.suo 10 | *.user 11 | *.userosscache 12 | *.sln.docstates 13 | 14 | # MonoDevelop user specific files 15 | *.userprefs 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | build/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | 29 | .vs/ 30 | 31 | *.log 32 | *.orig 33 | 34 | # Unity 35 | **/Library/ 36 | **/Temp/ 37 | **/Builds/ 38 | Examples/*.sln 39 | Examples/*.csproj -------------------------------------------------------------------------------- /AbstractLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using RSG.Utils; 6 | 7 | namespace UnityEditorUI 8 | { 9 | /// 10 | /// Layouts are widgets that can contain other child widgets. All layouts should inherit from AbstractLayout. 11 | /// 12 | internal abstract class AbstractLayout : AbstractWidget, ILayout 13 | { 14 | protected bool enabled = true; 15 | 16 | private PropertyBinding enabledProperty; 17 | 18 | /// 19 | /// Whether or not to draw this layout and its sub-widgets (default is true). 20 | /// 21 | public IPropertyBinding Enabled { get { return enabledProperty; } } 22 | 23 | private List children = new List(); 24 | 25 | protected AbstractLayout(ILayout parent) : 26 | base(parent) 27 | { 28 | enabledProperty = new PropertyBinding( 29 | this, 30 | value => this.enabled = value 31 | ); 32 | } 33 | 34 | public override void OnGUI() 35 | { 36 | children.Each(child => child.OnGUI()); 37 | } 38 | 39 | public override void BindViewModel(object viewModel) 40 | { 41 | enabledProperty.BindViewModel(viewModel); 42 | 43 | children.Each(child => child.BindViewModel(viewModel)); 44 | } 45 | 46 | /// 47 | /// Creates a new button and adds it to the layout. 48 | /// 49 | public IButton Button() 50 | { 51 | var newButton = new Button(this); 52 | children.Add(newButton); 53 | return newButton; 54 | } 55 | 56 | /// 57 | /// Creates a new label and adds it to the view. 58 | /// 59 | public ILabel Label() 60 | { 61 | var newLabel = new Label(this); 62 | children.Add(newLabel); 63 | return newLabel; 64 | } 65 | 66 | /// 67 | /// Creates a new TextBox and adds it to the layout. 68 | /// 69 | public ITextBox TextBox() 70 | { 71 | var newTextBox = new TextBox(this); 72 | children.Add(newTextBox); 73 | return newTextBox; 74 | } 75 | 76 | /// 77 | /// Widget for choosing dates, similar do TextBox except with date validation built-in. 78 | /// 79 | public IDateTimePicker DateTimePicker() 80 | { 81 | var newDateTimePicker = new DateTimePicker(this); 82 | children.Add(newDateTimePicker); 83 | return newDateTimePicker; 84 | } 85 | 86 | /// 87 | /// Creates a new drop-down selection box and adds it to the layout. 88 | /// 89 | public IDropdownBox DropdownBox() 90 | { 91 | var newDropdownBox = new DropdownBox(this); 92 | children.Add(newDropdownBox); 93 | return newDropdownBox; 94 | } 95 | 96 | /// 97 | /// Creates a new checkbox and adds it to the layout. 98 | /// 99 | public ICheckbox Checkbox() 100 | { 101 | var newCheckbox = new Checkbox(this); 102 | children.Add(newCheckbox); 103 | return newCheckbox; 104 | } 105 | 106 | /// 107 | /// Creates a Vector3 field with X, Y and Z entry boxes. 108 | /// 109 | public IVector3Field Vector3Field() 110 | { 111 | var newVector3Field = new Vector3Field(this); 112 | children.Add(newVector3Field); 113 | return newVector3Field; 114 | } 115 | 116 | /// 117 | /// Creates a widget for editing layer masks. 118 | /// 119 | public ILayerPicker LayerPicker() 120 | { 121 | var newLayerPicker = new LayerPicker(this); 122 | children.Add(newLayerPicker); 123 | return newLayerPicker; 124 | } 125 | 126 | /// 127 | /// Inserts a space between other widgets. 128 | /// 129 | public ILayout Spacer() 130 | { 131 | var newSpacer = new Spacer(this); 132 | children.Add(newSpacer); 133 | return this; 134 | } 135 | 136 | /// 137 | /// Creates a VerticalLayout and adds it to this layout. 138 | /// 139 | public ILayout VerticalLayout() 140 | { 141 | var newLayout = new VerticalLayout(this); 142 | children.Add(newLayout); 143 | return newLayout; 144 | } 145 | 146 | /// 147 | /// Creates a horizontal layout and adds it to this layout. 148 | /// 149 | public ILayout HorizontalLayout() 150 | { 151 | var newLayout = new HorizontalLayout(this); 152 | children.Add(newLayout); 153 | return newLayout; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /AbstractWidget.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityEditorUI 7 | { 8 | /// 9 | /// Abstract class that all other widgets must implement. 10 | /// 11 | internal abstract class AbstractWidget : IWidget 12 | { 13 | /// 14 | /// Needed in order to get back to the parent via the End() method. 15 | /// 16 | private ILayout parent; 17 | 18 | /// 19 | /// Creates the widget and sets its parent. 20 | /// 21 | protected AbstractWidget(ILayout parent) 22 | { 23 | this.parent = parent; 24 | } 25 | 26 | /// 27 | /// Updates this widget and all children (if it is a layout) 28 | /// 29 | public abstract void OnGUI(); 30 | 31 | /// 32 | /// Binds the properties and events in this widget to corrosponding ones in the supplied view model. 33 | /// 34 | public abstract void BindViewModel(object viewModel); 35 | 36 | /// 37 | /// Fluent API for getting the layout containing this widget. 38 | /// 39 | public ILayout End() 40 | { 41 | return parent; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Button.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace UnityEditorUI 8 | { 9 | /// 10 | /// Clickable push button widget. 11 | /// 12 | public interface IButton : IWidget 13 | { 14 | /// 15 | /// Text to be displayed on the button. 16 | /// 17 | IPropertyBinding Text { get; } 18 | 19 | /// 20 | /// Tooltip displayed on mouse hover. 21 | /// 22 | IPropertyBinding Tooltip { get; } 23 | 24 | /// 25 | /// Width of the widget in pixels. Default uses auto-layout. 26 | /// 27 | IPropertyBinding Width { get; } 28 | 29 | /// 30 | /// Height of the widget in pixels. Default uses auto-layout. 31 | /// 32 | IPropertyBinding Height { get; } 33 | 34 | /// 35 | /// Event invoked when the button is clicked. 36 | /// 37 | IEventBinding Click { get; } 38 | } 39 | 40 | /// 41 | /// Clickable push button widget. 42 | /// 43 | internal class Button : AbstractWidget, IButton 44 | { 45 | // Private members 46 | private string text = String.Empty; 47 | private string tooltip = String.Empty; 48 | private int width = -1; 49 | private int height = -1; 50 | 51 | // Concrete property bindings 52 | private PropertyBinding textProperty; 53 | private PropertyBinding tooltipProperty; 54 | private PropertyBinding widthProperty; 55 | private PropertyBinding heightProperty; 56 | private EventBinding clickEvent; 57 | 58 | // Public interfaces for getting PropertyBindings 59 | public IPropertyBinding Text { get { return textProperty; } } 60 | public IPropertyBinding Tooltip { get { return tooltipProperty; } } 61 | public IPropertyBinding Width { get { return widthProperty; } } 62 | public IPropertyBinding Height { get { return heightProperty; } } 63 | public IEventBinding Click { get { return clickEvent; } } 64 | 65 | internal Button(ILayout parent) : base(parent) 66 | { 67 | textProperty = new PropertyBinding( 68 | this, 69 | value => this.text = value 70 | ); 71 | 72 | tooltipProperty = new PropertyBinding( 73 | this, 74 | value => this.tooltip = value 75 | ); 76 | 77 | widthProperty = new PropertyBinding( 78 | this, 79 | value => this.width = value 80 | ); 81 | 82 | heightProperty = new PropertyBinding( 83 | this, 84 | value => this.height = value 85 | ); 86 | 87 | clickEvent = new EventBinding(this); 88 | } 89 | 90 | public override void OnGUI() 91 | { 92 | var layoutOptions = new List(); 93 | if(width >= 0) 94 | { 95 | layoutOptions.Add(GUILayout.Width(width)); 96 | } 97 | if(height >= 0) 98 | { 99 | layoutOptions.Add(GUILayout.Height(height)); 100 | } 101 | 102 | if (GUILayout.Button(new GUIContent(text, tooltip), layoutOptions.ToArray())) 103 | { 104 | clickEvent.Invoke(); 105 | } 106 | } 107 | 108 | public override void BindViewModel(object viewModel) 109 | { 110 | textProperty.BindViewModel(viewModel); 111 | tooltipProperty.BindViewModel(viewModel); 112 | widthProperty.BindViewModel(viewModel); 113 | heightProperty.BindViewModel(viewModel); 114 | clickEvent.BindViewModel(viewModel); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Checkbox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEditor; 6 | 7 | namespace UnityEditorUI 8 | { 9 | /// 10 | /// Boolean check box widget. 11 | /// 12 | public interface ICheckbox : IWidget 13 | { 14 | /// 15 | /// Whether or not the box is checked. 16 | /// 17 | IPropertyBinding Checked { get; } 18 | 19 | /// 20 | /// Text to display to the left of the check box. 21 | /// 22 | IPropertyBinding Label { get; } 23 | } 24 | 25 | /// 26 | /// Boolean check box widget. 27 | /// 28 | internal class Checkbox : AbstractWidget, ICheckbox 29 | { 30 | private bool boxChecked = false; 31 | private string label = String.Empty; 32 | 33 | private PropertyBinding boxCheckedProperty; 34 | private PropertyBinding labelProperty; 35 | 36 | public IPropertyBinding Checked { get { return boxCheckedProperty; } } 37 | public IPropertyBinding Label { get { return labelProperty; } } 38 | 39 | internal Checkbox(ILayout parent) : base(parent) 40 | { 41 | boxCheckedProperty = new PropertyBinding( 42 | this, 43 | value => this.boxChecked = value 44 | ); 45 | 46 | labelProperty = new PropertyBinding( 47 | this, 48 | value => this.label = value 49 | ); 50 | } 51 | 52 | public override void OnGUI() 53 | { 54 | if (boxChecked != (boxChecked = EditorGUILayout.Toggle(label, boxChecked))) 55 | { 56 | boxCheckedProperty.UpdateView(boxChecked); 57 | } 58 | } 59 | 60 | public override void BindViewModel(object viewModel) 61 | { 62 | boxCheckedProperty.BindViewModel(viewModel); 63 | labelProperty.BindViewModel(viewModel); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DateTimePicker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEngine; 7 | using UnityEditor; 8 | 9 | namespace UnityEditorUI 10 | { 11 | /// 12 | /// Widget for entering a date and time. 13 | /// 14 | public interface IDateTimePicker : IWidget 15 | { 16 | /// 17 | /// Date and time currently being displayed in the widget. 18 | /// 19 | IPropertyBinding Date { get; } 20 | 21 | /// 22 | /// Widget width in pixels. Default uses auto-layout. 23 | /// 24 | IPropertyBinding Width { get; } 25 | 26 | /// 27 | /// Widget height in pixels. Default uses auto-layout. 28 | /// 29 | IPropertyBinding Height { get; } 30 | } 31 | 32 | /// 33 | /// Widget for entering a date and time. 34 | /// 35 | internal class DateTimePicker : AbstractWidget, IDateTimePicker 36 | { 37 | private DateTime date; 38 | private string text; 39 | private bool textValid = true; 40 | private int width = -1; 41 | private int height = -1; 42 | private CultureInfo culture; 43 | 44 | private PropertyBinding dateProperty; 45 | private PropertyBinding widthProperty; 46 | private PropertyBinding heightProperty; 47 | 48 | public IPropertyBinding Date { get { return dateProperty; } } 49 | public IPropertyBinding Width { get { return widthProperty; } } 50 | public IPropertyBinding Height { get { return heightProperty; } } 51 | 52 | internal DateTimePicker(ILayout parent) : base(parent) 53 | { 54 | culture = CultureInfo.CreateSpecificCulture("en-AU"); 55 | 56 | dateProperty = new PropertyBinding( 57 | this, 58 | value => 59 | { 60 | this.date = value; 61 | this.text = date.ToString(culture); 62 | } 63 | ); 64 | 65 | widthProperty = new PropertyBinding( 66 | this, 67 | value => this.width = value 68 | ); 69 | 70 | heightProperty = new PropertyBinding( 71 | this, 72 | value => this.height = value 73 | ); 74 | } 75 | 76 | public override void OnGUI() 77 | { 78 | var layoutOptions = new List(); 79 | if (width >= 0) 80 | { 81 | layoutOptions.Add(GUILayout.Width(width)); 82 | } 83 | if (height >= 0) 84 | { 85 | layoutOptions.Add(GUILayout.Height(height)); 86 | } 87 | 88 | // Make the background of the widget red if the date is invalid. 89 | var savedColour = UnityEngine.GUI.backgroundColor; 90 | if (!textValid) 91 | { 92 | UnityEngine.GUI.backgroundColor = Color.red; 93 | } 94 | string newText = GUILayout.TextField(text, layoutOptions.ToArray()); 95 | UnityEngine.GUI.backgroundColor = savedColour; 96 | 97 | // Update the date 98 | if (newText != text) 99 | { 100 | text = newText; 101 | 102 | textValid = DateTime.TryParse(text, culture, DateTimeStyles.None, out date); 103 | if (textValid) 104 | { 105 | dateProperty.UpdateView(date); 106 | } 107 | } 108 | } 109 | 110 | public override void BindViewModel(object viewModel) 111 | { 112 | dateProperty.BindViewModel(viewModel); 113 | widthProperty.BindViewModel(viewModel); 114 | heightProperty.BindViewModel(viewModel); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /DropdownBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEditor; 7 | using UnityEngine; 8 | 9 | namespace UnityEditorUI 10 | { 11 | /// 12 | /// Drop-down selection field. 13 | /// 14 | public interface IDropdownBox : IWidget 15 | { 16 | /// 17 | /// Currently selected item. 18 | /// 19 | IPropertyBinding SelectedItem { get; } 20 | 21 | /// 22 | /// List of items to display. Names are generated by calling ToString on each item. 23 | /// 24 | IPropertyBinding Items { get; } 25 | 26 | /// 27 | /// Optional label to display to the left of the widget. 28 | /// 29 | IPropertyBinding Label { get; } 30 | 31 | /// 32 | /// Text displayed on mouse hover. 33 | /// 34 | IPropertyBinding Tooltip { get; } 35 | } 36 | 37 | /// 38 | /// Drop-down selection field. 39 | /// 40 | internal class DropdownBox : AbstractWidget, IDropdownBox 41 | { 42 | private int selectedIndex; 43 | private object selectedItem; 44 | private object[] items; 45 | private string label; 46 | private string tooltip; 47 | 48 | private PropertyBinding selectedItemProperty; 49 | private PropertyBinding itemsProperty; 50 | private PropertyBinding labelProperty; 51 | private PropertyBinding tooltipProperty; 52 | 53 | public IPropertyBinding SelectedItem { get { return selectedItemProperty; } } 54 | public IPropertyBinding Items { get { return itemsProperty; } } 55 | public IPropertyBinding Label { get { return labelProperty; } } 56 | public IPropertyBinding Tooltip { get { return tooltipProperty; } } 57 | 58 | internal DropdownBox(ILayout parent) : base(parent) 59 | { 60 | itemsProperty = new PropertyBinding( 61 | this, 62 | value => this.items = value 63 | ); 64 | 65 | selectedItemProperty = new PropertyBinding( 66 | this, 67 | value => 68 | { 69 | selectedItem = value; 70 | if (items != null) 71 | { 72 | selectedIndex = Array.IndexOf(items, value); 73 | } 74 | } 75 | ); 76 | 77 | labelProperty = new PropertyBinding( 78 | this, 79 | value => this.label = value 80 | ); 81 | 82 | tooltipProperty = new PropertyBinding( 83 | this, 84 | value => this.tooltip = value 85 | ); 86 | } 87 | 88 | public override void OnGUI() 89 | { 90 | var itemStrings = 91 | items != null ? items.Select(i => i.ToString()).ToArray() : new string[] {}; 92 | 93 | var guiContent = itemStrings.Select(m => new GUIContent(m, tooltip)).ToArray(); 94 | var newIndex = 95 | !String.IsNullOrEmpty(label) 96 | ? EditorGUILayout.Popup(new GUIContent(label), selectedIndex, guiContent) 97 | : EditorGUILayout.Popup(selectedIndex, guiContent); 98 | 99 | if (newIndex != selectedIndex) 100 | { 101 | selectedIndex = newIndex; 102 | selectedItem = items[selectedIndex]; 103 | selectedItemProperty.UpdateView(selectedItem); 104 | } 105 | } 106 | 107 | public override void BindViewModel(object viewModel) 108 | { 109 | selectedItemProperty.BindViewModel(viewModel); 110 | itemsProperty.BindViewModel(viewModel); 111 | labelProperty.BindViewModel(viewModel); 112 | tooltipProperty.BindViewModel(viewModel); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /EventBinding.cs: -------------------------------------------------------------------------------- 1 | using RSG.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | 9 | namespace UnityEditorUI 10 | { 11 | /// 12 | /// Binds an event like a button click to a method in the view model. 13 | /// 14 | public interface IEventBinding 15 | { 16 | /// 17 | /// Configure the event to bind to later. 18 | /// 19 | WidgetT Bind(string methodName); 20 | 21 | /// 22 | /// Configure the event to bind to later. 23 | /// 24 | WidgetT Bind(Expression methodExpression); 25 | } 26 | 27 | /// 28 | /// Binds an event like a button click to a method in the view model. 29 | /// 30 | internal class EventBinding : IEventBinding 31 | { 32 | WidgetT parentWidget; 33 | string boundMethodName; 34 | MethodInfo boundMethod; 35 | private object viewModel; 36 | 37 | public void BindViewModel(object newViewModel) 38 | { 39 | viewModel = newViewModel; 40 | if (!String.IsNullOrEmpty(boundMethodName)) 41 | { 42 | Type viewModelType = newViewModel.GetType(); 43 | boundMethod = viewModelType.GetMethod(boundMethodName); 44 | if (boundMethod == null) 45 | { 46 | throw new ApplicationException("Expected method " + boundMethodName + " not found on type " + viewModelType.Name + "."); 47 | } 48 | } 49 | } 50 | 51 | /// 52 | /// Creates an EventBinding with a reference to the widget using it (used for API fluency) 53 | /// 54 | internal EventBinding(WidgetT parentWidget) 55 | { 56 | this.parentWidget = parentWidget; 57 | } 58 | 59 | /// 60 | /// Invokes the method bound to this EventBinding 61 | /// 62 | internal void Invoke() 63 | { 64 | if (boundMethod != null) 65 | { 66 | boundMethod.Invoke(viewModel, null); 67 | } 68 | } 69 | 70 | /// 71 | /// Configure the event to bind to later. 72 | /// 73 | public WidgetT Bind(string methodName) 74 | { 75 | Argument.StringNotNullOrEmpty(() => methodName); 76 | 77 | boundMethodName = methodName; 78 | 79 | return parentWidget; 80 | } 81 | 82 | /// 83 | /// Configure the event to bind to later. 84 | /// 85 | public WidgetT Bind(Expression methodExpression) 86 | { 87 | Argument.NotNull(() => methodExpression); 88 | 89 | return Bind(GetMethodName(methodExpression)); 90 | } 91 | 92 | private static string GetMethodName(Expression methodExpression) 93 | { 94 | var expr = (MethodCallExpression)methodExpression.Body; 95 | return expr.Method.Name; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Examples/Assets/Example1.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using UnityEditorUI; 4 | 5 | /// 6 | /// An example of a simple editor window featuring a button and a label. 7 | /// 8 | class Example1 : EditorWindow 9 | { 10 | /// 11 | /// Our GUI object. 12 | /// 13 | private UnityEditorUI.GUI gui; 14 | 15 | /// 16 | /// Allow the window to be opened via a menu item. 17 | /// 18 | [MenuItem("UnityEditorUI Examples/Example 1")] 19 | public static void ShowWindow() 20 | { 21 | var window = (Example1) EditorWindow.GetWindow(false, "Editor window"); 22 | 23 | if (window.gui == null) 24 | { 25 | window.SetUpGUI(); 26 | } 27 | window.Show(); 28 | } 29 | 30 | /// 31 | /// Set up the GUI. 32 | /// 33 | private void SetUpGUI() 34 | { 35 | // First, create an instance of the view we want to bind the GUI to 36 | var viewModel = new ExampleView(); 37 | 38 | // Set up the GUI widgets 39 | gui = new UnityEditorUI.GUI(); 40 | gui.Root() 41 | .Label() 42 | .Text.Value("My new editor window") 43 | .End() 44 | .Button() 45 | .Text.Value("Do something!") 46 | .Click.Bind(() => viewModel.DoSomething()) 47 | .Tooltip.Value("Click to trigger an event") 48 | .End(); 49 | 50 | // Bind the resulting GUI to the view. 51 | gui.BindViewModel(viewModel); 52 | } 53 | 54 | void OnGUI() 55 | { 56 | // Calling OnGUI on the root will update the whole GUI. 57 | gui.OnGUI(); 58 | } 59 | 60 | /// 61 | /// A simple view to bind our GUI to. 62 | /// 63 | private class ExampleView 64 | { 65 | // Properties and events to bind to go here 66 | public void DoSomething() 67 | { 68 | Debug.Log("Event triggered!"); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /Examples/Assets/Example1.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 881a5e583d2f1e948bc332861c1066b8 3 | timeCreated: 1450850715 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/Assets/Example2.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using UnityEditorUI; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | /// 8 | /// An editor window that binds to a view, subscribing to INotifyPropertyChanged 9 | /// 10 | class Example2 : EditorWindow 11 | { 12 | /// 13 | /// Our GUI object. 14 | /// 15 | private UnityEditorUI.GUI gui; 16 | 17 | /// 18 | /// Allow the window to be opened via a menu item. 19 | /// 20 | [MenuItem("UnityEditorUI Examples/Example 2")] 21 | public static void ShowWindow() 22 | { 23 | var window = (Example2) EditorWindow.GetWindow(false, "Editor window"); 24 | 25 | if (window.gui == null) 26 | { 27 | window.SetUpGUI(); 28 | } 29 | window.Show(); 30 | } 31 | 32 | /// 33 | /// Set up the GUI. 34 | /// 35 | private void SetUpGUI() 36 | { 37 | // First, create an instance of the view we want to bind the GUI to 38 | var viewModel = new ExampleView(); 39 | 40 | // Set up the GUI widgets 41 | gui = new UnityEditorUI.GUI(); 42 | gui.Root() 43 | .Label() 44 | .Text.Value("Object movement tool") 45 | .Bold.Value(true) 46 | .End() 47 | .HorizontalLayout() 48 | .Label() 49 | .Text.Value("Object to look for") 50 | .End() 51 | .TextBox() 52 | .Text.Bind(() => viewModel.SelectedObjectName) 53 | .End() 54 | .End() 55 | .Vector3Field() 56 | .Label.Value("Position") 57 | .Vector.Bind(() => viewModel.ObjectPosition) 58 | .End() 59 | .Button() 60 | .Text.Value("Capture position") 61 | .Click.Bind(() => viewModel.CaptureObjectPosition()) 62 | .End() 63 | .Button() 64 | .Text.Value("Set position") 65 | .Click.Bind(() => viewModel.SetObjectPosition()) 66 | .End(); 67 | 68 | // Bind the resulting GUI to the view. 69 | gui.BindViewModel(viewModel); 70 | } 71 | 72 | void OnGUI() 73 | { 74 | // Calling OnGUI on the root will update the whole GUI. 75 | gui.OnGUI(); 76 | } 77 | 78 | /// 79 | /// A simple view to bind our GUI to. 80 | /// 81 | private class ExampleView : INotifyPropertyChanged 82 | { 83 | public event PropertyChangedEventHandler PropertyChanged; 84 | 85 | private void NotifyPropertyChanged(string propertyName) 86 | { 87 | if (PropertyChanged != null) 88 | { 89 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 90 | } 91 | } 92 | 93 | 94 | // Properties and events to bind to go here 95 | public void CaptureObjectPosition() 96 | { 97 | var go = GameObject.Find(SelectedObjectName); 98 | if (go == null) 99 | { 100 | EditorUtility.DisplayDialog("Error", "Can't find object '" + "'", "Ok"); 101 | return; 102 | } 103 | 104 | ObjectPosition = go.transform.position; 105 | } 106 | 107 | public void SetObjectPosition() 108 | { 109 | var go = GameObject.Find(SelectedObjectName); 110 | if (go == null) 111 | { 112 | EditorUtility.DisplayDialog("Error", "Can't find object '" + "'", "Ok"); 113 | return; 114 | } 115 | 116 | go.transform.position = ObjectPosition; 117 | } 118 | 119 | private Vector3 objectPosition; 120 | 121 | public Vector3 ObjectPosition 122 | { 123 | get 124 | { 125 | return objectPosition; 126 | } 127 | set 128 | { 129 | objectPosition = value; 130 | NotifyPropertyChanged("ObjectPosition"); 131 | } 132 | } 133 | 134 | private string selectedObjectName; 135 | 136 | public string SelectedObjectName 137 | { 138 | get 139 | { 140 | return selectedObjectName; 141 | } 142 | set 143 | { 144 | selectedObjectName = value; 145 | NotifyPropertyChanged("SelectedObjectName"); 146 | } 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /Examples/Assets/Example2.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4b86431221f492449f54a56f228806d 3 | timeCreated: 1450851227 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Examples/Assets/RSG.Toolkit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/Assets/RSG.Toolkit.dll -------------------------------------------------------------------------------- /Examples/Assets/RSG.Toolkit.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ba6b6caa7aa2fa4ab8573d0cf3b105b 3 | timeCreated: 1450829210 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | platformData: 11 | Any: 12 | enabled: 1 13 | settings: {} 14 | Editor: 15 | enabled: 0 16 | settings: 17 | DefaultValueInitialized: true 18 | userData: 19 | assetBundleName: 20 | assetBundleVariant: 21 | -------------------------------------------------------------------------------- /Examples/Assets/UnityEditorUI_Editor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/Assets/UnityEditorUI_Editor.dll -------------------------------------------------------------------------------- /Examples/Assets/UnityEditorUI_Editor.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b9c6af2eaa2cf344a0a1547e8d11f5d 3 | timeCreated: 1450829177 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | platformData: 11 | Any: 12 | enabled: 1 13 | settings: {} 14 | Editor: 15 | enabled: 0 16 | settings: 17 | DefaultValueInitialized: true 18 | userData: 19 | assetBundleName: 20 | assetBundleVariant: 21 | -------------------------------------------------------------------------------- /Examples/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.2.3p2 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /Examples/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /Examples/ProjectSettings/UnityAnalyticsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/Examples/ProjectSettings/UnityAnalyticsManager.asset -------------------------------------------------------------------------------- /GUI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityEditorUI 7 | { 8 | /// 9 | /// Base GUI class. Creates and keeps track of the root of the widget stack, which can then be used to add new widgets. 10 | /// 11 | public class GUI 12 | { 13 | private RootLayout root; 14 | 15 | public GUI() 16 | { 17 | root = new RootLayout(); 18 | } 19 | 20 | /// 21 | /// Updates the UI and processes events. Should be called in the unity OnGUI function. 22 | /// 23 | public void OnGUI() 24 | { 25 | root.OnGUI(); 26 | } 27 | 28 | /// 29 | /// Returns the root layout of the UI. 30 | /// 31 | public ILayout Root() 32 | { 33 | return root; 34 | } 35 | 36 | /// 37 | /// Binds the GUI to a view model by using reflection to find properties and methods that match 38 | /// the strings set in PropertyBindings in widgets. 39 | /// 40 | public void BindViewModel(object viewModel) 41 | { 42 | root.BindViewModel(viewModel); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /HorizontalLayout.cs: -------------------------------------------------------------------------------- 1 | using RSG.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEngine; 7 | 8 | namespace UnityEditorUI 9 | { 10 | /// 11 | /// Lays widgets out horizontally. 12 | /// 13 | internal class HorizontalLayout : AbstractLayout 14 | { 15 | public HorizontalLayout(ILayout parent) : 16 | base(parent) 17 | { 18 | Argument.NotNull(() => parent); 19 | } 20 | 21 | public override void OnGUI() 22 | { 23 | if (!enabled) 24 | { 25 | return; 26 | } 27 | 28 | GUILayout.BeginHorizontal(); 29 | base.OnGUI(); 30 | GUILayout.EndHorizontal(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ILayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityEditorUI 7 | { 8 | /// 9 | /// Layouts are widgets that can contain other child widgets. 10 | /// 11 | public interface ILayout : IWidget 12 | { 13 | /// 14 | /// Creates a new button and adds it to the layout. 15 | /// 16 | IButton Button(); 17 | 18 | /// 19 | /// Creates a new label and adds it to the view. 20 | /// 21 | ILabel Label(); 22 | 23 | /// 24 | /// Creates a new TextBox and adds it to the layout. 25 | /// 26 | ITextBox TextBox(); 27 | 28 | /// 29 | /// Widget for choosing dates, similar do TextBox except with date validation built-in. 30 | /// 31 | IDateTimePicker DateTimePicker(); 32 | 33 | /// 34 | /// Creates a new drop-down selection box and adds it to the layout. 35 | /// 36 | IDropdownBox DropdownBox(); 37 | 38 | /// 39 | /// Creates a new checkbox and adds it to the layout. 40 | /// 41 | ICheckbox Checkbox(); 42 | 43 | /// 44 | /// Creates a Vector3 field with X, Y and Z entry boxes. 45 | /// 46 | IVector3Field Vector3Field(); 47 | 48 | /// 49 | /// Creates a widget for editing layer masks. 50 | /// 51 | ILayerPicker LayerPicker(); 52 | 53 | /// 54 | /// Inserts a space between other widgets. 55 | /// 56 | ILayout Spacer(); 57 | 58 | /// 59 | /// Creates a VerticalLayout and adds it to this layout. 60 | /// 61 | ILayout VerticalLayout(); 62 | 63 | /// 64 | /// Creates a horizontal layout and adds it to this layout. 65 | /// 66 | ILayout HorizontalLayout(); 67 | 68 | /// 69 | /// Whether or not to draw this layout and its sub-widgets (default is true). 70 | /// 71 | IPropertyBinding Enabled { get; } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /IWidget.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace UnityEditorUI 7 | { 8 | /// 9 | /// Basic interface for all widgets. 10 | /// 11 | public interface IWidget 12 | { 13 | /// 14 | /// Returns ths widget's parent layout. 15 | /// 16 | ILayout End(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Real Serious Games 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Label.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | namespace UnityEditorUI 9 | { 10 | /// 11 | /// Widget for displaying read-only text. 12 | /// 13 | public interface ILabel : IWidget 14 | { 15 | /// 16 | /// Label text. 17 | /// 18 | IPropertyBinding Text { get; } 19 | 20 | /// 21 | /// Text displayed on mouse hover. 22 | /// 23 | IPropertyBinding Tooltip { get; } 24 | 25 | /// 26 | /// Whether or not the label should be displayed in bold (default is false). 27 | /// 28 | IPropertyBinding Bold { get; } 29 | 30 | /// 31 | /// Width of the widget in pixels. Default uses auto-layout. 32 | /// 33 | IPropertyBinding Width { get; } 34 | 35 | /// 36 | /// Height of the widget in pixels. Default uses auto-layout. 37 | /// 38 | IPropertyBinding Height { get; } 39 | } 40 | 41 | /// 42 | /// Widget for displaying read-only text. 43 | /// 44 | internal class Label : AbstractWidget, ILabel 45 | { 46 | private string text = String.Empty; 47 | private string tooltip = String.Empty; 48 | private bool bold = false; 49 | private int width = -1; 50 | private int height = -1; 51 | 52 | private PropertyBinding textProperty; 53 | private PropertyBinding tooltipProperty; 54 | private PropertyBinding boldProperty; 55 | private PropertyBinding widthProperty; 56 | private PropertyBinding heightProperty; 57 | 58 | public IPropertyBinding Text { get { return textProperty; } } 59 | public IPropertyBinding Tooltip { get { return tooltipProperty; } } 60 | public IPropertyBinding Bold { get { return boldProperty; } } 61 | public IPropertyBinding Width { get { return widthProperty; } } 62 | public IPropertyBinding Height { get { return heightProperty; } } 63 | 64 | internal Label(ILayout parent) : base(parent) 65 | { 66 | textProperty = new PropertyBinding( 67 | this, 68 | value => this.text = value 69 | ); 70 | 71 | tooltipProperty = new PropertyBinding( 72 | this, 73 | value => this.tooltip = value 74 | ); 75 | 76 | boldProperty = new PropertyBinding( 77 | this, 78 | value => this.bold = value 79 | ); 80 | 81 | widthProperty = new PropertyBinding( 82 | this, 83 | value => this.width = value 84 | ); 85 | 86 | heightProperty = new PropertyBinding( 87 | this, 88 | value => this.height = value 89 | ); 90 | } 91 | 92 | public override void OnGUI() 93 | { 94 | var guiContent = new GUIContent(text, tooltip); 95 | var style = bold ? EditorStyles.boldLabel : EditorStyles.label; 96 | var layoutOptions = new List(); 97 | if (width >= 0) 98 | { 99 | layoutOptions.Add(GUILayout.Width(width)); 100 | } 101 | if (height >= 0) 102 | { 103 | layoutOptions.Add(GUILayout.Height(height)); 104 | } 105 | GUILayout.Label(guiContent, style, layoutOptions.ToArray()); 106 | } 107 | 108 | public override void BindViewModel(object viewModel) 109 | { 110 | textProperty.BindViewModel(viewModel); 111 | tooltipProperty.BindViewModel(viewModel); 112 | boldProperty.BindViewModel(viewModel); 113 | widthProperty.BindViewModel(viewModel); 114 | heightProperty.BindViewModel(viewModel); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /LayerPicker.cs: -------------------------------------------------------------------------------- 1 | using RSG.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEditor; 7 | using UnityEngine; 8 | 9 | namespace UnityEditorUI 10 | { 11 | /// 12 | /// Widget for selecting Unity layers. 13 | /// 14 | public interface ILayerPicker : IWidget 15 | { 16 | /// 17 | /// Text to display to the left of the widget. 18 | /// 19 | IPropertyBinding Label { get; } 20 | 21 | /// 22 | /// List of names of the selected layers. 23 | /// 24 | IPropertyBinding SelectedLayers { get; } 25 | } 26 | 27 | /// 28 | /// Widget for selecting Unity layers. 29 | /// 30 | internal class LayerPicker : AbstractWidget, ILayerPicker 31 | { 32 | private string label; 33 | 34 | private LayerMask selectedLayerMask; 35 | 36 | private static List layers; 37 | private static List layerNumbers; 38 | private static string[] layerNames; 39 | private static long lastUpdateTick; 40 | 41 | private PropertyBinding labelProperty; 42 | private PropertyBinding selectedLayersProperty; 43 | 44 | public IPropertyBinding Label { get { return labelProperty; } } 45 | public IPropertyBinding SelectedLayers { get { return selectedLayersProperty; } } 46 | 47 | internal LayerPicker(ILayout parent) : base(parent) 48 | { 49 | labelProperty = new PropertyBinding( 50 | this, 51 | value => this.label = value 52 | ); 53 | 54 | selectedLayersProperty = new PropertyBinding( 55 | this, 56 | value => this.selectedLayerMask.value = LayerNamesToMask(value) 57 | ); 58 | } 59 | 60 | public override void OnGUI() 61 | { 62 | var newLayerMask = LayerMaskField(label, selectedLayerMask); 63 | if (selectedLayerMask != newLayerMask) 64 | { 65 | selectedLayerMask = newLayerMask; 66 | var newLayerNames = MaskToLayerNames(selectedLayerMask).ToArray(); 67 | selectedLayersProperty.UpdateView(newLayerNames); 68 | } 69 | } 70 | 71 | public override void BindViewModel(object viewModel) 72 | { 73 | labelProperty.BindViewModel(viewModel); 74 | selectedLayersProperty.BindViewModel(viewModel); 75 | } 76 | 77 | /// 78 | /// http://answers.unity3d.com/questions/42996/how-to-create-layermask-field-in-a-custom-editorwi.html 79 | /// 80 | private static LayerMask LayerMaskField(string label, LayerMask selected) 81 | { 82 | if (layers == null || (DateTime.Now.Ticks - lastUpdateTick > 10000000L && Event.current.type == EventType.Layout)) 83 | { 84 | lastUpdateTick = DateTime.Now.Ticks; 85 | if (layers == null) 86 | { 87 | layers = new List(); 88 | layerNumbers = new List(); 89 | layerNames = new string[4]; 90 | } 91 | else 92 | { 93 | layers.Clear(); 94 | layerNumbers.Clear(); 95 | } 96 | 97 | int emptyLayers = 0; 98 | for (int i = 0; i < 32; i++) 99 | { 100 | string layerName = LayerMask.LayerToName(i); 101 | 102 | if (layerName != String.Empty) 103 | { 104 | for (; emptyLayers > 0; emptyLayers--) 105 | { 106 | layers.Add("Layer " + (i - emptyLayers)); 107 | } 108 | layerNumbers.Add(i); 109 | layers.Add(layerName); 110 | } 111 | else 112 | { 113 | emptyLayers++; 114 | } 115 | } 116 | 117 | if (layerNames.Length != layers.Count) 118 | { 119 | layerNames = new string[layers.Count]; 120 | } 121 | for (int i = 0; i < layerNames.Length; i++) 122 | { 123 | layerNames[i] = layers[i]; 124 | } 125 | } 126 | 127 | selected.value = EditorGUILayout.MaskField(label, selected.value, layerNames); 128 | 129 | return selected; 130 | } 131 | 132 | /// 133 | /// Convert layer names to a mask. 134 | /// 135 | private int LayerNamesToMask(IEnumerable layerNames) 136 | { 137 | Argument.NotNull(() => layerNames); 138 | 139 | return layerNames 140 | .Select(n => 1 << LayerMask.NameToLayer(n)) // Convert to index then to bitfield. 141 | .Aggregate(0, (l, r) => l | r); 142 | } 143 | 144 | /// 145 | /// Convert a layers mask to a collection of layer names. 146 | /// 147 | private IEnumerable MaskToLayerNames(int layerMask) 148 | { 149 | var layerIndex = 0; 150 | 151 | while (layerMask > 0) 152 | { 153 | if ((layerMask & 0x1) != 0) 154 | { 155 | var layerName = LayerMask.LayerToName(layerIndex); 156 | if (!string.IsNullOrEmpty(layerName)) 157 | { 158 | yield return layerName; 159 | } 160 | } 161 | 162 | layerMask >>= 1; 163 | ++layerIndex; 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UnityEditorUI")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Real Serious Games")] 12 | [assembly: AssemblyProduct("UnityEditorUI")] 13 | [assembly: AssemblyCopyright("Copyright © Real Serious Games 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f59f39b5-4a86-4a45-89f0-854c01c57dfc")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.1.0.0")] 36 | [assembly: AssemblyFileVersion("0.1.0.0")] 37 | -------------------------------------------------------------------------------- /PropertyBinding.cs: -------------------------------------------------------------------------------- 1 | using RSG.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Reflection; 8 | using System.Text; 9 | using UnityEngine; 10 | 11 | namespace UnityEditorUI 12 | { 13 | /// 14 | /// Binds a widget's property to a property in the external view model. 15 | /// 16 | public interface IPropertyBinding 17 | { 18 | /// 19 | /// Configure the property to bind to later. 20 | /// 21 | WidgetT Bind(string propertyName); 22 | 23 | /// 24 | /// Configure the property to bind to later. 25 | /// 26 | WidgetT Bind(Expression> propertyExpression); 27 | 28 | /// 29 | /// Set the value of the property directly (only used in initial setup) 30 | /// 31 | WidgetT Value(ValueT propertyValue); 32 | } 33 | 34 | /// 35 | /// Binds a widget's property to a property in the external view model. 36 | /// 37 | internal class PropertyBinding : IPropertyBinding 38 | { 39 | // Used in fluent API so that Bind and Value methods can return the parent widget and thus be chained together 40 | WidgetT parentWidget; 41 | Action onViewModelUpdated; 42 | 43 | private string boundPropertyName; 44 | private object viewModel; 45 | private PropertyInfo boundProperty; 46 | 47 | public void BindViewModel(object newViewModel) 48 | { 49 | viewModel = newViewModel; 50 | if (!String.IsNullOrEmpty(boundPropertyName)) 51 | { 52 | var viewModelType = newViewModel.GetType(); 53 | boundProperty = viewModelType.GetProperty(boundPropertyName); 54 | if (boundProperty == null) 55 | { 56 | throw new ApplicationException("Expected property " + boundPropertyName + " not found on type " + viewModelType.Name + "."); 57 | } 58 | 59 | // Update the widget with the initial value from the bound property. 60 | var widgetValue = GetValueFromViewModel(); 61 | UpdateWidget(widgetValue); 62 | 63 | // Bind the property so that the widget gets updated when the view model changes 64 | var notifyPropertyChanged = viewModel as INotifyPropertyChanged; 65 | if (notifyPropertyChanged != null) 66 | { 67 | notifyPropertyChanged.PropertyChanged += viewModel_PropertyChanged; 68 | } 69 | } 70 | } 71 | 72 | void viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) 73 | { 74 | if (e.PropertyName == boundPropertyName) 75 | { 76 | var widgetValue = GetValueFromViewModel(); 77 | UpdateWidget(widgetValue); 78 | } 79 | } 80 | 81 | /// 82 | /// Gets the value from the property in the bound view model. 83 | /// 84 | private ValueT GetValueFromViewModel() 85 | { 86 | if (boundProperty == null) 87 | { 88 | return default(ValueT); 89 | } 90 | 91 | var viewModelValue = boundProperty.GetValue(viewModel, null); 92 | try 93 | { 94 | return (ValueT)viewModelValue; 95 | } 96 | catch (InvalidCastException) 97 | { 98 | //Logger.LogError(ex, "todo") 99 | Debug.LogError("Failed to cast view model value of type " + viewModelValue.GetType().Name + " to " + typeof(ValueT).Name); 100 | } 101 | 102 | return default(ValueT); 103 | } 104 | 105 | /// 106 | /// Create the PropertyBinding with a reference to the widget using it and an action to be called when the external view model changes. 107 | /// 108 | internal PropertyBinding(WidgetT parentWidget, Action onViewModelUpdated) 109 | { 110 | this.parentWidget = parentWidget; 111 | this.onViewModelUpdated = onViewModelUpdated; 112 | } 113 | 114 | /// 115 | /// Update the parent widget when the value of the property is changed. 116 | /// 117 | internal void UpdateWidget(ValueT newValue) 118 | { 119 | onViewModelUpdated(newValue); 120 | } 121 | 122 | /// 123 | /// Updates the bound view model when the value is changed by the widget. 124 | /// 125 | internal void UpdateView(ValueT newValue) 126 | { 127 | if (viewModel != null && boundProperty != null) 128 | { 129 | boundProperty.SetValue(viewModel, newValue, null); 130 | } 131 | } 132 | 133 | /// 134 | /// Binds this PropertyBinding to an external property. 135 | /// 136 | public WidgetT Bind(string propertyName) 137 | { 138 | Argument.StringNotNullOrEmpty(() => propertyName); 139 | 140 | boundPropertyName = propertyName; 141 | 142 | return parentWidget; 143 | } 144 | 145 | /// 146 | /// Binds this PropertyBinding to an external property. 147 | /// 148 | public WidgetT Bind(Expression> propertyExpression) 149 | { 150 | Argument.NotNull(() => propertyExpression); 151 | 152 | return Bind(GetPropertyName(propertyExpression)); 153 | } 154 | 155 | /// 156 | /// Get the string name of a property. 157 | /// 158 | private static string GetPropertyName(Expression> propertyExpression) 159 | { 160 | var expr = (MemberExpression)propertyExpression.Body; 161 | return expr.Member.Name; 162 | } 163 | 164 | /// 165 | /// Permanently set the value of this PropertyBinding 166 | /// 167 | public WidgetT Value(ValueT propertyValue) 168 | { 169 | UpdateWidget(propertyValue); 170 | 171 | return parentWidget; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityEditorUI 2 | 3 | A wrapper around the Unity editor GUI system for constructing editor windows using a fluent API instead of Unity's `OnGUI` functions. 4 | 5 | This system supports two-way data binding between the UI and a ViewModel class, allowing you to create simpler, unit testable code for editor extensions by moving the logic and the UI code into separate classes. 6 | 7 | ## Constructing editor windows 8 | 9 | Set up a simple editor window with a label and a button: 10 | 11 | ``` 12 | // Create an instance of the view you want to bind the UI to 13 | var viewModel = new ExampleView(); 14 | 15 | // Create the UI 16 | var gui = new UnityEditorUI.GUI(); 17 | gui.Root() 18 | .Label() 19 | .Text.Value("My new editor window") 20 | .End() 21 | .Button() 22 | .Text.Value("Do something!") 23 | .Click.Bind(() => viewModel.DoSomething()) 24 | .Tooltip.Value("Click to trigger an event") 25 | .End() 26 | 27 | // Bind the UI to the view 28 | gui.BindViewModel(viewModel); 29 | ``` 30 | 31 | And then render and update it by adding the following line to your editor window's existing `OnGUI()` method: 32 | ``` 33 | gui.OnGUI(); 34 | ``` 35 | 36 | Every property on a GUI widget can have its value set to a constant value using `.Value()`, or bound to another property using `.Bind()`. If the class being bound to implements [`INotifyPropertyChanged`](https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx), this will set up a two way data binding, so that properties in the bound class get updated when the UI changes and the UI gets . 37 | 38 | ## Examples 39 | The project in the `Examples` directory has been tested with Unity 5.2.3p2 and should contain everything you need to load up and run the examples. Since this library is purely for Unity editor extensions, there is no scene included in the project. 40 | 41 | ### Example 1 42 | This example shows the most basic sample of a use case for the UnityEditorUI system, binding a Unity editor window to a simple view model class but not subscribing to property changed events. 43 | 44 | ### Example 2 45 | This example demonstrates how to set up a view model class that implements [`INotifyPropertyChanged`](https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx) and sends events back to the UI when properties are changed. 46 | 47 | ## Widgets 48 | ### Button 49 | Clickable push button widget. 50 | #### Bindable properties 51 | - Text : `string` 52 | - Tooltip : `string` 53 | - Width : `int` 54 | - Height : `int` 55 | 56 | #### Bindable events 57 | - Click 58 | 59 | ### Checkbox 60 | Boolean check box widget. 61 | #### Bindable properties 62 | - Checked : `bool` 63 | - Label : `string` 64 | 65 | ### DateTimePicker 66 | Widget for entering a date and time. Essentially a TextBox with date validation on it. 67 | #### Bindable properties 68 | - Date : `DateTime` 69 | - Width : `int` 70 | - Height : `int` 71 | 72 | ### DropdownBox 73 | Drop-down selection field. Labels for individual items are set by calling the bound object's `ToString()` method. 74 | #### Bindable properties 75 | - SelectedItem : `object` 76 | - Items : `object[]` 77 | - Label : `string` 78 | - Tooltip : `string` 79 | 80 | ### Label 81 | Widget for displaying read-only text. 82 | #### Bindable properties 83 | - Text : `string` 84 | - Tooltip : `string` 85 | - Bold : `bool` 86 | - Width : `int` 87 | - Height : `int` 88 | 89 | ### LayerPicker 90 | Widget for selecting Unity layers. 91 | #### Bindable properties 92 | - Label : `string` 93 | - SelectedLayers : `string[]` 94 | 95 | ### Spacer 96 | Inserts a space between other widgets 97 | 98 | ### TextBox 99 | Widget for entering text 100 | #### Bindable properties 101 | - Text : `string` 102 | - Width : `int` 103 | - Height : `int` 104 | 105 | ### Vector3Field 106 | Widget for entering vectors with X, Y and Z coordinates. 107 | #### Bindable properties 108 | - Label : `string` 109 | - Tooltip : `string` 110 | - Vector : `Vector3` 111 | 112 | ## Layouts 113 | ### RootLayout 114 | ### HorizontalLayout 115 | ### VerticalLayout 116 | 117 | 118 | -------------------------------------------------------------------------------- /RootLayout.cs: -------------------------------------------------------------------------------- 1 | using RSG; 2 | using RSG.Utils; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using UnityEngine; 8 | 9 | namespace UnityEditorUI 10 | { 11 | /// 12 | /// Root layout - same as VerticalLayout except that it does not contain a parent. 13 | /// 14 | internal class RootLayout : AbstractLayout 15 | { 16 | internal RootLayout() : 17 | base(null) 18 | { 19 | } 20 | 21 | public override void OnGUI() 22 | { 23 | GUILayout.BeginVertical(); 24 | base.OnGUI(); 25 | GUILayout.EndVertical(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Spacer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEditor; 6 | 7 | namespace UnityEditorUI 8 | { 9 | /// 10 | /// Inserts a space between other widgets. 11 | /// 12 | interface ISpacer : IWidget 13 | { 14 | 15 | } 16 | 17 | /// 18 | /// Inserts a space between other widgets. 19 | /// 20 | class Spacer : AbstractWidget, ISpacer 21 | { 22 | internal Spacer(ILayout parent) : base(parent) 23 | { 24 | 25 | } 26 | 27 | public override void OnGUI() 28 | { 29 | EditorGUILayout.Space(); 30 | } 31 | 32 | public override void BindViewModel(object viewModel) 33 | { 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TextBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace UnityEditorUI 8 | { 9 | /// 10 | /// Widget for entering text. 11 | /// 12 | public interface ITextBox : IWidget 13 | { 14 | /// 15 | /// Editable text. 16 | /// 17 | IPropertyBinding Text { get; } 18 | 19 | /// 20 | /// Width of the widget in pixels. Default uses auto-layout. 21 | /// 22 | IPropertyBinding Width { get; } 23 | 24 | /// 25 | /// Height of the widget in pixels. Default uses auto-layout. 26 | /// 27 | IPropertyBinding Height { get; } 28 | } 29 | 30 | /// 31 | /// Widget for entering text. 32 | /// 33 | internal class TextBox : AbstractWidget, ITextBox 34 | { 35 | private string text = string.Empty; 36 | private int width = -1; 37 | private int height = -1; 38 | 39 | private PropertyBinding textProperty; 40 | private PropertyBinding widthProperty; 41 | private PropertyBinding heightProperty; 42 | 43 | public IPropertyBinding Text { get { return textProperty; } } 44 | public IPropertyBinding Width { get { return widthProperty; } } 45 | public IPropertyBinding Height { get { return heightProperty; } } 46 | 47 | internal TextBox(ILayout parent) : base(parent) 48 | { 49 | textProperty = new PropertyBinding( 50 | this, 51 | value => text = value == null ? String.Empty : value 52 | ); 53 | 54 | widthProperty = new PropertyBinding( 55 | this, 56 | value => this.width = value 57 | ); 58 | 59 | heightProperty = new PropertyBinding( 60 | this, 61 | value => this.height = value 62 | ); 63 | } 64 | 65 | public override void OnGUI() 66 | { 67 | var layoutOptions = new List(); 68 | if (width >= 0) 69 | { 70 | layoutOptions.Add(GUILayout.Width(width)); 71 | } 72 | 73 | if (height >= 0) 74 | { 75 | layoutOptions.Add(GUILayout.Height(height)); 76 | } 77 | 78 | string newText = height >= 0 // Use TextField if height isn't specified, otherwise use TextArea 79 | ? GUILayout.TextArea(text, layoutOptions.ToArray()) 80 | : GUILayout.TextField(text, layoutOptions.ToArray()); 81 | if (newText != text) 82 | { 83 | text = newText; 84 | textProperty.UpdateView(newText); 85 | } 86 | } 87 | 88 | public override void BindViewModel(object viewModel) 89 | { 90 | textProperty.BindViewModel(viewModel); 91 | widthProperty.BindViewModel(viewModel); 92 | heightProperty.BindViewModel(viewModel); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /UnityEditorUI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7C203707-FAC1-40A6-BD83-19D753293C16} 8 | Library 9 | Properties 10 | UnityEditorUI 11 | UnityEditorUI_Editor 12 | v3.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | packages\RSG.Toolkit.1.0.0.0\lib\net35\RSG.Toolkit.dll 36 | True 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | dlls\UnityEditor.dll 46 | 47 | 48 | False 49 | dlls\UnityEngine.dll 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 | 89 | -------------------------------------------------------------------------------- /UnityEditorUI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityEditorUI", "UnityEditorUI.csproj", "{7C203707-FAC1-40A6-BD83-19D753293C16}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {7C203707-FAC1-40A6-BD83-19D753293C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {7C203707-FAC1-40A6-BD83-19D753293C16}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {7C203707-FAC1-40A6-BD83-19D753293C16}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {7C203707-FAC1-40A6-BD83-19D753293C16}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Vector3Field.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEditor; 6 | using UnityEngine; 7 | 8 | namespace UnityEditorUI 9 | { 10 | /// 11 | /// Widget for entering vectors with X, Y and Z coordinates. 12 | /// 13 | public interface IVector3Field : IWidget 14 | { 15 | /// 16 | /// Label shown to the left of the widget. 17 | /// 18 | IPropertyBinding Label { get; } 19 | 20 | /// 21 | /// Text shown on mouse hover 22 | /// 23 | IPropertyBinding Tooltip { get; } 24 | 25 | /// 26 | /// Vector entered in widget. 27 | /// 28 | IPropertyBinding Vector { get; } 29 | } 30 | 31 | /// 32 | /// Widget for entering vectors with X, Y and Z coordinates. 33 | /// 34 | internal class Vector3Field : AbstractWidget, IVector3Field 35 | { 36 | private Vector3 vector; 37 | private string label; 38 | private string tooltip; 39 | 40 | private PropertyBinding vectorProperty; 41 | private PropertyBinding labelProperty; 42 | private PropertyBinding tooltipProperty; 43 | 44 | public IPropertyBinding Vector { get { return vectorProperty; } } 45 | public IPropertyBinding Label { get { return labelProperty; } } 46 | public IPropertyBinding Tooltip { get { return tooltipProperty; } } 47 | 48 | internal Vector3Field(ILayout parent) : base(parent) 49 | { 50 | vectorProperty = new PropertyBinding( 51 | this, 52 | value => this.vector = value 53 | ); 54 | 55 | labelProperty = new PropertyBinding( 56 | this, 57 | value => this.label = value 58 | ); 59 | 60 | tooltipProperty = new PropertyBinding( 61 | this, 62 | value => this.tooltip = value 63 | ); 64 | } 65 | 66 | public override void OnGUI() 67 | { 68 | var newVector = EditorGUILayout.Vector3Field(new GUIContent(label, tooltip), vector); 69 | if (newVector != vector) 70 | { 71 | vector = newVector; 72 | vectorProperty.UpdateView(vector); 73 | } 74 | } 75 | 76 | public override void BindViewModel(object viewModel) 77 | { 78 | vectorProperty.BindViewModel(viewModel); 79 | labelProperty.BindViewModel(viewModel); 80 | tooltipProperty.BindViewModel(viewModel); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /VerticalLayout.cs: -------------------------------------------------------------------------------- 1 | using RSG.Utils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using UnityEngine; 7 | 8 | namespace UnityEditorUI 9 | { 10 | /// 11 | /// Layout that arranges widgets in a row vertically. 12 | /// 13 | internal class VerticalLayout : AbstractLayout 14 | { 15 | public VerticalLayout(ILayout parent) : 16 | base(parent) 17 | { 18 | } 19 | 20 | public override void OnGUI() 21 | { 22 | if (!enabled) 23 | { 24 | return; 25 | } 26 | 27 | GUILayout.BeginVertical(); 28 | base.OnGUI(); 29 | GUILayout.EndVertical(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /dlls/UnityEditor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/dlls/UnityEditor.dll -------------------------------------------------------------------------------- /dlls/UnityEngine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/dlls/UnityEngine.dll -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/RSG.Toolkit.1.0.0.0/RSG.Toolkit.1.0.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/packages/RSG.Toolkit.1.0.0.0/RSG.Toolkit.1.0.0.0.nupkg -------------------------------------------------------------------------------- /packages/RSG.Toolkit.1.0.0.0/lib/net35/RSG.Toolkit.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Real-Serious-Games/Unity-Editor-UI/3c3d4efdd67fa790579f58f81ac27dd6a2f2fb22/packages/RSG.Toolkit.1.0.0.0/lib/net35/RSG.Toolkit.dll -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------