// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using System.Linq; using System.Reflection; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.GUI; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { /// /// Dedicated custom editor for enum. /// /// [CustomEditor(typeof(AnchorPresets)), DefaultEditor] public sealed class AnchorPresetsEditor : CustomEditor { class AnchorButton : Button { private AnchorPresets _presets = (AnchorPresets)((int)AnchorPresets.StretchAll + 1); public AnchorPresets Presets { get => _presets; set { if (_presets != value) { _presets = value; TooltipText = Utilities.Utils.GetPropertyNameUI(_presets.ToString()); } } } public bool IsSelected; public bool SupportsShiftModulation; /// public override void Draw() { // Cache data var rect = new Rectangle(Float2.Zero, Size); if (rect.Width >= rect.Height) { rect.X = (rect.Width - rect.Height) * 0.5f; rect.Width = rect.Height; } else { rect.Y = (rect.Height - rect.Width) * 0.5f; rect.Height = rect.Width; } var enabled = EnabledInHierarchy; var style = FlaxEngine.GUI.Style.Current; var backgroundColor = BackgroundColor; var borderColor = BorderColor; if (!enabled) { backgroundColor *= 0.7f; borderColor *= 0.7f; } else if (_isPressed) { backgroundColor = BackgroundColorSelected; borderColor = BorderColorSelected; } else if (IsMouseOver) { backgroundColor = BackgroundColorHighlighted; borderColor = BorderColorHighlighted; } if (SupportsShiftModulation && Input.GetKey(KeyboardKeys.Shift)) { backgroundColor = BackgroundColorSelected; } // Calculate fill area float fillSize = rect.Width / 3; Rectangle fillArea; switch (_presets) { case AnchorPresets.Custom: fillArea = Rectangle.Empty; break; case AnchorPresets.TopLeft: fillArea = new Rectangle(0, 0, fillSize, fillSize); break; case AnchorPresets.TopCenter: fillArea = new Rectangle(fillSize, 0, fillSize, fillSize); break; case AnchorPresets.TopRight: fillArea = new Rectangle(fillSize * 2, 0, fillSize, fillSize); break; case AnchorPresets.MiddleLeft: fillArea = new Rectangle(0, fillSize, fillSize, fillSize); break; case AnchorPresets.MiddleCenter: fillArea = new Rectangle(fillSize, fillSize, fillSize, fillSize); break; case AnchorPresets.MiddleRight: fillArea = new Rectangle(fillSize * 2, fillSize, fillSize, fillSize); break; case AnchorPresets.BottomLeft: fillArea = new Rectangle(0, fillSize * 2, fillSize, fillSize); break; case AnchorPresets.BottomCenter: fillArea = new Rectangle(fillSize, fillSize * 2, fillSize, fillSize); break; case AnchorPresets.BottomRight: fillArea = new Rectangle(fillSize * 2, fillSize * 2, fillSize, fillSize); break; case AnchorPresets.VerticalStretchLeft: fillArea = new Rectangle(0, 0, fillSize, fillSize * 3); break; case AnchorPresets.VerticalStretchRight: fillArea = new Rectangle(fillSize * 2, 0, fillSize, fillSize * 3); break; case AnchorPresets.VerticalStretchCenter: fillArea = new Rectangle(fillSize, 0, fillSize, fillSize * 3); break; case AnchorPresets.HorizontalStretchTop: fillArea = new Rectangle(0, 0, fillSize * 3, fillSize); break; case AnchorPresets.HorizontalStretchMiddle: fillArea = new Rectangle(0, fillSize, fillSize * 3, fillSize); break; case AnchorPresets.HorizontalStretchBottom: fillArea = new Rectangle(0, fillSize * 2, fillSize * 3, fillSize); break; case AnchorPresets.StretchAll: fillArea = new Rectangle(0, 0, fillSize * 3, fillSize * 3); break; default: throw new ArgumentOutOfRangeException(); } // Draw background //Render2D.FillRectangle(rect, backgroundColor); Render2D.DrawRectangle(rect, borderColor, 1.1f); // Draw fill Render2D.FillRectangle(fillArea.MakeOffsetted(rect.Location), backgroundColor); // Draw frame if (IsMouseOver) { Render2D.DrawRectangle(rect, style.ProgressNormal.AlphaMultiplied(0.8f), 1.1f); } if (IsSelected) { Render2D.DrawRectangle(rect, style.BackgroundSelected.AlphaMultiplied(0.8f), 1.1f); } // Draw pivot point if (SupportsShiftModulation && Input.GetKey(KeyboardKeys.Control)) { Float2 pivotPoint; switch (_presets) { case AnchorPresets.Custom: pivotPoint = Float2.Minimum; break; case AnchorPresets.TopLeft: pivotPoint = new Float2(0, 0); break; case AnchorPresets.TopCenter: case AnchorPresets.HorizontalStretchTop: pivotPoint = new Float2(rect.Width / 2, 0); break; case AnchorPresets.TopRight: pivotPoint = new Float2(rect.Width, 0); break; case AnchorPresets.MiddleLeft: case AnchorPresets.VerticalStretchLeft: pivotPoint = new Float2(0, rect.Height / 2); break; case AnchorPresets.MiddleCenter: case AnchorPresets.VerticalStretchCenter: case AnchorPresets.HorizontalStretchMiddle: case AnchorPresets.StretchAll: pivotPoint = new Float2(rect.Width / 2, rect.Height / 2); break; case AnchorPresets.MiddleRight: case AnchorPresets.VerticalStretchRight: pivotPoint = new Float2(rect.Width, rect.Height / 2); break; case AnchorPresets.BottomLeft: pivotPoint = new Float2(0, rect.Height); break; case AnchorPresets.BottomCenter: case AnchorPresets.HorizontalStretchBottom: pivotPoint = new Float2(rect.Width / 2, rect.Height); break; case AnchorPresets.BottomRight: pivotPoint = new Float2(rect.Width, rect.Height); break; default: throw new ArgumentOutOfRangeException(); } var pivotPointSize = new Float2(3.0f); Render2D.DrawRectangle(new Rectangle(pivotPoint - pivotPointSize * 0.5f, pivotPointSize), style.ProgressNormal, 1.1f); } } } /// /// Context menu for anchors presets editing. /// /// public sealed class AnchorPresetsEditorPopup : ContextMenuBase { const float ButtonsMargin = 10.0f; const float ButtonsMarginStretch = 8.0f; const float ButtonsSize = 32.0f; const float TitleHeight = 23.0f; const float InfoHeight = 23.0f; const float DialogWidth = ButtonsSize * 4 + ButtonsMargin * 5 + ButtonsMarginStretch; const float DialogHeight = TitleHeight + InfoHeight + ButtonsSize * 4 + ButtonsMargin * 5 + ButtonsMarginStretch; private readonly bool _supportsShiftModulation; /// /// Initializes a new instance of the class. /// /// The initial value. /// If the popup should react to shift public AnchorPresetsEditorPopup(AnchorPresets presets, bool supportsShiftModulation = true) { _supportsShiftModulation = supportsShiftModulation; var style = FlaxEngine.GUI.Style.Current; Tag = presets; Size = new Float2(DialogWidth, DialogHeight); // Title var title = new Label(2, 2, DialogWidth - 4, TitleHeight) { Font = new FontReference(style.FontLarge), Text = "Anchor Presets", Parent = this }; // Info var info = new Label(0, title.Bottom, DialogWidth, InfoHeight) { Font = new FontReference(style.FontSmall), Text = "Shift: also set bounds\nControl: also set pivot", Parent = this }; // Buttons var buttonsX = ButtonsMargin; var buttonsY = info.Bottom + ButtonsMargin; var buttonsSpacingX = ButtonsSize + ButtonsMargin; var buttonsSpacingY = ButtonsSize + ButtonsMargin; // AddButton(buttonsX + buttonsSpacingX * 0, buttonsY + buttonsSpacingY * 0, AnchorPresets.TopLeft); AddButton(buttonsX + buttonsSpacingX * 1, buttonsY + buttonsSpacingY * 0, AnchorPresets.TopCenter); AddButton(buttonsX + buttonsSpacingX * 2, buttonsY + buttonsSpacingY * 0, AnchorPresets.TopRight); AddButton(buttonsX + buttonsSpacingX * 3 + ButtonsMarginStretch, buttonsY + buttonsSpacingY * 0, AnchorPresets.HorizontalStretchTop); // AddButton(buttonsX + buttonsSpacingX * 0, buttonsY + buttonsSpacingY * 1, AnchorPresets.MiddleLeft); AddButton(buttonsX + buttonsSpacingX * 1, buttonsY + buttonsSpacingY * 1, AnchorPresets.MiddleCenter); AddButton(buttonsX + buttonsSpacingX * 2, buttonsY + buttonsSpacingY * 1, AnchorPresets.MiddleRight); AddButton(buttonsX + buttonsSpacingX * 3 + ButtonsMarginStretch, buttonsY + buttonsSpacingY * 1, AnchorPresets.HorizontalStretchMiddle); // AddButton(buttonsX + buttonsSpacingX * 0, buttonsY + buttonsSpacingY * 2, AnchorPresets.BottomLeft); AddButton(buttonsX + buttonsSpacingX * 1, buttonsY + buttonsSpacingY * 2, AnchorPresets.BottomCenter); AddButton(buttonsX + buttonsSpacingX * 2, buttonsY + buttonsSpacingY * 2, AnchorPresets.BottomRight); AddButton(buttonsX + buttonsSpacingX * 3 + ButtonsMarginStretch, buttonsY + buttonsSpacingY * 2, AnchorPresets.HorizontalStretchBottom); // AddButton(buttonsX + buttonsSpacingX * 0, buttonsY + buttonsSpacingY * 3 + ButtonsMarginStretch, AnchorPresets.VerticalStretchLeft); AddButton(buttonsX + buttonsSpacingX * 1, buttonsY + buttonsSpacingY * 3 + ButtonsMarginStretch, AnchorPresets.VerticalStretchCenter); AddButton(buttonsX + buttonsSpacingX * 2, buttonsY + buttonsSpacingY * 3 + ButtonsMarginStretch, AnchorPresets.VerticalStretchRight); AddButton(buttonsX + buttonsSpacingX * 3 + ButtonsMarginStretch, buttonsY + buttonsSpacingY * 3 + ButtonsMarginStretch, AnchorPresets.StretchAll); } private void AddButton(float x, float y, AnchorPresets presets) { var button = new AnchorButton { Bounds = new Rectangle(x, y, ButtonsSize, ButtonsSize), Parent = this, Presets = presets, IsSelected = presets == (AnchorPresets)Tag, SupportsShiftModulation = _supportsShiftModulation, Tag = presets, }; button.ButtonClicked += OnButtonClicked; } private void OnButtonClicked(Button button) { Tag = button.Tag; Hide(); } /// protected override void OnShow() { Focus(); base.OnShow(); } /// public override void Hide() { if (!Visible) return; Focus(null); base.Hide(); } /// public override bool OnKeyDown(KeyboardKeys key) { if (key == KeyboardKeys.Escape) { Hide(); return true; } return base.OnKeyDown(key); } } private AnchorButton _button; /// public override DisplayStyle Style => DisplayStyle.Inline; /// public override void Initialize(LayoutElementsContainer layout) { _button = layout.Custom().CustomControl; _button.Presets = (AnchorPresets)Values[0]; _button.Clicked += OnButtonClicked; } private void OnButtonClicked() { var location = _button.Center + new Float2(3.0f); var editor = new AnchorPresetsEditorPopup(_button.Presets, true); editor.VisibleChanged += OnEditorVisibleChanged; editor.Show(_button.Parent, location); } private void OnEditorVisibleChanged(Control control) { if (control.Visible) return; SetValue(control.Tag); } /// protected override void SynchronizeValue(object value) { // Custom anchors editing for Control to handle bounds preservation via key modifiers if (ParentEditor != null) { var centerToPosition = Input.GetKey(KeyboardKeys.Shift); var setPivot = Input.GetKey(KeyboardKeys.Control); var editedAny = false; foreach (var parentValue in ParentEditor.Values) { if (parentValue is Control parentControl) { parentControl.SetAnchorPreset((AnchorPresets)value, !centerToPosition, setPivot); editedAny = true; } } if (editedAny) return; } base.SynchronizeValue(value); } /// public override void Refresh() { base.Refresh(); _button.Presets = (AnchorPresets)Values[0]; } /// protected override void Deinitialize() { _button = null; base.Deinitialize(); } } /// /// Dedicated custom editor for object. /// /// public class UIControlControlEditor : GenericEditor { private Type _cachedType; private bool _anchorDropDownClosed = true; private Button _pivotRelativeButton; /// public override void Initialize(LayoutElementsContainer layout) { _cachedType = null; if (HasDifferentTypes) { // TODO: support stable editing multiple different control types (via generic way or for transform-only) return; } // Set control type button var space = layout.Space(20); float setTypeButtonWidth = 60.0f; var setTypeButton = new Button { TooltipText = "Sets the control to the given type", AnchorPreset = AnchorPresets.MiddleCenter, Text = "Set Type", Parent = space.Spacer, Bounds = new Rectangle((space.Spacer.Width - setTypeButtonWidth) / 2, 1, setTypeButtonWidth, 18), }; setTypeButton.ButtonClicked += OnSetTypeButtonClicked; // Don't show editor if any control is invalid if (Values.HasNull) { var label = layout.Label("Select control type to create", TextAlignment.Center); label.Label.Enabled = false; return; } // Add control type helper label { var type = Values[0].GetType(); _cachedType = type; var label = layout.AddPropertyItem("Type", "The type of the created control."); label.Label(type.FullName); } // Show control properties base.Initialize(layout); for (int i = 0; i < layout.Children.Count; i++) { if (layout.Children[i] is GroupElement group && group.Panel.HeaderText == "Transform") { VerticalPanelElement mainHor = VerticalPanelWithoutMargin(group); CreateTransformElements(mainHor, ValuesTypes); group.ContainerControl.ChangeChildIndex(mainHor.Control, 0); break; } } } private void CreateTransformElements(LayoutElementsContainer main, ScriptType[] valueTypes) { main.Space(10); HorizontalPanelElement sidePanel = main.HorizontalPanel(); sidePanel.Panel.ClipChildren = false; ScriptMemberInfo anchorInfo = valueTypes[0].GetProperty("AnchorPreset"); ItemInfo anchorItem = new ItemInfo(anchorInfo); sidePanel.Object(anchorItem.GetValues(Values)); VerticalPanelElement group = VerticalPanelWithoutMargin(sidePanel); group.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop; group.Panel.Offsets = new Margin(100, 10, 0, 0); var horUp = UniformGridTwoByOne(group); horUp.CustomControl.Height = TextBoxBase.DefaultHeight; var horDown = UniformGridTwoByOne(group); horDown.CustomControl.Height = TextBoxBase.DefaultHeight; GetAnchorEquality(out _cachedXEq, out _cachedYEq, valueTypes); BuildLocationSizeOffsets(horUp, horDown, _cachedXEq, _cachedYEq, valueTypes); BuildExtraButtons(group); main.Space(10); BuildAnchorsDropper(main, valueTypes); } private void BuildExtraButtons(VerticalPanelElement group) { (Values[0] as Control).PivotRelative = Editor.Instance.Windows.PropertiesWin.PivotRelativeSize; var current = Editor.Instance.Windows.PropertiesWin.PivotRelativeSize; var panel = group.CustomContainer(); panel.CustomControl.Height = TextBoxBase.DefaultHeight; panel.CustomControl.ClipChildren = false; panel.CustomControl.Parent = group.ContainerControl; _pivotRelativeButton = new Button() { Parent = panel.ContainerControl, Width = 18, Height = 18, BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Scale32), AnchorPreset = AnchorPresets.TopRight, X = 77, }; SetStyle(current); _pivotRelativeButton.Clicked += PivotRelativeClicked; } private void PivotRelativeClicked() { var current = (Values[0] as Control).PivotRelative; (Values[0] as Control).PivotRelative = !current; Editor.Instance.Windows.PropertiesWin.PivotRelativeSize = !current; SetStyle((Values[0] as Control).PivotRelative); } private void SetStyle(bool current) { var style = FlaxEngine.GUI.Style.Current; var backgroundColor = current ? style.Foreground : style.ForegroundDisabled; _pivotRelativeButton.SetColors(backgroundColor); } private void BuildAnchorsDropper(LayoutElementsContainer main, ScriptType[] valueTypes) { ScriptMemberInfo minInfo = valueTypes[0].GetProperty("AnchorMin"); ScriptMemberInfo maxInfo = valueTypes[0].GetProperty("AnchorMax"); ItemInfo minItem = new ItemInfo(minInfo); ItemInfo maxItem = new ItemInfo(maxInfo); GroupElement ng = main.Group("Anchors", true); ng.Panel.IsClosed = _anchorDropDownClosed; ng.Panel.IsClosedChanged += panel => _anchorDropDownClosed = panel.IsClosed; ng.Property("Min", minItem.GetValues(Values)); ng.Property("Max", maxItem.GetValues(Values)); } private void GetAnchorEquality(out bool xEq, out bool yEq, ScriptType[] valueTypes) { ScriptMemberInfo minInfo = valueTypes[0].GetProperty("AnchorMin"); ScriptMemberInfo maxInfo = valueTypes[0].GetProperty("AnchorMax"); ItemInfo minItem = new ItemInfo(minInfo); ItemInfo maxItem = new ItemInfo(maxInfo); ValueContainer minVal = minItem.GetValues(Values); ValueContainer maxVal = maxItem.GetValues(Values); ItemInfo xItem = new ItemInfo(minInfo.ValueType.GetField("X")); ItemInfo yItem = new ItemInfo(minInfo.ValueType.GetField("Y")); xEq = xItem.GetValues(minVal).ToList().Any(xItem.GetValues(maxVal).ToList().Contains); yEq = yItem.GetValues(minVal).ToList().Any(yItem.GetValues(maxVal).ToList().Contains); } private void BuildLocationSizeOffsets(LayoutElementsContainer horUp, LayoutElementsContainer horDown, bool xEq, bool yEq, ScriptType[] valueTypes) { ScriptMemberInfo xInfo = valueTypes[0].GetProperty("LocalX"); ItemInfo xItem = new ItemInfo(xInfo); ScriptMemberInfo yInfo = valueTypes[0].GetProperty("LocalY"); ItemInfo yItem = new ItemInfo(yInfo); ScriptMemberInfo widthInfo = valueTypes[0].GetProperty("Width"); ItemInfo widthItem = new ItemInfo(widthInfo); ScriptMemberInfo heightInfo = valueTypes[0].GetProperty("Height"); ItemInfo heightItem = new ItemInfo(heightInfo); ScriptMemberInfo leftInfo = valueTypes[0].GetProperty("Proxy_Offset_Left", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ItemInfo leftItem = new ItemInfo(leftInfo); ScriptMemberInfo rightInfo = valueTypes[0].GetProperty("Proxy_Offset_Right", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ItemInfo rightItem = new ItemInfo(rightInfo); ScriptMemberInfo topInfo = valueTypes[0].GetProperty("Proxy_Offset_Top", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ItemInfo topItem = new ItemInfo(topInfo); ScriptMemberInfo bottomInfo = valueTypes[0].GetProperty("Proxy_Offset_Bottom", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); ItemInfo bottomItem = new ItemInfo(bottomInfo); LayoutElementsContainer xEl; LayoutElementsContainer yEl; LayoutElementsContainer hEl; LayoutElementsContainer vEl; if (xEq) { xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values)); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values)); } else { xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values)); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values)); } if (yEq) { yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values)); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values)); } else { yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values)); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values)); } xEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y); xEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y); vEl.Control.AnchorMin = new Float2(0, xEl.Control.AnchorMin.Y); vEl.Control.AnchorMax = new Float2(0.5f, xEl.Control.AnchorMax.Y); yEl.Control.AnchorMin = new Float2(0.5f, xEl.Control.AnchorMin.Y); yEl.Control.AnchorMax = new Float2(1, xEl.Control.AnchorMax.Y); hEl.Control.AnchorMin = new Float2(0.5f, xEl.Control.AnchorMin.Y); hEl.Control.AnchorMax = new Float2(1, xEl.Control.AnchorMax.Y); } private VerticalPanelElement VerticalPanelWithoutMargin(LayoutElementsContainer cont) { var horUp = cont.VerticalPanel(); horUp.Panel.Margin = Margin.Zero; return horUp; } private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont) { var horUp = cont.CustomContainer(); horUp.CustomControl.SlotsHorizontally = 2; horUp.CustomControl.SlotsVertically = 1; horUp.CustomControl.SlotPadding = Margin.Zero; horUp.CustomControl.ClipChildren = false; return horUp; } private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values) { CustomElementsContainer hor = UniformGridTwoByOne(el); hor.CustomControl.SlotPadding = new Margin(5, 5, 0, 0); LabelElement lab = hor.Label(text); hor.Object(values); return hor; } private bool _cachedXEq; private bool _cachedYEq; /// public override void Refresh() { if (_cachedType != null) { // Automatic layout rebuild if control type gets changed var type = Values.HasNull ? null : Values[0].GetType(); if (type != _cachedType) { RebuildLayout(); return; } // Refresh anchors GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); if (xEq != _cachedXEq || yEq != _cachedYEq) { RebuildLayout(); } } base.Refresh(); } private void OnSetTypeButtonClicked(Button button) { var controlTypes = Editor.Instance.CodeEditing.Controls.Get(); if (controlTypes.Count == 0) return; // Show context menu with list of controls to add var cm = new ItemsListContextMenu(180); for (int i = 0; i < controlTypes.Count; i++) { cm.AddItem(new TypeSearchPopup.TypeItemView(controlTypes[i])); } cm.ItemClicked += controlType => SetType((ScriptType)controlType.Tag); cm.SortItems(); cm.Show(button.Parent, button.BottomLeft - new Float2((cm.Width - button.Width) / 2, 0)); } private void SetType(ref ScriptType controlType, UIControl uiControl) { string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); uiControl.Control = (Control)controlType.CreateInstance(); if (uiControl.Name.StartsWith(previousName)) { string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); if (uiControl.Parent != null) newName = StringUtils.IncrementNameNumber(newName, x => uiControl.Parent.GetChild(x) == null); uiControl.Name = newName; } } private void SetType(ScriptType controlType) { var uiControls = ParentEditor.Values; if (Presenter.Undo?.Enabled ?? false) { using (new UndoMultiBlock(Presenter.Undo, uiControls, "Set Control Type")) { for (int i = 0; i < uiControls.Count; i++) SetType(ref controlType, (UIControl)uiControls[i]); } } else { for (int i = 0; i < uiControls.Count; i++) SetType(ref controlType, (UIControl)uiControls[i]); } ParentEditor.RebuildLayout(); } } }