├── .gitignore ├── Attributes.cs ├── Editor ├── EditorHelper.cs ├── EditorHelper.cs.meta ├── PropertyDrawer.cs ├── PropertyDrawer.cs.meta ├── SelectionWizard.cs └── SelectionWizard.cs.meta ├── Example ├── DemoObject.cs ├── DemoObject.cs.meta ├── Editor.meta └── Editor │ ├── Base32Encoding.cs │ ├── Base32Encoding.cs.meta │ ├── DemoScenarios.cs │ ├── DemoScenarios.cs.meta │ ├── SampleEditor.cs │ └── SampleEditor.cs.meta ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Autogenerated VS/MD solution and project files 9 | ExportedObj/ 10 | *.csproj 11 | *.unityproj 12 | *.sln 13 | *.suo 14 | *.tmp 15 | *.user 16 | *.userprefs 17 | *.pidb 18 | *.booproj 19 | *.svd 20 | 21 | 22 | # Unity3D generated meta files 23 | *.pidb.meta 24 | 25 | # Unity3D Generated File On Crash Reports 26 | sysinfo.txt 27 | /*.meta 28 | -------------------------------------------------------------------------------- /Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace UnityEditorHelper 5 | { 6 | public class LayerAttribute : PropertyAttribute { } 7 | 8 | public class LimitAttribute : PropertyAttribute 9 | { 10 | private enum Mode { LimitLower, LimitUpper, LimitBoth } 11 | 12 | private readonly Mode _limitMode; 13 | 14 | private readonly int _lowerLimit; 15 | private readonly int _upperLimit; 16 | 17 | public LimitAttribute(int lowerLimit) : this(Mode.LimitLower, lowerLimit, int.MaxValue) { } 18 | 19 | public LimitAttribute(int lowerLimit, int upperLimit) : this(Mode.LimitBoth, lowerLimit, upperLimit) { } 20 | 21 | private LimitAttribute(Mode mode, int lowerLimit, int upperLimit) 22 | { 23 | _limitMode = mode; 24 | _lowerLimit = lowerLimit; 25 | _upperLimit = upperLimit; 26 | } 27 | 28 | public int Limit(int value) 29 | { 30 | switch (_limitMode) 31 | { 32 | case Mode.LimitLower: 33 | return Mathf.Clamp(value, _lowerLimit, int.MaxValue); 34 | case Mode.LimitUpper: 35 | return Mathf.Clamp(value, int.MinValue, _upperLimit); 36 | case Mode.LimitBoth: 37 | return Mathf.Clamp(value, _lowerLimit, _upperLimit); 38 | default: 39 | throw new ArgumentOutOfRangeException(); 40 | } 41 | } 42 | } 43 | 44 | public class SortingLayerAttribute : PropertyAttribute { } 45 | 46 | public class TagAttribute : PropertyAttribute { } 47 | } -------------------------------------------------------------------------------- /Editor/EditorHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace UnityEditorHelper 7 | { 8 | public static class EditorHelper 9 | { 10 | private static GUIStyle _simpleRectStyle; 11 | 12 | public static GUIStyle SimpleRectStyle 13 | { 14 | get 15 | { 16 | if (_simpleRectStyle != null) 17 | return _simpleRectStyle; 18 | 19 | Texture2D simpleTexture = new Texture2D(1, 1); 20 | simpleTexture.SetPixel(0, 0, Color.white); 21 | simpleTexture.Apply(); 22 | return _simpleRectStyle = new GUIStyle { normal = { background = simpleTexture } }; 23 | } 24 | } 25 | 26 | private static Texture2D _checkerboard; 27 | 28 | public static Texture2D Checkerboard 29 | { 30 | get 31 | { 32 | return _checkerboard ?? (_checkerboard = CreateCheckerTex( 33 | new Color(0.1f, 0.1f, 0.1f, 0.5f), 34 | new Color(0.2f, 0.2f, 0.2f, 0.5f))); 35 | } 36 | } 37 | 38 | #region Utility Functions 39 | 40 | public static string GetAssetPath(string assetFileName) 41 | { 42 | if (!AssetDatabase.GetAllAssetPaths().Any(p => p.EndsWith(assetFileName))) 43 | { 44 | AssetDatabase.Refresh(); 45 | } 46 | string basePath = AssetDatabase.GetAllAssetPaths().First(p => p.EndsWith(assetFileName)); 47 | int lastDelimiter = basePath.LastIndexOf('/') + 1; 48 | basePath = basePath.Remove(lastDelimiter, basePath.Length - lastDelimiter); 49 | return basePath; 50 | } 51 | 52 | public static GUIStyle GetEditorStyle(string style) 53 | { 54 | return EditorGUIUtility.GetBuiltinSkin(EditorGUIUtility.isProSkin ? EditorSkin.Scene : EditorSkin.Inspector).GetStyle(style); 55 | } 56 | 57 | public static GUIStyle GetColoredTextStyle(Color color, FontStyle fontStyle) 58 | { 59 | return new GUIStyle 60 | { 61 | normal = { textColor = color }, 62 | fontStyle = fontStyle 63 | }; 64 | } 65 | 66 | public static int IndexOf(this SerializedProperty prop, string value) 67 | { 68 | if (!prop.isArray) 69 | return -1; 70 | for (int i = 0; i < prop.arraySize; i++) 71 | { 72 | if (prop.GetArrayElementAtIndex(i).stringValue.Equals(value)) 73 | return i; 74 | } 75 | return -1; 76 | } 77 | 78 | public static Vector3 ScrollableSelectableLabel(Vector3 position, string text, GUIStyle style) 79 | { 80 | // Source: yoyo @ https://answers.unity.com/questions/255119/selectablelabel-or-textarea-in-scrollview.html 81 | // Extract scroll position and width from position vector. 82 | Vector2 scrollPos = new Vector2(position.x, position.y); 83 | float width = position.z; 84 | float pixelHeight = style.CalcHeight(new GUIContent(text), width); 85 | 86 | scrollPos = EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(pixelHeight + 10)); 87 | // Calculate height of text. 88 | EditorGUILayout.SelectableLabel(text, style, GUILayout.Height(pixelHeight)); 89 | // Update the width on repaint, based on width of the SelectableLabel's rectangle. 90 | if (Event.current.type == EventType.Repaint) 91 | { 92 | width = GUILayoutUtility.GetLastRect().width; 93 | } 94 | EditorGUILayout.EndScrollView(); 95 | // Put scroll position and width back into the Vector3 used to track position. 96 | return new Vector3(scrollPos.x, scrollPos.y, width); 97 | } 98 | 99 | #endregion Utility Functions 100 | 101 | #region GUI Utilities 102 | 103 | public static bool DrawIconHeader(string key, Texture icon, string caption, Color captionColor, bool canToggle = true) 104 | { 105 | bool state = EditorPrefs.GetBool(key, true); 106 | EditorGUILayout.BeginHorizontal(); 107 | { 108 | EditorGUILayout.LabelField(new GUIContent(icon), EditorStyles.miniLabel, GUILayout.Width(22), GUILayout.Height(22)); 109 | using (new SwitchBackgroundColor(captionColor)) 110 | { 111 | EditorGUILayout.BeginVertical(); 112 | GUILayout.Space(5); 113 | GUIStyle style = new GUIStyle { normal = { textColor = captionColor }, fontStyle = FontStyle.Bold }; 114 | EditorGUILayout.LabelField(caption, style); 115 | EditorGUILayout.EndVertical(); 116 | } 117 | EditorGUILayout.Space(); 118 | string cap = state ? "\u25bc" : "\u25b2"; 119 | if (canToggle) 120 | { 121 | if (GUILayout.Button(cap, EditorStyles.label, GUILayout.Width(16), GUILayout.Height(16))) 122 | { 123 | state = !state; 124 | EditorPrefs.SetBool(key, state); 125 | } 126 | } 127 | } 128 | EditorGUILayout.EndHorizontal(); 129 | GUILayout.Space(2); 130 | return state; 131 | } 132 | 133 | public static void GUIDrawRect(Rect position, Color color) 134 | { 135 | using (new SwitchColor(color)) 136 | { 137 | GUI.Box(position, GUIContent.none, SimpleRectStyle); 138 | } 139 | } 140 | 141 | public static void DrawTiledTexture(Rect rect, Texture tex) 142 | { 143 | GUI.BeginGroup(rect); 144 | { 145 | int width = Mathf.RoundToInt(rect.width); 146 | int height = Mathf.RoundToInt(rect.height); 147 | 148 | for (int y = 0; y < height; y += tex.height) 149 | { 150 | for (int x = 0; x < width; x += tex.width) 151 | { 152 | GUI.DrawTexture(new Rect(x, y, tex.width, tex.height), tex); 153 | } 154 | } 155 | } 156 | GUI.EndGroup(); 157 | } 158 | 159 | public static Texture2D CreateCheckerTex(Color c0, Color c1, int size = 16, int columns = 2) 160 | { 161 | if (columns > size) 162 | throw new ArgumentOutOfRangeException("Cannot generate Checkerboard texture with more columns than texture has pixels"); 163 | if (size % columns != 0) 164 | throw new ArgumentOutOfRangeException("Size has to be dividable by columns"); 165 | 166 | Texture2D tex = new Texture2D(size, size) 167 | { 168 | name = "[EditorHelper] Checker Texture", 169 | hideFlags = HideFlags.DontSave 170 | }; 171 | 172 | int fieldSize = size / columns; 173 | for (int y = 0; y < size; y++) 174 | { 175 | for (int x = 0; x < size; x++) 176 | { 177 | if (y / fieldSize % 2 != 0) 178 | tex.SetPixel(x, y, y / fieldSize % 2 == 0 || x / fieldSize % 2 == 0 ? c1 : c0); 179 | else 180 | tex.SetPixel(x, y, y / fieldSize % 2 != 0 || x / fieldSize % 2 != 0 ? c1 : c0); 181 | } 182 | } 183 | tex.Apply(); 184 | tex.filterMode = FilterMode.Point; 185 | return tex; 186 | } 187 | 188 | public enum OutlineMode { Outside, Centered, Inside } 189 | 190 | public static void DrawOutline(Rect rect, Color color, float borderWidth = 1f, OutlineMode mode = OutlineMode.Outside) 191 | { 192 | if (Event.current.type == EventType.Repaint) 193 | { 194 | Texture2D tex = EditorGUIUtility.whiteTexture; 195 | GUI.color = color; 196 | 197 | switch (mode) 198 | { 199 | case OutlineMode.Outside: 200 | GUI.DrawTexture(new Rect(rect.xMin - borderWidth, rect.yMin - borderWidth, borderWidth, rect.height + borderWidth * 2), tex); // left 201 | GUI.DrawTexture(new Rect(rect.xMax, rect.yMin - borderWidth, borderWidth, rect.height + borderWidth * 2), tex); // right 202 | GUI.DrawTexture(new Rect(rect.xMin, rect.yMin - borderWidth, rect.width, borderWidth), tex); // top 203 | GUI.DrawTexture(new Rect(rect.xMin, rect.yMax, rect.width + borderWidth, borderWidth), tex); // bottom 204 | break; 205 | 206 | case OutlineMode.Centered: 207 | GUI.DrawTexture(new Rect(rect.xMin - borderWidth / 2, rect.yMin - borderWidth / 2, borderWidth, rect.height + borderWidth), tex); // left 208 | GUI.DrawTexture(new Rect(rect.xMax - borderWidth / 2, rect.yMin - borderWidth / 2, borderWidth, rect.height + borderWidth), tex); // right 209 | GUI.DrawTexture(new Rect(rect.xMin - borderWidth / 2, rect.yMin - borderWidth / 2, rect.width + borderWidth, borderWidth), tex); // top 210 | GUI.DrawTexture(new Rect(rect.xMin - borderWidth / 2, rect.yMax - borderWidth / 2, rect.width + borderWidth, borderWidth), tex); // bottom 211 | break; 212 | 213 | case OutlineMode.Inside: 214 | GUI.DrawTexture(new Rect(rect.xMin, rect.yMin, borderWidth, rect.height), tex); // left 215 | GUI.DrawTexture(new Rect(rect.xMax - borderWidth, rect.yMin, borderWidth, rect.height), tex); // right 216 | GUI.DrawTexture(new Rect(rect.xMin, rect.yMin, rect.width, borderWidth), tex); // top 217 | GUI.DrawTexture(new Rect(rect.xMin, rect.yMax - borderWidth, rect.width, borderWidth), tex); // bottom 218 | break; 219 | 220 | default: 221 | throw new ArgumentOutOfRangeException("mode", mode, null); 222 | } 223 | 224 | GUI.color = Color.white; 225 | } 226 | } 227 | 228 | #endregion GUI Utilities 229 | } 230 | 231 | #region Blocks 232 | 233 | public sealed class RoundedBox : IDisposable 234 | { 235 | public RoundedBox() 236 | { 237 | GUILayout.Space(8f); 238 | GUILayout.BeginHorizontal(); 239 | GUILayout.Space(4f); 240 | EditorGUILayout.BeginHorizontal(EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).GetStyle("sv_iconselector_labelselection"), GUILayout.MinHeight(10f)); 241 | GUILayout.BeginVertical(); 242 | GUILayout.Space(4f); 243 | } 244 | 245 | public void Dispose() 246 | { 247 | GUILayout.Space(3f); 248 | GUILayout.EndVertical(); 249 | EditorGUILayout.EndHorizontal(); 250 | GUILayout.Space(3f); 251 | GUILayout.EndHorizontal(); 252 | GUILayout.Space(3f); 253 | } 254 | } 255 | 256 | public sealed class EditorFrame : IDisposable 257 | { 258 | public EditorFrame(string caption, Color captionColor, int width = -1) 259 | { 260 | if (width == -1) 261 | GUILayout.BeginVertical(); 262 | else 263 | GUILayout.BeginVertical(GUILayout.Width(width)); 264 | 265 | GUILayout.Space(8); 266 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 267 | { 268 | GUILayout.Space(11); 269 | using (new EditorBlock(EditorBlock.Orientation.Horizontal, "TL LogicBar 1")) 270 | { 271 | GUILayout.Space(4); 272 | EditorGUILayout.LabelField(caption, EditorHelper.GetColoredTextStyle(captionColor, FontStyle.Bold)); 273 | } 274 | GUILayout.FlexibleSpace(); 275 | GUILayout.Space(11); 276 | } 277 | GUILayout.Space(-11); 278 | 279 | EditorGUILayout.BeginVertical("GroupBox"); 280 | } 281 | 282 | public void Dispose() 283 | { 284 | EditorGUILayout.EndVertical(); 285 | EditorGUILayout.EndVertical(); 286 | } 287 | } 288 | 289 | public sealed class FoldableEditorFrame : IDisposable 290 | { 291 | public bool Expanded; 292 | 293 | public FoldableEditorFrame(string key, string caption, Color captionColor, Color folderColor, int width = -1) 294 | { 295 | Expanded = EditorPrefs.GetBool(key, true); 296 | Draw(ref Expanded, caption, captionColor, folderColor, width); 297 | EditorPrefs.SetBool(key, Expanded); 298 | } 299 | 300 | public FoldableEditorFrame(ref bool expanded, string caption, Color captionColor, Color folderColor, int width = -1) 301 | { 302 | Draw(ref expanded, caption, captionColor, folderColor, width); 303 | } 304 | 305 | private void Draw(ref bool expanded, string caption, Color captionColor, Color folderColor, int width = -1) 306 | { 307 | Expanded = expanded; 308 | 309 | if (width == -1) 310 | GUILayout.BeginVertical(); 311 | else 312 | GUILayout.BeginVertical(GUILayout.Width(width)); 313 | GUILayout.Space(8); 314 | 315 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 316 | { 317 | GUIStyle coloredTextStyle = EditorHelper.GetColoredTextStyle(captionColor, FontStyle.Bold); 318 | float textWidth = coloredTextStyle.CalcSize(new GUIContent(caption)).x + 15; 319 | 320 | GUILayout.Space(11); 321 | expanded = EditorGUILayout.Toggle(expanded, "TL LogicBar 0", GUILayout.Width(Mathf.Max(textWidth, 196)), GUILayout.Height(24)); 322 | GUILayout.Space(-Mathf.Max(textWidth, 196) + 4); 323 | using (new EditorBlock(EditorBlock.Orientation.Vertical)) 324 | { 325 | GUILayout.Space(6); 326 | EditorGUILayout.LabelField(caption, coloredTextStyle); 327 | } 328 | } 329 | GUILayout.Space(-11); 330 | if (Expanded) 331 | { 332 | using (new SwitchColor(folderColor)) 333 | { 334 | EditorGUILayout.BeginVertical("GroupBox", GUILayout.ExpandWidth(true)); 335 | } 336 | } 337 | } 338 | 339 | public void Dispose() 340 | { 341 | EditorGUILayout.EndVertical(); 342 | if (Expanded) 343 | { 344 | EditorGUILayout.EndVertical(); 345 | } 346 | } 347 | } 348 | 349 | public sealed class EditorBlock : IDisposable 350 | { 351 | public enum Orientation 352 | { 353 | Horizontal, 354 | Vertical 355 | } 356 | 357 | private readonly Orientation _orientation; 358 | 359 | public EditorBlock(Orientation orientation, string style, params GUILayoutOption[] options) 360 | { 361 | _orientation = orientation; 362 | if (orientation == Orientation.Horizontal) 363 | { 364 | EditorGUILayout.BeginHorizontal(string.IsNullOrEmpty(style) ? GUIStyle.none : style, options); 365 | } 366 | else 367 | { 368 | EditorGUILayout.BeginVertical(string.IsNullOrEmpty(style) ? GUIStyle.none : style, options); 369 | } 370 | } 371 | 372 | public EditorBlock(Orientation orientation, string style) : this(orientation, style, new GUILayoutOption[] { }) 373 | { 374 | } 375 | 376 | public EditorBlock(Orientation orientation) : this(orientation, null, new GUILayoutOption[] { }) 377 | { 378 | } 379 | 380 | public EditorBlock(Orientation orientation, params GUILayoutOption[] layoutOptions) : this(orientation, null, layoutOptions) 381 | { 382 | } 383 | 384 | public void Dispose() 385 | { 386 | if (_orientation == Orientation.Horizontal) 387 | { 388 | EditorGUILayout.EndHorizontal(); 389 | } 390 | else 391 | { 392 | EditorGUILayout.EndVertical(); 393 | } 394 | } 395 | } 396 | 397 | public sealed class SwitchColor : IDisposable 398 | { 399 | private readonly Color _savedColor; 400 | 401 | public SwitchColor(Color newColor) 402 | { 403 | _savedColor = GUI.color; 404 | GUI.color = newColor; 405 | } 406 | 407 | public void Dispose() 408 | { 409 | GUI.color = _savedColor; 410 | } 411 | } 412 | 413 | public sealed class SwitchBackgroundColor : IDisposable 414 | { 415 | private readonly Color _savedColor; 416 | 417 | public SwitchBackgroundColor(Color newColor) 418 | { 419 | _savedColor = GUI.backgroundColor; 420 | GUI.backgroundColor = newColor; 421 | } 422 | 423 | public void Dispose() 424 | { 425 | GUI.backgroundColor = _savedColor; 426 | } 427 | } 428 | 429 | public sealed class SwitchGUIDepth : IDisposable 430 | { 431 | private readonly int _savedDepth; 432 | 433 | public SwitchGUIDepth(int depth) 434 | { 435 | _savedDepth = GUI.depth; 436 | GUI.depth = depth; 437 | } 438 | 439 | public void Dispose() 440 | { 441 | GUI.depth = _savedDepth; 442 | } 443 | } 444 | 445 | public class IndentBlock : IDisposable 446 | { 447 | public IndentBlock() 448 | { 449 | EditorGUI.indentLevel++; 450 | } 451 | 452 | public void Dispose() 453 | { 454 | EditorGUI.indentLevel--; 455 | } 456 | } 457 | 458 | public class ScrollViewBlock : IDisposable 459 | { 460 | public ScrollViewBlock(ref Vector2 scrollPosition, params GUILayoutOption[] options) 461 | { 462 | scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, options); 463 | } 464 | 465 | public void Dispose() 466 | { 467 | EditorGUILayout.EndScrollView(); 468 | } 469 | } 470 | 471 | public class PaddedBlock : IDisposable 472 | { 473 | public PaddedBlock(int left, int right, int top, int bottom) 474 | { 475 | GUIStyle style = new GUIStyle(EditorStyles.inspectorDefaultMargins) { padding = new RectOffset(left, right, top, bottom) }; 476 | EditorGUILayout.BeginVertical(style); 477 | } 478 | 479 | public PaddedBlock(int padding) : this(padding, padding, padding, padding) 480 | { 481 | } 482 | 483 | public void Dispose() 484 | { 485 | EditorGUILayout.EndVertical(); 486 | } 487 | } 488 | 489 | public sealed class FoldableBlock : IDisposable 490 | { 491 | private readonly Color _defaultBackgroundColor; 492 | 493 | private readonly bool _expanded; 494 | 495 | public FoldableBlock(ref bool expanded, string header) : this(ref expanded, header, null) 496 | { 497 | } 498 | 499 | public FoldableBlock(ref bool expanded, string header, Texture2D icon) 500 | { 501 | _defaultBackgroundColor = GUI.backgroundColor; 502 | EditorGUILayout.BeginHorizontal(); 503 | GUI.changed = false; 504 | if (!GUILayout.Toggle(true, new GUIContent("" + header + "", icon), "dragtab", GUILayout.MinWidth(20f))) 505 | expanded = !expanded; 506 | EditorGUILayout.EndHorizontal(); 507 | GUILayout.Space(-2f); 508 | 509 | if (expanded) 510 | { 511 | GUILayout.BeginHorizontal(); 512 | EditorGUILayout.BeginHorizontal("TextArea", GUILayout.MinHeight(10f)); 513 | GUILayout.BeginVertical(); 514 | } 515 | _expanded = expanded; 516 | } 517 | 518 | public void Dispose() 519 | { 520 | if (_expanded) 521 | { 522 | GUILayout.EndVertical(); 523 | EditorGUILayout.EndHorizontal(); 524 | GUILayout.EndHorizontal(); 525 | GUI.backgroundColor = _defaultBackgroundColor; 526 | } 527 | } 528 | } 529 | 530 | #endregion Blocks 531 | } -------------------------------------------------------------------------------- /Editor/EditorHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c3e20c87f936934aaefd4030d9b275b 3 | timeCreated: 1434825476 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/PropertyDrawer.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using UnityEditor; 3 | using UnityEditorInternal; 4 | using UnityEngine; 5 | 6 | namespace UnityEditorHelper 7 | { 8 | [CustomPropertyDrawer(typeof(LayerAttribute))] 9 | public class LayerPropertyDrawer : PropertyDrawer 10 | { 11 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 12 | { 13 | if (property.propertyType != SerializedPropertyType.Integer) 14 | { 15 | Debug.LogWarning("LayerAttribute can only be applied on integer properties/fields"); 16 | return; 17 | } 18 | 19 | property.intValue = EditorGUI.LayerField(position, property.name, property.intValue); 20 | } 21 | } 22 | 23 | [CustomPropertyDrawer(typeof(LimitAttribute))] 24 | public class LimitPropertyDrawer : PropertyDrawer 25 | { 26 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 27 | { 28 | if (property.propertyType != SerializedPropertyType.Integer) 29 | { 30 | Debug.LogWarning("LimitAttribute can only be applied on integer properties/fields"); 31 | return; 32 | } 33 | 34 | LimitAttribute limiter = (LimitAttribute)attribute; 35 | property.intValue = limiter.Limit(EditorGUI.IntField(position, property.name, property.intValue)); 36 | } 37 | } 38 | 39 | [CustomPropertyDrawer(typeof(SortingLayerAttribute))] 40 | public class SortingLayerDrawer : PropertyDrawer 41 | { 42 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 43 | { 44 | if (property.propertyType != SerializedPropertyType.Integer) 45 | { 46 | Debug.LogWarning("SortingLayerAttributes can only be applied on integer properties/fields"); 47 | return; 48 | } 49 | EditorGUI.LabelField(position, label); 50 | 51 | position.x += EditorGUIUtility.labelWidth; 52 | position.width -= EditorGUIUtility.labelWidth; 53 | 54 | string[] sortingLayerNames = GetSortingLayerNames(); 55 | int[] sortingLayerIDs = GetSortingLayerIDs(); 56 | 57 | int sortingLayerIndex = Mathf.Max(0, System.Array.IndexOf(sortingLayerIDs, property.intValue)); 58 | sortingLayerIndex = EditorGUI.Popup(position, sortingLayerIndex, sortingLayerNames); 59 | property.intValue = sortingLayerIDs[sortingLayerIndex]; 60 | } 61 | 62 | private string[] GetSortingLayerNames() 63 | { 64 | System.Type internalEditorUtilityType = typeof(InternalEditorUtility); 65 | PropertyInfo sortingLayersProperty = internalEditorUtilityType.GetProperty("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic); 66 | return (string[])sortingLayersProperty.GetValue(null, new object[0]); 67 | } 68 | 69 | private int[] GetSortingLayerIDs() 70 | { 71 | System.Type internalEditorUtilityType = typeof(InternalEditorUtility); 72 | PropertyInfo sortingLayersProperty = internalEditorUtilityType.GetProperty("sortingLayerUniqueIDs", BindingFlags.Static | BindingFlags.NonPublic); 73 | return (int[])sortingLayersProperty.GetValue(null, new object[0]); 74 | } 75 | } 76 | 77 | [CustomPropertyDrawer(typeof(TagAttribute))] 78 | public class TagPropertyDrawer : PropertyDrawer 79 | { 80 | public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) 81 | { 82 | if (property.propertyType != SerializedPropertyType.String) 83 | { 84 | Debug.LogWarning("TagAttribute can only be applied on string properties/fields"); 85 | return; 86 | } 87 | 88 | property.stringValue = EditorGUI.TagField(position, property.name, property.stringValue); 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Editor/PropertyDrawer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db7cad7f95703f84ea5d8b4baea7a483 3 | timeCreated: 1434825477 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Editor/SelectionWizard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | using UnityEditor; 6 | 7 | namespace UnityEditorHelper 8 | { 9 | public abstract class SelectionWizard : ScriptableWizard 10 | { 11 | public static SelectionWizard Instance; 12 | 13 | private void OnEnable() 14 | { 15 | Instance = this; 16 | } 17 | 18 | private void OnDisable() 19 | { 20 | Instance = null; 21 | } 22 | 23 | private Vector2 _scrollPos = Vector2.zero; 24 | private string _searchString = ""; 25 | protected List Elements; 26 | protected float ItemSize; 27 | protected float Padding; 28 | protected Func SearchStringFilterFunc { get; set; } 29 | private float _clickTime; 30 | protected T Selected; 31 | protected Func TextureGetter; 32 | protected Func CaptionGetter; 33 | protected Action SelectionCallback; 34 | 35 | 36 | private static Texture2D _selectionTexture; 37 | protected static Texture2D selectionTexture { get { return _selectionTexture ?? CreateSelectionTexture(); } } 38 | 39 | const float textHeight = 26; 40 | 41 | 42 | private static Texture2D CreateSelectionTexture() 43 | { 44 | Texture2D tex = new Texture2D(2, 2, TextureFormat.ARGB32, false) 45 | { 46 | name = "[EditorHelper] Selection Texture", 47 | hideFlags = HideFlags.DontSave 48 | }; 49 | 50 | tex.SetPixels(new[] { Color.blue, Color.blue, Color.blue, Color.blue }); 51 | tex.filterMode = FilterMode.Point; 52 | tex.Apply(); 53 | _selectionTexture = tex; 54 | return tex; 55 | } 56 | 57 | public static void Show(string caption, List elements, Func searchStringFilterFunc, Func textureGetter, 58 | Func captionGetter, Action selectionCallback, float itemSize = 80, float padding = 10) where K : SelectionWizard 59 | { 60 | if (Instance != null) 61 | { 62 | Instance.Close(); 63 | Instance = null; 64 | } 65 | 66 | SelectionWizard self = DisplayWizard(caption); 67 | self.Elements = elements; 68 | self.SearchStringFilterFunc = searchStringFilterFunc; 69 | self.ItemSize = itemSize; 70 | self.Padding = padding; 71 | self.TextureGetter = textureGetter; 72 | self.CaptionGetter = captionGetter; 73 | self.SelectionCallback = selectionCallback; 74 | } 75 | 76 | private void OnGUI() 77 | { 78 | EditorGUIUtility.labelWidth = 80; 79 | bool close = false; 80 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 81 | { 82 | GUILayout.Space(84f); 83 | _searchString = EditorGUILayout.TextField("", _searchString, "SearchTextField"); 84 | 85 | if (GUILayout.Button("", "SearchCancelButton", GUILayout.Width(18f))) 86 | { 87 | _searchString = ""; 88 | GUIUtility.keyboardControl = 0; 89 | } 90 | GUILayout.Space(84); 91 | } 92 | 93 | List elements = Elements.Where(arg => SearchStringFilterFunc.Invoke(arg, _searchString)).ToList(); 94 | 95 | float padded = ItemSize + Padding; 96 | int columns = Math.Max(1, Mathf.FloorToInt(Screen.width / padded)); 97 | int offset = 0; 98 | Rect rect = new Rect(10, 0, ItemSize, ItemSize); 99 | GUILayout.Space(10f); 100 | using (new ScrollViewBlock(ref _scrollPos)) 101 | { 102 | int rows = 1; 103 | 104 | while (offset < elements.Count) 105 | { 106 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 107 | { 108 | int col = 0; 109 | rect.x = 10; 110 | 111 | for (; offset < elements.Count; ++offset) 112 | { 113 | Rect frame = new Rect(rect); 114 | frame.xMin -= 2; 115 | frame.yMin -= 2; 116 | frame.xMax += 2; 117 | frame.yMax += 8 + textHeight; 118 | 119 | if (Selected != null && ReferenceEquals(Selected, elements[offset])) 120 | { 121 | GUI.DrawTexture(frame, selectionTexture, ScaleMode.StretchToFill); 122 | } 123 | 124 | if (GUI.Button(rect, "")) 125 | { 126 | if (Event.current.button == 0) 127 | { 128 | float delta = Time.realtimeSinceStartup - _clickTime; 129 | _clickTime = Time.realtimeSinceStartup; 130 | 131 | if (!ReferenceEquals(Selected, elements[offset])) 132 | { 133 | Selected = elements[offset]; 134 | Repaint(); 135 | } 136 | else if (delta < 0.5f) 137 | close = true; 138 | } 139 | } 140 | 141 | if (Event.current.type == EventType.Repaint) 142 | { 143 | GUI.DrawTexture(rect, EditorHelper.Checkerboard, ScaleMode.ScaleToFit); 144 | GUI.DrawTexture(rect, TextureGetter.Invoke(elements[offset]), ScaleMode.ScaleAndCrop); 145 | } 146 | 147 | GUI.backgroundColor = new Color(1f, 1f, 1f, 0.5f); 148 | GUI.contentColor = new Color(1f, 1f, 1f, 0.7f); 149 | GUI.Label(new Rect(rect.x, rect.y + rect.height, rect.width, 32f), CaptionGetter.Invoke(elements[offset]), "ProgressBarBack"); 150 | GUI.contentColor = Color.white; 151 | GUI.backgroundColor = Color.white; 152 | 153 | if (++col >= columns) 154 | { 155 | ++offset; 156 | break; 157 | } 158 | rect.x += padded; 159 | } 160 | } 161 | GUILayout.Space(padded); 162 | rect.y += padded + textHeight; 163 | ++rows; 164 | } 165 | GUILayout.Space(rows * textHeight); 166 | } 167 | if (close) 168 | { 169 | if (SelectionCallback != null) 170 | SelectionCallback.Invoke(Selected); 171 | Close(); 172 | } 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /Editor/SelectionWizard.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 663cd1bdf8d65ea47b88ee6f954a393b 3 | timeCreated: 1519320015 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Example/DemoObject.cs: -------------------------------------------------------------------------------- 1 | using UnityEditorHelper; 2 | using UnityEngine; 3 | 4 | public class DemoObject : ScriptableObject 5 | { 6 | [Layer] 7 | public int Layer; 8 | 9 | [Tag] 10 | public string Tag; 11 | 12 | [SortingLayer] 13 | public int SortingLayer; 14 | 15 | [Limit(0)] 16 | public int LimitLow; 17 | 18 | [Limit(-10, 42)] 19 | public int LimitBoth; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Example/DemoObject.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 384a49a068f68564b813e8710a281b64 3 | timeCreated: 1519321980 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Example/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b36ef011582d2f642b7cd2c5c46303e7 3 | folderAsset: yes 4 | timeCreated: 1519321541 5 | licenseType: Free 6 | DefaultImporter: 7 | externalObjects: {} 8 | userData: 9 | assetBundleName: 10 | assetBundleVariant: 11 | -------------------------------------------------------------------------------- /Example/Editor/Base32Encoding.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Derived from https://github.com/google/google-authenticator-android/blob/master/AuthenticatorApp/src/main/java/com/google/android/apps/authenticator/Base32String.java 3 | * 4 | * Copyright (C) 2016 BravoTango86 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | using System; 20 | using System.Collections.Generic; 21 | using System.Text; 22 | using System.Text.RegularExpressions; 23 | 24 | public static class Base32 25 | { 26 | 27 | private static readonly char[] DIGITS; 28 | private static readonly int MASK; 29 | private static readonly int SHIFT; 30 | private static Dictionary CHAR_MAP = new Dictionary(); 31 | private const string SEPARATOR = "-"; 32 | 33 | static Base32() 34 | { 35 | DIGITS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".ToCharArray(); 36 | MASK = DIGITS.Length - 1; 37 | SHIFT = numberOfTrailingZeros(DIGITS.Length); 38 | for (int i = 0; i < DIGITS.Length; i++) CHAR_MAP[DIGITS[i]] = i; 39 | } 40 | 41 | private static int numberOfTrailingZeros(int i) 42 | { 43 | // HD, Figure 5-14 44 | int y; 45 | if (i == 0) return 32; 46 | int n = 31; 47 | y = i << 16; if (y != 0) { n = n - 16; i = y; } 48 | y = i << 8; if (y != 0) { n = n - 8; i = y; } 49 | y = i << 4; if (y != 0) { n = n - 4; i = y; } 50 | y = i << 2; if (y != 0) { n = n - 2; i = y; } 51 | return n - (int)((uint)(i << 1) >> 31); 52 | } 53 | 54 | public static string FromBase32(this string input) 55 | { 56 | return Encoding.ASCII.GetString(Decode(input)); 57 | } 58 | 59 | public static string ToBase32(this string input) 60 | { 61 | return Encode(Encoding.ASCII.GetBytes(input)); 62 | } 63 | 64 | public static byte[] Decode(string encoded) 65 | { 66 | // Remove whitespace and separators 67 | encoded = encoded.Trim().Replace(SEPARATOR, ""); 68 | 69 | // Remove padding. Note: the padding is used as hint to determine how many 70 | // bits to decode from the last incomplete chunk (which is commented out 71 | // below, so this may have been wrong to start with). 72 | encoded = Regex.Replace(encoded, "[=]*$", ""); 73 | 74 | // Canonicalize to all upper case 75 | encoded = encoded.ToUpper(); 76 | if (encoded.Length == 0) 77 | { 78 | return new byte[0]; 79 | } 80 | int encodedLength = encoded.Length; 81 | int outLength = encodedLength * SHIFT / 8; 82 | byte[] result = new byte[outLength]; 83 | int buffer = 0; 84 | int next = 0; 85 | int bitsLeft = 0; 86 | foreach (char c in encoded) 87 | { 88 | if (!CHAR_MAP.ContainsKey(c)) 89 | { 90 | throw new DecodingException("Illegal character: " + c); 91 | } 92 | buffer <<= SHIFT; 93 | buffer |= CHAR_MAP[c] & MASK; 94 | bitsLeft += SHIFT; 95 | if (bitsLeft >= 8) 96 | { 97 | result[next++] = (byte)(buffer >> (bitsLeft - 8)); 98 | bitsLeft -= 8; 99 | } 100 | } 101 | // We'll ignore leftover bits for now. 102 | // 103 | // if (next != outLength || bitsLeft >= SHIFT) { 104 | // throw new DecodingException("Bits left: " + bitsLeft); 105 | // } 106 | return result; 107 | } 108 | 109 | 110 | public static string Encode(byte[] data, bool padOutput = false) 111 | { 112 | if (data.Length == 0) 113 | { 114 | return ""; 115 | } 116 | 117 | // SHIFT is the number of bits per output character, so the length of the 118 | // output is the length of the input multiplied by 8/SHIFT, rounded up. 119 | if (data.Length >= 1 << 28) 120 | { 121 | // The computation below will fail, so don't do it. 122 | throw new ArgumentOutOfRangeException("data"); 123 | } 124 | 125 | int outputLength = (data.Length * 8 + SHIFT - 1) / SHIFT; 126 | StringBuilder result = new StringBuilder(outputLength); 127 | 128 | int buffer = data[0]; 129 | int next = 1; 130 | int bitsLeft = 8; 131 | while (bitsLeft > 0 || next < data.Length) 132 | { 133 | if (bitsLeft < SHIFT) 134 | { 135 | if (next < data.Length) 136 | { 137 | buffer <<= 8; 138 | buffer |= data[next++] & 0xff; 139 | bitsLeft += 8; 140 | } 141 | else 142 | { 143 | int pad = SHIFT - bitsLeft; 144 | buffer <<= pad; 145 | bitsLeft += pad; 146 | } 147 | } 148 | int index = MASK & (buffer >> (bitsLeft - SHIFT)); 149 | bitsLeft -= SHIFT; 150 | result.Append(DIGITS[index]); 151 | } 152 | if (padOutput) 153 | { 154 | int padding = 8 - result.Length % 8; 155 | if (padding > 0) result.Append(new string('=', padding == 8 ? 0 : padding)); 156 | } 157 | return result.ToString(); 158 | } 159 | 160 | private class DecodingException : Exception 161 | { 162 | public DecodingException(string message) : base(message) 163 | { 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /Example/Editor/Base32Encoding.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de420fedcefd5b644b5bd0d34f2d134d 3 | timeCreated: 1519762039 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Example/Editor/DemoScenarios.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEditorHelper; 5 | using UnityEngine; 6 | 7 | public static class DemoScenarios 8 | { 9 | public static DemoScenario EditorBlockHorizontal = new DemoScenario 10 | { 11 | Name = "EditorBlock (horizontal)", 12 | Scenarios = new List 13 | { 14 | new DemoScenarioContent("Simple horizontal block", "Same as EditorGUILayout.BeginHorizontal/EndHorizontal", 15 | delegate 16 | { 17 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 18 | { 19 | EditorGUILayout.TextField("Text", ""); 20 | EditorGUILayout.ColorField("Color", Color.grey); 21 | } 22 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXEQ33SNF5G63TUMFWCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 23 | new DemoScenarioContent("Horizontal block with named style 'Box'", 24 | "Same as above but uses a style of a given name", 25 | delegate 26 | { 27 | using (new EditorBlock(EditorBlock.Orientation.Horizontal, "Box")) 28 | { 29 | EditorGUILayout.TextField("Text", ""); 30 | EditorGUILayout.ColorField("Color", Color.grey); 31 | } 32 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXEQ33SNF5G63TUMFWCYIBCIJXXQIRJFEFHWCRAEAQCARLENF2G64SHKVEUYYLZN52XILSUMV4HIRTJMVWGIKBCKRSXQ5BCFQQCEIRJHMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXEG33MN5ZEM2LFNRSCQISDN5WG64RCFQQEG33MN5ZC4Z3SMV4SSOYKPU======".FromBase32()), 33 | new DemoScenarioContent("Horizontal block with named style 'GroupBox'", 34 | "Same as above but uses a style of a given name", 35 | delegate 36 | { 37 | using (new EditorBlock(EditorBlock.Orientation.Horizontal, "GroupBox")) 38 | { 39 | EditorGUILayout.TextField("Text", ""); 40 | EditorGUILayout.ColorField("Color", Color.grey); 41 | } 42 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXEQ33SNF5G63TUMFWCYIBCI5ZG65LQIJXXQIRJFEFHWCRAEAQCARLENF2G64SHKVEUYYLZN52XILSUMV4HIRTJMVWGIKBCKRSXQ5BCFQQCEIRJHMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXEG33MN5ZEM2LFNRSCQISDN5WG64RCFQQEG33MN5ZC4Z3SMV4SSOYKPU======".FromBase32()), 43 | new DemoScenarioContent("Horizontal block with named style 'TL LogicBar 1'", 44 | "Same as above but uses a style of a given name", 45 | delegate 46 | { 47 | using (new EditorBlock(EditorBlock.Orientation.Horizontal, "TL LogicBar 1")) 48 | { 49 | EditorGUILayout.TextField("Text", ""); 50 | EditorGUILayout.ColorField("Color", Color.grey); 51 | } 52 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXFMZLSORUWGYLMFQQCEVCMEBGG6Z3JMNBGC4RAGERCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 53 | } 54 | }; 55 | 56 | public static DemoScenario EditorBlockVertical = new DemoScenario 57 | { 58 | Name = "EditorBlock (vertical)", 59 | Scenarios = new List 60 | { 61 | new DemoScenarioContent("Simple vertical block", "Same as EditorGUILayout.BeginVertical/EndVertical", 62 | delegate 63 | { 64 | using (new EditorBlock(EditorBlock.Orientation.Vertical)) 65 | { 66 | DrawSampleControls(); 67 | } 68 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXFMZLSORUWGYLMFEUQU6YKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOKRSXQ5CGNFSWYZBIEJKGK6DUEIWCAIRCFE5QUIBAEAQEKZDJORXXER2VJFGGC6LPOV2C4Q3PNRXXERTJMVWGIKBCINXWY33SEIWCAQ3PNRXXELTHOJSXSKJ3BJ6QU===".FromBase32()), 69 | new DemoScenarioContent("Vertical block with named style 'Box'", 70 | "Same as above but uses a style of a given name", 71 | delegate 72 | { 73 | using (new EditorBlock(EditorBlock.Orientation.Vertical, "Box")) 74 | { 75 | DrawSampleControls(); 76 | } 77 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXFMZLSORUWGYLMFQQCEQTPPARCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 78 | new DemoScenarioContent("Vertical block with named style 'GroupBox'", 79 | "Same as above but uses a style of a given name", 80 | delegate 81 | { 82 | using (new EditorBlock(EditorBlock.Orientation.Vertical, "GroupBox")) 83 | { 84 | DrawSampleControls(); 85 | } 86 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXFMZLSORUWGYLMFQQCER3SN52XAQTPPARCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 87 | new DemoScenarioContent("Vertical block with named style 'TE NodeBox'", 88 | "Same as above but uses a style of a given name", 89 | delegate 90 | { 91 | using (new EditorBlock(EditorBlock.Orientation.Vertical, "TE NodeBox")) 92 | { 93 | DrawSampleControls(); 94 | } 95 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEE3DPMNVSQRLENF2G64SCNRXWG2ZOJ5ZGSZLOORQXI2LPNYXFMZLSORUWGYLMFQQCEVCMEBGG6Z3JMNBGC4RAGERCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 96 | } 97 | }; 98 | 99 | public static DemoScenario RoundedBox = new DemoScenario 100 | { 101 | Name = "RoundedBox", 102 | Scenarios = new List 103 | { 104 | new DemoScenarioContent("RoundedBox", "A simple solid colored rectangle with rounded corners. Currently, no further customizations are available", 105 | delegate 106 | { 107 | using (new RoundedBox()) 108 | { 109 | DrawSampleControls(); 110 | } 111 | }, "OVZWS3THEAUG4ZLXEBJG65LOMRSWIQTPPAUCSKIKPMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOINXWY33SIZUWK3DEFAREG33MN5ZCELBAINXWY33SFZTXEZLZFE5QU7I=".FromBase32()), 112 | new DemoScenarioContent("RoundedBox with color", "For the moment this needs 2 SwitchColor blocks. This will be improved in a future release", 113 | delegate 114 | { 115 | using (new SwitchColor(Color.cyan)) 116 | { 117 | using (new RoundedBox()) 118 | { 119 | using (new SwitchColor(Color.white)) 120 | { 121 | DrawSampleControls(); 122 | } 123 | } 124 | } 125 | }, "OVZWS3THEAUG4ZLXEBJXO2LUMNUEG33MN5ZCQQ3PNRXXELTDPFQW4KJJBJ5QUIBAEAQHK43JNZTSAKDOMV3SAUTPOVXGIZLEIJXXQKBJFEFCAIBAEB5QUIBAEAQCAIBAEB2XG2LOM4QCQ3TFO4QFG53JORRWQQ3PNRXXEKCDN5WG64ROO5UGS5DFFEUQUIBAEAQCAIBAEB5QUIBAEAQCAIBAEAQCAIBAIVSGS5DPOJDVKSKMMF4W65LUFZKGK6DUIZUWK3DEFARFIZLYOQRCYIBCEIUTWCRAEAQCAIBAEAQCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXEG33MN5ZEM2LFNRSCQISDN5WG64RCFQQEG33MN5ZC4Z3SMV4SSOYKEAQCAIBAEAQCA7IKEAQCAID5BJ6Q====".FromBase32()), 126 | } 127 | }; 128 | 129 | public static DemoScenario EditorFrame = new DemoScenario 130 | { 131 | Name = "EditorFrame", 132 | Scenarios = new List 133 | { 134 | new DemoScenarioContent("EditorFrame", "A box with a header looking like an index card", 135 | delegate 136 | { 137 | using (new EditorFrame("Caption", Color.white)) 138 | { 139 | DrawSampleControls(); 140 | } 141 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEM4TBNVSSQISDMFYHI2LPNYRCYICDN5WG64ROO5UGS5DFFEUQU6YKEAQCAICFMRUXI33SI5KUSTDBPFXXK5BOKRSXQ5CGNFSWYZBIEJKGK6DUEIWCAIRCFE5QUIBAEAQEKZDJORXXER2VJFGGC6LPOV2C4Q3PNRXXERTJMVWGIKBCINXWY33SEIWCAQ3PNRXXELTHOJSXSKJ3BJ6Q====".FromBase32()), 142 | new DemoScenarioContent("EditorFrame, colored Caption", "Same as above, but with customized color for the caption", 143 | delegate 144 | { 145 | using (new EditorFrame("Caption", Color.cyan)) 146 | { 147 | DrawSampleControls(); 148 | } 149 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEM4TBNVSSQISDMFYHI2LPNYRCYICDN5WG64ROMN4WC3RJFEFHWCRAEAQCARLENF2G64SHKVEUYYLZN52XILSUMV4HIRTJMVWGIKBCKRSXQ5BCFQQCEIRJHMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXEG33MN5ZEM2LFNRSCQISDN5WG64RCFQQEG33MN5ZC4Z3SMV4SSOYKPU======".FromBase32()), 150 | new DemoScenarioContent("EditorFrame, fixed width", "Same as above, but with a fixed width", 151 | delegate 152 | { 153 | using (new EditorFrame("Caption", Color.cyan, 500)) 154 | { 155 | DrawSampleControls(); 156 | } 157 | }, "OVZWS3THEAUG4ZLXEBCWI2LUN5ZEM4TBNVSSQISDMFYHI2LPNYRCYICDN5WG64ROMN4WC3RMEA2TAMBJFEFHWCRAEAQCARLENF2G64SHKVEUYYLZN52XILSUMV4HIRTJMVWGIKBCKRSXQ5BCFQQCEIRJHMFCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXEG33MN5ZEM2LFNRSCQISDN5WG64RCFQQEG33MN5ZC4Z3SMV4SSOYKPU======".FromBase32()), 158 | } 159 | }; 160 | 161 | public static DemoScenario FoldableEditorFrame = new DemoScenario 162 | { 163 | Name = "FoldableEditorFrame", 164 | Scenarios = new List 165 | { 166 | new DemoScenarioContent("FoldableEditorFrame", "Similar to EditorFrame, but can be collapsed by clicking on the header", 167 | delegate 168 | { 169 | using (FoldableEditorFrame foldable = new FoldableEditorFrame("[FoldableEditorFrameExample]", "Click me!", Color.white, Color.white)) 170 | { 171 | if(foldable.Expanded) 172 | { 173 | DrawSampleControls(); 174 | } 175 | else 176 | GUILayout.Space(10); 177 | } 178 | }, "OVZWS3THEAUEM33MMRQWE3DFIVSGS5DPOJDHEYLNMUQGM33MMRQWE3DFEA6SA3TFO4QEM33MMRQWE3DFIVSGS5DPOJDHEYLNMUUCEW2GN5WGIYLCNRSUKZDJORXXERTSMFWWKRLYMFWXA3DFLURCYIBCINWGSY3LEBWWKIJCFQQEG33MN5ZC453INF2GKLBAINXWY33SFZ3WQ2LUMUUSSCT3BIQCAIBANFTCQZTPNRSGCYTMMUXEK6DQMFXGIZLEFEFCAIBAEB5QUIBAEAQCAIBAEBCWI2LUN5ZEOVKJJRQXS33VOQXFIZLYORDGSZLMMQUCEVDFPB2CELBAEIRCSOYKEAQCAIBAEAQCARLENF2G64SHKVEUYYLZN52XILSDN5WG64SGNFSWYZBIEJBW63DPOIRCYICDN5WG64ROM5ZGK6JJHMFCAIBAEB6QUIBAEAQGK3DTMUFCAIBAEAQCAIBAI5KUSTDBPFXXK5BOKNYGCY3FFAYTAKJ3BJ6Q====".FromBase32()), 179 | new DemoScenarioContent("FoldableEditorFrame, colored caption and content", "Same as above, but with customized colors", 180 | delegate 181 | { 182 | using (FoldableEditorFrame foldable = new FoldableEditorFrame("[FoldableEditorFrameExample]", "Click me!", Color.cyan, Color.red)) 183 | { 184 | if(foldable.Expanded) 185 | { 186 | DrawSampleControls(); 187 | } 188 | else 189 | GUILayout.Space(10); 190 | } 191 | }, "OVZWS3THEAUEM33MMRQWE3DFIVSGS5DPOJDHEYLNMUQGM33MMRQWE3DFEA6SA3TFO4QEM33MMRQWE3DFIVSGS5DPOJDHEYLNMUUCEW2GN5WGIYLCNRSUKZDJORXXERTSMFWWKRLYMFWXA3DFLURCYIBCINWGSY3LEBWWKIJCFQQEG33MN5ZC4Y3ZMFXCYICDN5WG64ROOJSWIKJJBJ5QUIBAEAQGSZRIMZXWYZDBMJWGKLSFPBYGC3TEMVSCSCRAEAQCA6YKEAQCAIBAEAQCARLENF2G64SHKVEUYYLZN52XILSUMV4HIRTJMVWGIKBCKRSXQ5BCFQQCEIRJHMFCAIBAEAQCAIBAIVSGS5DPOJDVKSKMMF4W65LUFZBW63DPOJDGSZLMMQUCEQ3PNRXXEIRMEBBW63DPOIXGO4TFPEUTWCRAEAQCA7IKEAQCAIDFNRZWKCRAEAQCAIBAEAQEOVKJJRQXS33VOQXFG4DBMNSSQMJQFE5Q====".FromBase32()), 192 | } 193 | }; 194 | 195 | //ToDo 196 | //SwitchColor 197 | //SwitchBackgroundColor 198 | //SwitchGUIDepth 199 | //IndentBlock 200 | //ScrollViewBlock 201 | //PaddedBlock 202 | //FoldableBlock 203 | 204 | private static Vector2 _switchGUIDepthScrollPos; 205 | 206 | public static DemoScenario SwitchGUIDepth = new DemoScenario 207 | { 208 | Name = "SwitchGUIDepth", 209 | Scenarios = new List 210 | { 211 | new DemoScenarioContent("SwitchGUIDepth", "Similar to EditorFrame, but can be collapsed by clicking on the header", 212 | delegate 213 | { 214 | Rect rect = EditorGUILayout.GetControlRect(false, 50); 215 | 216 | GUI.RepeatButton(rect, "Button 2"); 217 | using (new SwitchGUIDepth(999)) 218 | { 219 | rect.xMin += 40; 220 | rect.yMin += 30; 221 | rect.xMax -= 40; 222 | rect.yMax += 30; 223 | GUI.RepeatButton(rect, "Button 1"); 224 | } 225 | GUILayout.Space(40); 226 | }, "KJSWG5BAOJSWG5BAHUQEKZDJORXXER2VJFGGC6LPOV2C4R3FORBW63TUOJXWYUTFMN2CQZTBNRZWKLBAGUYCSOYKBJDVKSJOKJSXAZLBORBHK5DUN5XCQ4TFMN2CYIBCIJ2XI5DPNYQDEIRJHMFHK43JNZTSAKDOMV3SAU3XNF2GG2CHKVEUIZLQORUCQOJZHEUSSCT3BIQCAIBAOJSWG5BOPBGWS3RAFM6SANBQHMFCAIBAEBZGKY3UFZ4U22LOEAVT2IBTGA5QUIBAEAQHEZLDOQXHQTLBPAQC2PJAGQYDWCRAEAQCA4TFMN2C46KNMF4CAKZ5EAZTAOYKEAQCAICHKVES4UTFOBSWC5CCOV2HI33OFBZGKY3UFQQCEQTVOR2G63RAGERCSOYKPUFEOVKJJRQXS33VOQXFG4DBMNSSQNBQFE5Q====".FromBase32()), 227 | } 228 | }; 229 | 230 | private static void DrawSampleControls() 231 | { 232 | EditorGUILayout.TextField("Text", ""); 233 | EditorGUILayout.ColorField("Color", Color.grey); 234 | } 235 | } 236 | 237 | public class DemoScenario 238 | { 239 | public string Name; 240 | 241 | public List Scenarios; 242 | 243 | public DemoScenario() 244 | { 245 | Scenarios = new List(); 246 | } 247 | } 248 | 249 | public class DemoScenarioContent 250 | { 251 | public string Caption; 252 | public string Description; 253 | public Action Sample; 254 | public string SampleCode; 255 | 256 | public bool ShowCode; 257 | public Vector3 ScrollPos; 258 | 259 | public DemoScenarioContent(string caption, string description, Action sample, string sampleCode) 260 | { 261 | Caption = caption; 262 | Description = description; 263 | Sample = sample; 264 | SampleCode = sampleCode; 265 | } 266 | } -------------------------------------------------------------------------------- /Example/Editor/DemoScenarios.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 845f236207bc0b5478d491c5a414ec99 3 | timeCreated: 1519378311 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Example/Editor/SampleEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEditor; 5 | using UnityEditorHelper; 6 | using UnityEngine; 7 | 8 | public class DemoScriptEditor : EditorWindow 9 | { 10 | private enum EditorPage { PropertyDrawer, Blocks, Utility, About } 11 | 12 | private EditorPage _currentPage; 13 | 14 | [MenuItem("Tools/UnityEditorHelper/Open Demo Window")] 15 | public static void OpenDemoEditor() 16 | { 17 | GetWindow(false, "Demo"); 18 | } 19 | 20 | private DemoObject _demoObject; 21 | private SerializedObject _serializedObject; 22 | 23 | private static List _blockDemoScenarios; 24 | 25 | private void OnEnable() 26 | { 27 | _blockDemoScenarios = new List 28 | { 29 | DemoScenarios.EditorBlockHorizontal, 30 | DemoScenarios.EditorBlockVertical, 31 | DemoScenarios.RoundedBox, 32 | DemoScenarios.EditorFrame, 33 | DemoScenarios.FoldableEditorFrame, 34 | DemoScenarios.SwitchGUIDepth, 35 | }; 36 | 37 | _demoObject = CreateInstance(); 38 | _serializedObject = new SerializedObject(_demoObject); 39 | } 40 | 41 | private void OnGUI() 42 | { 43 | using (new EditorBlock(EditorBlock.Orientation.Vertical)) 44 | { 45 | DrawToolbar(); 46 | 47 | switch (_currentPage) 48 | { 49 | case EditorPage.PropertyDrawer: 50 | DrawPropertyDrawer(); 51 | break; 52 | 53 | case EditorPage.Blocks: 54 | DrawBlockExamples(); 55 | break; 56 | 57 | case EditorPage.Utility: 58 | using (new PaddedBlock(8, 8, 16, 0)) 59 | DrawUtilityDemos(); 60 | break; 61 | 62 | case EditorPage.About: 63 | break; 64 | 65 | default: 66 | throw new ArgumentOutOfRangeException(); 67 | } 68 | } 69 | } 70 | 71 | public void DrawToolbar() 72 | { 73 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 74 | string[] enumNames = Enum.GetNames(typeof(EditorPage)); 75 | EditorPage[] enumValues = (EditorPage[])Enum.GetValues(typeof(EditorPage)); 76 | for (int i = 0; i < enumNames.Length; i++) 77 | { 78 | if (GUILayout.Toggle(_currentPage == enumValues[i], enumNames[i], EditorStyles.toolbarButton, GUILayout.Width(100))) 79 | { 80 | _currentPage = enumValues[i]; 81 | } 82 | } 83 | GUILayout.Toolbar(0, new[] { "" }, EditorStyles.toolbar, GUILayout.ExpandWidth(true)); 84 | EditorGUILayout.EndHorizontal(); 85 | EditorGUILayout.Space(); 86 | } 87 | 88 | private void DrawPropertyDrawer() 89 | { 90 | EditorGUILayout.PropertyField(_serializedObject.FindProperty("Layer")); 91 | EditorGUILayout.PropertyField(_serializedObject.FindProperty("Tag")); 92 | EditorGUILayout.PropertyField(_serializedObject.FindProperty("SortingLayer")); 93 | EditorGUILayout.PropertyField(_serializedObject.FindProperty("LimitLow")); 94 | EditorGUILayout.PropertyField(_serializedObject.FindProperty("LimitBoth")); 95 | 96 | _serializedObject.ApplyModifiedProperties(); 97 | } 98 | 99 | private void DrawUtilityDemos() 100 | { 101 | bool expanded = true; 102 | using (new FoldableBlock(ref expanded, "GetAssetPath")) 103 | { 104 | EditorGUILayout.LabelField("", "The Asset \"SampleEditor.cs\" is located at the following path:"); 105 | EditorGUILayout.TextField("", EditorHelper.GetAssetPath("SampleEditor.cs")); 106 | } 107 | 108 | using (new FoldableBlock(ref expanded, "DrawIconHeader")) 109 | { 110 | if (EditorHelper.DrawIconHeader("DemoKey", EditorGUIUtility.FindTexture("SceneAsset Icon"), "Caption (foldable)", Color.white)) 111 | { 112 | EditorGUILayout.LabelField("", "Some fancy content..."); 113 | } 114 | 115 | if (EditorHelper.DrawIconHeader("DemoKey", EditorGUIUtility.FindTexture("Shader Icon"), "Caption (non-foldable)", Color.white, false)) 116 | { 117 | EditorGUILayout.LabelField("", "Even more fancy content..."); 118 | } 119 | } 120 | 121 | using (new FoldableBlock(ref expanded, "GUIDrawRect")) 122 | { 123 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 124 | { 125 | for (int i = 0; i <= 10; i++) 126 | { 127 | Rect rect = EditorGUILayout.GetControlRect(false, 32); 128 | rect.width = 32; 129 | EditorHelper.GUIDrawRect(rect, new Color(0.1f * i, 0.7f, 0.8f)); 130 | } 131 | } 132 | } 133 | 134 | using (new FoldableBlock(ref expanded, "DrawTiledTexture")) 135 | { 136 | Rect rect = EditorGUILayout.GetControlRect(false, 64); 137 | EditorHelper.DrawTiledTexture(rect, EditorGUIUtility.FindTexture("_Help")); 138 | } 139 | 140 | using (new FoldableBlock(ref expanded, "CreateCheckerTex")) 141 | { 142 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 143 | { 144 | using (new EditorBlock(EditorBlock.Orientation.Vertical)) 145 | { 146 | Rect rect = EditorGUILayout.GetControlRect(false, 32); 147 | rect.width = 32; 148 | GUI.DrawTexture(rect, EditorHelper.CreateCheckerTex(Color.blue, Color.cyan, 32)); 149 | 150 | rect = EditorGUILayout.GetControlRect(false, 32); 151 | rect.width = 32; 152 | GUI.DrawTexture(rect, EditorHelper.CreateCheckerTex(Color.blue, Color.cyan, 32, 8)); 153 | } 154 | 155 | using (new EditorBlock(EditorBlock.Orientation.Vertical)) 156 | { 157 | Rect rect = EditorGUILayout.GetControlRect(false, 64); 158 | rect.width = 64; 159 | GUI.DrawTexture(rect, EditorHelper.CreateCheckerTex(new Color(0.1f, 0.1f, 0.1f, 0.5f), new Color(0.2f, 0.2f, 0.2f, 0.5f), 64)); 160 | 161 | rect = EditorGUILayout.GetControlRect(false, 64); 162 | rect.width = 64; 163 | GUI.DrawTexture(rect, EditorHelper.CreateCheckerTex(new Color(0.1f, 0.1f, 0.1f, 0.5f), new Color(0.2f, 0.2f, 0.2f, 0.5f), 64, 8)); 164 | } 165 | 166 | Rect largeRect = EditorGUILayout.GetControlRect(false, 128); 167 | largeRect.width = 128; 168 | GUI.DrawTexture(largeRect, EditorHelper.CreateCheckerTex(new Color(0.1f, 0.1f, 0.1f, 0.5f), new Color(0.2f, 0.2f, 0.2f, 0.5f), 128, 64)); 169 | } 170 | } 171 | 172 | using (new FoldableBlock(ref expanded, "DrawOutline")) 173 | { 174 | EditorGUILayout.Space(); 175 | using (new EditorBlock(EditorBlock.Orientation.Horizontal)) 176 | { 177 | EditorGUILayout.Space(); 178 | Rect rect = EditorGUILayout.GetControlRect(false, 64); 179 | rect.width = 128; 180 | EditorHelper.DrawOutline(rect, Color.blue, 4f); 181 | EditorHelper.GUIDrawRect(rect, new Color(1f, 1f, 1f, 0.5f)); 182 | EditorGUILayout.Space(); 183 | 184 | rect = EditorGUILayout.GetControlRect(false, 64); 185 | rect.width = 128; 186 | EditorHelper.DrawOutline(rect, Color.blue, 4f, EditorHelper.OutlineMode.Centered); 187 | EditorHelper.GUIDrawRect(rect, new Color(1f, 1f, 1f, 0.5f)); 188 | EditorGUILayout.Space(); 189 | 190 | rect = EditorGUILayout.GetControlRect(false, 64); 191 | rect.width = 128; 192 | EditorHelper.DrawOutline(rect, Color.blue, 4f, EditorHelper.OutlineMode.Inside); 193 | EditorHelper.GUIDrawRect(rect, new Color(1f, 1f, 1f, 0.5f)); 194 | EditorGUILayout.Space(); 195 | } 196 | EditorGUILayout.Space(); 197 | } 198 | } 199 | 200 | private int _selectedBlockIndex = -1; 201 | private Vector2 _scrollPos; 202 | 203 | private void DrawBlockExamples() 204 | { 205 | GUILayout.Space(-10); 206 | using (new EditorBlock(EditorBlock.Orientation.Horizontal, GUILayout.ExpandWidth(true))) 207 | { 208 | using (new EditorBlock(EditorBlock.Orientation.Vertical, "Box", GUILayout.Width(200), GUILayout.ExpandHeight(true))) 209 | { 210 | int oldSelectedBlockIndex = _selectedBlockIndex; 211 | _selectedBlockIndex = GUILayout.SelectionGrid(_selectedBlockIndex, _blockDemoScenarios.Select(s => s.Name).ToArray(), 1); 212 | if (oldSelectedBlockIndex != _selectedBlockIndex) 213 | { 214 | GUI.SetNextControlName("Some stuff to loose focus"); 215 | GUI.Label(new Rect(-float.MaxValue, -float.MaxValue, 1, 1), ""); 216 | GUI.FocusControl("Some stuff to loose focus"); 217 | } 218 | } 219 | using (new EditorBlock(EditorBlock.Orientation.Vertical, GUILayout.ExpandHeight(true))) 220 | { 221 | if (_selectedBlockIndex >= 0) 222 | { 223 | using (new ScrollViewBlock(ref _scrollPos, GUILayout.ExpandWidth(true))) 224 | { 225 | foreach (DemoScenarioContent scenario in _blockDemoScenarios[_selectedBlockIndex].Scenarios) 226 | { 227 | using (FoldableEditorFrame block = new FoldableEditorFrame("[SampleEditorScenario]." + scenario.Caption, scenario.Caption, Color.white, Color.white)) 228 | { 229 | if (block.Expanded) 230 | { 231 | EditorGUILayout.LabelField(scenario.Description, EditorStyles.miniLabel ); 232 | 233 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 234 | string[] tabs = { "Sample", "Code" }; 235 | for (int i = 0; i < tabs.Length; i++) 236 | { 237 | if (GUILayout.Toggle(i == (scenario.ShowCode ? 1 : 0), tabs[i], EditorStyles.toolbarButton, GUILayout.Width(100))) 238 | { 239 | scenario.ShowCode = i == 1; 240 | } 241 | } 242 | GUILayout.Toolbar(0, new[] { "" }, EditorStyles.toolbar, GUILayout.ExpandWidth(true)); 243 | EditorGUILayout.EndHorizontal(); 244 | EditorGUILayout.Space(); 245 | 246 | GUILayout.Space(-10); 247 | using (new EditorBlock(EditorBlock.Orientation.Vertical, "Box")) 248 | { 249 | using (new PaddedBlock(10)) 250 | { 251 | if (scenario.ShowCode) 252 | { 253 | EditorHelper.ScrollableSelectableLabel(scenario.ScrollPos, scenario.SampleCode, EditorStyles.label); 254 | } 255 | else 256 | { 257 | scenario.Sample.Invoke(); 258 | } 259 | } 260 | } 261 | } 262 | else 263 | EditorGUILayout.Space(); 264 | } 265 | } 266 | } 267 | } 268 | } 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /Example/Editor/SampleEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2144e9a906a2afe4f95e6874ac925ed1 3 | timeCreated: 1519321546 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Thomas Hummes 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityEditorHelper 2 | An organized bunch of scripts to make editor scripting in Unity easier - gathered from some of my projects and other free sources 3 | 4 | More information can be found in the wiki including examples and screenshots: https://github.com/JefferiesTube/UnityEditorHelper/wiki 5 | --------------------------------------------------------------------------------