// Copyright (c) 2012-2024 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); var buttonText = "Set Type"; var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(buttonText); float setTypeButtonWidth = (textSize.X < 60.0f) ? 60.0f : textSize.X + 4; var setTypeButton = new Button { TooltipText = "Sets the control to the given type", AnchorPreset = AnchorPresets.MiddleCenter, Text = buttonText, 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.Equals("Transform", StringComparison.Ordinal)) { layout.Children.Remove(group); layout.ContainerControl.Children.Remove(group.Panel); break; } } // Setup transform if (Presenter is LayoutElementsContainer l) { for (int i = 0; i < l.Children.Count; i++) { if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("Transform", StringComparison.Ordinal) && i + 1 <= l.Children.Count) { l.Children.Remove(g); l.ContainerControl.Children.Remove(g.Panel); break; } } var transformGroup = l.Group("Transform"); VerticalPanelElement mainHor = VerticalPanelWithoutMargin(transformGroup); CreateTransformElements(mainHor, ValuesTypes); ScriptMemberInfo scaleInfo = ValuesTypes[0].GetProperty("Scale"); ItemInfo scaleItem = new ItemInfo(scaleInfo); transformGroup.Property("Scale", scaleItem.GetValues(Values)); ScriptMemberInfo pivotInfo = ValuesTypes[0].GetProperty("Pivot"); ItemInfo pivotItem = new ItemInfo(pivotInfo); transformGroup.Property("Pivot", pivotItem.GetValues(Values)); ScriptMemberInfo shearInfo = ValuesTypes[0].GetProperty("Shear"); ItemInfo shearItem = new ItemInfo(shearInfo); transformGroup.Property("Shear", shearItem.GetValues(Values)); ScriptMemberInfo rotationInfo = ValuesTypes[0].GetProperty("Rotation"); ItemInfo rotationItem = new ItemInfo(rotationInfo); transformGroup.Property("Rotation", rotationItem.GetValues(Values)); // Get position of general tab for (int i = 0; i < l.Children.Count; i++) { if (l.Children[i] is GroupElement g && g.Panel.HeaderText.Equals("General", StringComparison.Ordinal) && i + 1 <= l.Children.Count) { Presenter.ContainerControl.ChangeChildIndex(transformGroup.Control, i + 1); 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) { var control = (Control)Values[0]; var pivotRelative = Editor.Instance.Windows.PropertiesWin.UIPivotRelative; control.PivotRelative = pivotRelative; var panel = group.CustomContainer(); panel.CustomControl.Height = TextBoxBase.DefaultHeight; panel.CustomControl.ClipChildren = false; panel.CustomControl.Parent = group.ContainerControl; _pivotRelativeButton = new Button { TooltipText = "Toggles UI control resizing based on where the pivot is rather than just the top-left.", Size = new Float2(18), Parent = panel.ContainerControl, BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Scale32), AnchorPreset = AnchorPresets.TopRight, X = 77, }; SetStyle(pivotRelative); _pivotRelativeButton.Clicked += PivotRelativeClicked; } private void PivotRelativeClicked() { var control = (Control)Values[0]; var pivotRelative = control.PivotRelative; control.PivotRelative = !pivotRelative; Editor.Instance.Windows.PropertiesWin.UIPivotRelative = !pivotRelative; SetStyle(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; Color axisColorX = ActorTransformEditor.AxisColorX; Color axisColorY = ActorTransformEditor.AxisColorY; if (xEq) { xEl = UniformPanelCapsuleForObjectWithText(horUp, "X: ", xItem.GetValues(Values), axisColorX); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Width: ", widthItem.GetValues(Values), axisColorX); } else { xEl = UniformPanelCapsuleForObjectWithText(horUp, "Left: ", leftItem.GetValues(Values), axisColorX); vEl = UniformPanelCapsuleForObjectWithText(horDown, "Right: ", rightItem.GetValues(Values), axisColorX); } if (yEq) { yEl = UniformPanelCapsuleForObjectWithText(horUp, "Y: ", yItem.GetValues(Values), axisColorY); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Height: ", heightItem.GetValues(Values), axisColorY); } else { yEl = UniformPanelCapsuleForObjectWithText(horUp, "Top: ", topItem.GetValues(Values), axisColorY); hEl = UniformPanelCapsuleForObjectWithText(horDown, "Bottom: ", bottomItem.GetValues(Values), axisColorY); } 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 panel = cont.VerticalPanel(); panel.Panel.Margin = Margin.Zero; return panel; } private CustomElementsContainer UniformGridTwoByOne(LayoutElementsContainer cont) { var grid = cont.CustomContainer(); grid.CustomControl.SlotsHorizontally = 2; grid.CustomControl.SlotsVertically = 1; grid.CustomControl.SlotPadding = Margin.Zero; grid.CustomControl.ClipChildren = false; return grid; } private CustomElementsContainer UniformPanelCapsuleForObjectWithText(LayoutElementsContainer el, string text, ValueContainer values, Color borderColor) { var grid = UniformGridTwoByOne(el); grid.CustomControl.SlotPadding = new Margin(5, 5, 1, 1); var label = grid.Label(text, TextAlignment.Far); var editor = grid.Object(values); if (editor is FloatEditor floatEditor && floatEditor.Element is FloatValueElement floatEditorElement) { var back = FlaxEngine.GUI.Style.Current.TextBoxBackground; floatEditorElement.ValueBox.BorderColor = Color.Lerp(borderColor, back, ActorTransformEditor.AxisGreyOutFactor); floatEditorElement.ValueBox.BorderSelectedColor = borderColor; } return grid; } 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); var oldControlType = (Control)uiControl.Control; var newControlType = (Control)controlType.CreateInstance(); // copy old control data to new control if (oldControlType != null) { newControlType.Visible = oldControlType.Visible; newControlType.Enabled = oldControlType.Enabled; newControlType.AutoFocus = oldControlType.AutoFocus; newControlType.AnchorMin = oldControlType.AnchorMin; newControlType.AnchorMax = oldControlType.AnchorMax; newControlType.Offsets = oldControlType.Offsets; newControlType.LocalLocation = oldControlType.LocalLocation; newControlType.Scale = oldControlType.Scale; newControlType.Bounds = oldControlType.Bounds; newControlType.Width = oldControlType.Width; newControlType.Height = oldControlType.Height; newControlType.Center = oldControlType.Center; newControlType.PivotRelative = oldControlType.PivotRelative; newControlType.Pivot = oldControlType.Pivot; newControlType.Shear = oldControlType.Shear; newControlType.Rotation = oldControlType.Rotation; } if (oldControlType is ContainerControl oldContainer && newControlType is ContainerControl newContainer) { newContainer.CullChildren = oldContainer.CullChildren; newContainer.ClipChildren = oldContainer.ClipChildren; } uiControl.Control = newControlType; if (uiControl.Name.StartsWith(previousName)) { string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); if (uiControl.Parent != null) newName = Utilities.Utils.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(); } } }