From 53d4ea51af4ae620df77879b681969184fea49a5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 27 Jun 2025 21:18:53 -0500 Subject: [PATCH 1/4] Add prefab UI viewport scaling. --- Source/Editor/Gizmo/UIEditorGizmo.cs | 10 + Source/Editor/Modules/UIModule.cs | 100 ++++++ .../Editor/Viewport/PrefabWindowViewport.cs | 311 +++++++++++++++++- Source/Editor/Windows/Assets/PrefabWindow.cs | 10 + Source/Editor/Windows/GameWindow.cs | 186 ++++------- 5 files changed, 501 insertions(+), 116 deletions(-) diff --git a/Source/Editor/Gizmo/UIEditorGizmo.cs b/Source/Editor/Gizmo/UIEditorGizmo.cs index bdc1c56ca..765893bed 100644 --- a/Source/Editor/Gizmo/UIEditorGizmo.cs +++ b/Source/Editor/Gizmo/UIEditorGizmo.cs @@ -155,6 +155,16 @@ namespace FlaxEditor private List _widgets; private Widget _activeWidget; + /// + /// Sets the view size. + /// + /// The new size. + public void SetViewSize(Float2 size) + { + _view.Size = size; + _view.PerformLayout(); + } + /// /// True if enable displaying UI editing background and grid elements. /// diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 19a0c1142..e62dd6b6e 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -37,6 +37,53 @@ namespace FlaxEditor.Modules private bool _progressFailed; ContextMenuSingleSelectGroup _numberOfClientsGroup = new ContextMenuSingleSelectGroup(); + + /// + /// Defines a viewport scaling option. + /// + public class ViewportScaleOption + { + /// + /// Defines the viewport scale type. + /// + public enum ViewportScaleType + { + /// + /// Resolution. + /// + Resolution = 0, + + /// + /// Aspect Ratio. + /// + Aspect = 1, + } + + /// + /// The name. + /// + public string Label; + + /// + /// The Type of scaling to do. + /// + public ViewportScaleType ScaleType; + + /// + /// The width and height to scale by. + /// + public Int2 Size; + } + + /// + /// The default viewport scaling options. + /// + public List DefaultViewportScaleOptions = new List(); + + /// + /// The user defined viewport scaling options. + /// + public List CustomViewportScaleOptions = new List(); private ContextMenuButton _menuFileSaveScenes; private ContextMenuButton _menuFileReloadScenes; @@ -371,6 +418,8 @@ namespace FlaxEditor.Modules // Update window background mainWindow.BackgroundColor = Style.Current.Background; + + InitViewportScaleOptions(); InitSharedMenus(); InitMainMenu(mainWindow); @@ -392,6 +441,57 @@ namespace FlaxEditor.Modules } } + private void InitViewportScaleOptions() + { + if (DefaultViewportScaleOptions.Count == 0) + { + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "Free Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(1, 1), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "16:9 Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(16, 9), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "16:10 Aspect", + ScaleType = ViewportScaleOption.ViewportScaleType.Aspect, + Size = new Int2(16, 10), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "1920x1080 Resolution (Full HD)", + ScaleType = ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(1920, 1080), + }); + DefaultViewportScaleOptions.Add(new ViewportScaleOption + { + Label = "2560x1440 Resolution (2K)", + ScaleType = ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(2560, 1440), + }); + } + + if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data)) + { + CustomViewportScaleOptions = JsonSerializer.Deserialize>(data); + } + } + + /// + /// Saves the custom viewport scaling options. + /// + public void SaveCustomViewportScalingOptions() + { + var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions); + Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions); + } + /// public override void OnUpdate() { diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2efe7c95f..f7c496703 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -6,6 +6,8 @@ using System.Linq; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.GUI.Input; +using FlaxEditor.Modules; using FlaxEditor.SceneGraph; using FlaxEditor.Scripting; using FlaxEditor.Viewport.Cameras; @@ -13,6 +15,7 @@ using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows.Assets; using FlaxEngine; using FlaxEngine.GUI; +using FlaxEngine.Json; using Utils = FlaxEditor.Utilities.Utils; namespace FlaxEditor.Viewport @@ -71,7 +74,10 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; + private int _defaultScaleActiveIndex = 0; + private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; + private ContextMenu _uiViewCM; /// /// Event fired when the UI Mode is toggled. @@ -213,7 +219,7 @@ namespace FlaxEditor.Viewport _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", (button) => ShowUI = button.Checked); _uiModeButton.AutoCheck = true; _uiModeButton.VisibleChanged += control => (control as ContextMenuButton).Checked = ShowUI; - + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions @@ -222,6 +228,309 @@ namespace FlaxEditor.Viewport SetUpdate(ref _update, OnUpdate); } + /// + /// Creates the view scaling options. Needs to be called after a Prefab is valid and loaded. + /// + public void CreateViewScalingOptions() + { + // View Scaling + var uiViewCM = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + LoadCustomUIScalingOption(); + CreateUIViewScalingContextMenu(uiViewCM.ContextMenu); + } + + private void ChangeUIView(UIModule.ViewportScaleOption uiViewScaleOption) + { + _uiRoot.SetViewSize((Float2)uiViewScaleOption.Size); + } + + private void CreateUIViewScalingContextMenu(ContextMenu vsMenu) + { + // Add default viewport sizing options + var defaultOptions = Editor.Instance.UI.DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) + { + var viewportScale = defaultOptions[i]; + + // Skip aspect ratio types in prefab + if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) + continue; + + var button = vsMenu.AddButton(viewportScale.Label); + button.CloseMenuOnClick = false; + button.Tag = viewportScale; + + // No default index is active. + if (_defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; + ChangeUIView(viewportScale); + } + + button.Clicked += () => + { + if (button.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (cmb == button) + { + var index = defaultOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = index; + _customScaleActiveIndex = -1; // Reset custom index because default was chosen. + button.Icon = Style.Current.CheckBoxTick; + ChangeUIView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + } + if (defaultOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add custom viewport options + var customOptions = Editor.Instance.UI.CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) + { + var viewportScale = customOptions[i]; + + // Skip aspect ratio types for prefabs + if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) + continue; + + var childCM = vsMenu.AddChildMenu(viewportScale.Label); + childCM.CloseMenuOnClick = false; + childCM.Tag = viewportScale; + + // No custom index is active. + if (_customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; + ChangeUIView(viewportScale); + } + + var applyButton = childCM.ContextMenu.AddButton("Apply"); + applyButton.Tag = childCM.Tag = viewportScale; + applyButton.CloseMenuOnClick = false; + applyButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (child == childCM) + { + var index = customOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. + _customScaleActiveIndex = index; + childCM.Icon = Style.Current.CheckBoxTick; + ChangeUIView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + + var deleteButton = childCM.ContextMenu.AddButton("Delete"); + deleteButton.CloseMenuOnClick = false; + deleteButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + var v = (UIModule.ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) + { + _customScaleActiveIndex = -1; + _defaultScaleActiveIndex = 0; + ChangeUIView(defaultOptions[0]); + } + customOptions.Remove(v); + Editor.Instance.UI.SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateUIViewScalingContextMenu(vsMenu); + vsMenu.PerformLayout(); + }; + } + if (customOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add button + var add = vsMenu.AddButton("Add..."); + add.CloseMenuOnClick = false; + add.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 95), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(add, new Float2(add.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + + var whLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Width & Height", + HorizontalAlignment = TextAlignment.Near, + }; + whLabel.LocalX += 10; + whLabel.LocalY += 30; + + var wValue = new IntValueBox(1920) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + wValue.LocalY += 30; + wValue.LocalX += 100; + + var hValue = new IntValueBox(1080) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + hValue.LocalY += 30; + hValue.LocalX += 165; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Submit", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 60; + + submitButton.Clicked += () => + { + var name = nameTextBox.Text + " (" + wValue.Value + "x" + hValue.Value + ")"; + + var newViewportOption = new UIModule.ViewportScaleOption + { + Label = name, + ScaleType = UIModule.ViewportScaleOption.ViewportScaleType.Resolution, + Size = new Int2(wValue.Value, hValue.Value), + }; + + customOptions.Add(newViewportOption); + Editor.Instance.UI.SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateUIViewScalingContextMenu(vsMenu); + vsMenu.PerformLayout(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 60; + + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + hValue.Value = 9; + wValue.Value = 16; + popup.Hide(); + }; + }; + } + + /// + /// Saves the active ui scaling option. + /// + public void SaveActiveUIScalingOption() + { + var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex"; + Editor.Instance.ProjectCache.SetCustomData(defaultKey, _defaultScaleActiveIndex.ToString()); + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; + Editor.Instance.ProjectCache.SetCustomData(customKey, _customScaleActiveIndex.ToString()); + } + + private void LoadCustomUIScalingOption() + { + Prefab.WaitForLoaded(); + var defaultKey = $"{Prefab.ID}:DefaultViewportScalingIndex"; + if (Editor.Instance.ProjectCache.TryGetCustomData(defaultKey, out string defaultData)) + { + if (int.TryParse(defaultData, out var index)) + { + _defaultScaleActiveIndex = index; + if (index != -1) + { + ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + } + } + } + + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; + if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data)) + { + if (int.TryParse(data, out var index)) + { + _customScaleActiveIndex = index; + if (index != -1) + { + ChangeUIView(Editor.Instance.UI.CustomViewportScaleOptions[index]); + } + } + } + } + private void OnUpdate(float deltaTime) { for (int i = 0; i < Gizmos.Count; i++) diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 44c21d863..8202577d0 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -368,6 +368,7 @@ namespace FlaxEditor.Windows.Assets else _viewport.SetInitialUIMode(_viewport._hasUILinked); _viewport.UIModeToggled += OnUIModeToggled; + _viewport.CreateViewScalingOptions(); Graph.MainActor = _viewport.Instance; Selection.Clear(); Select(Graph.Main); @@ -564,6 +565,15 @@ namespace FlaxEditor.Windows.Assets Graph.Dispose(); } + /// + protected override void OnClose() + { + // Save current UI view size state. + _viewport.SaveActiveUIScalingOption(); + + base.OnClose(); + } + /// public EditorViewport PresenterViewport => _viewport; diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index e05f0d2db..1f5399926 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -6,6 +6,7 @@ using System.Xml; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; +using FlaxEditor.Modules; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; @@ -34,8 +35,8 @@ namespace FlaxEditor.Windows private CursorLockMode _cursorLockMode = CursorLockMode.None; // Viewport scaling variables - private List _defaultViewportScaling = new List(); - private List _customViewportScaling = new List(); + private int _defaultScaleActiveIndex = 0; + private int _customScaleActiveIndex = -1; private float _viewportAspectRatio = 1; private float _windowAspectRatio = 1; private bool _useAspect = false; @@ -234,35 +235,6 @@ namespace FlaxEditor.Windows /// public InterfaceOptions.PlayModeFocus FocusOnPlayOption { get; set; } - private enum ViewportScaleType - { - Resolution = 0, - Aspect = 1, - } - - private class ViewportScaleOptions - { - /// - /// The name. - /// - public string Label; - - /// - /// The Type of scaling to do. - /// - public ViewportScaleType ScaleType; - - /// - /// The width and height to scale by. - /// - public Int2 Size; - - /// - /// If the scaling is active. - /// - public bool Active; - } - private class PlayModeFocusOptions { /// @@ -408,7 +380,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand()); } - private void ChangeViewportRatio(ViewportScaleOptions v) + private void ChangeViewportRatio(UIModule.ViewportScaleOption v) { if (v == null) return; @@ -427,11 +399,11 @@ namespace FlaxEditor.Windows { switch (v.ScaleType) { - case ViewportScaleType.Aspect: + case UIModule.ViewportScaleOption.ViewportScaleType.Aspect: _useAspect = true; _freeAspect = false; break; - case ViewportScaleType.Resolution: + case UIModule.ViewportScaleOption.ViewportScaleType.Resolution: _useAspect = false; _freeAspect = false; break; @@ -629,45 +601,6 @@ namespace FlaxEditor.Windows // Viewport aspect ratio { // Create default scaling options if they dont exist from deserialization. - if (_defaultViewportScaling.Count == 0) - { - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "Free Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(1, 1), - Active = true, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "16:9 Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(16, 9), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "16:10 Aspect", - ScaleType = ViewportScaleType.Aspect, - Size = new Int2(16, 10), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "1920x1080 Resolution (Full HD)", - ScaleType = ViewportScaleType.Resolution, - Size = new Int2(1920, 1080), - Active = false, - }); - _defaultViewportScaling.Add(new ViewportScaleOptions - { - Label = "2560x1440 Resolution (2K)", - ScaleType = ViewportScaleType.Resolution, - Size = new Int2(2560, 1440), - Active = false, - }); - } - var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu; CreateViewportSizingContextMenu(vsMenu); @@ -771,15 +704,25 @@ namespace FlaxEditor.Windows private void CreateViewportSizingContextMenu(ContextMenu vsMenu) { // Add default viewport sizing options - for (int i = 0; i < _defaultViewportScaling.Count; i++) + var defaultOptions = Editor.UI.DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) { - var viewportScale = _defaultViewportScaling[i]; + var viewportScale = defaultOptions[i]; var button = vsMenu.AddButton(viewportScale.Label); button.CloseMenuOnClick = false; - button.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; button.Tag = viewportScale; - if (viewportScale.Active) + + // No default index is active. + if (_defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(viewportScale); + } button.Clicked += () => { @@ -789,36 +732,47 @@ namespace FlaxEditor.Windows // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { - if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) { if (cmb == button) { - v.Active = true; + var index = defaultOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = index; + _customScaleActiveIndex = -1; // Reset custom index because default was chosen. button.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } - else if (v.Active) + else if (cmb.Icon != SpriteHandle.Invalid) { cmb.Icon = SpriteHandle.Invalid; - v.Active = false; } } } }; } - if (_defaultViewportScaling.Count != 0) + if (defaultOptions.Count != 0) vsMenu.AddSeparator(); // Add custom viewport options - for (int i = 0; i < _customViewportScaling.Count; i++) + var customOptions = Editor.UI.CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) { - var viewportScale = _customViewportScaling[i]; + var viewportScale = customOptions[i]; var childCM = vsMenu.AddChildMenu(viewportScale.Label); childCM.CloseMenuOnClick = false; - childCM.Icon = viewportScale.Active ? Style.Current.CheckBoxTick : SpriteHandle.Invalid; childCM.Tag = viewportScale; - if (viewportScale.Active) + + // No custom index is active. + if (_customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index. + else if (_customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(viewportScale); + } var applyButton = childCM.ContextMenu.AddButton("Apply"); applyButton.Tag = childCM.Tag = viewportScale; @@ -831,18 +785,19 @@ namespace FlaxEditor.Windows // Reset selected icon on all buttons foreach (var child in vsMenu.Items) { - if (child is ContextMenuButton cmb && cmb.Tag is ViewportScaleOptions v) + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) { if (child == childCM) { - v.Active = true; + var index = customOptions.FindIndex(x => x == v); + _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. + _customScaleActiveIndex = index; childCM.Icon = Style.Current.CheckBoxTick; ChangeViewportRatio(v); } - else if (v.Active) + else if (cmb.Icon != SpriteHandle.Invalid) { cmb.Icon = SpriteHandle.Invalid; - v.Active = false; } } } @@ -855,20 +810,21 @@ namespace FlaxEditor.Windows if (childCM.Tag == null) return; - var v = (ViewportScaleOptions)childCM.Tag; - if (v.Active) + var v = (UIModule.ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) { - v.Active = false; - _defaultViewportScaling[0].Active = true; - ChangeViewportRatio(_defaultViewportScaling[0]); + _customScaleActiveIndex = -1; + _defaultScaleActiveIndex = 0; + ChangeViewportRatio(defaultOptions[0]); } - _customViewportScaling.Remove(v); + customOptions.Remove(v); + Editor.UI.SaveCustomViewportScalingOptions(); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); }; } - if (_customViewportScaling.Count != 0) + if (customOptions.Count != 0) vsMenu.AddSeparator(); // Add button @@ -966,19 +922,20 @@ namespace FlaxEditor.Windows submitButton.Clicked += () => { - Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleType type); + Enum.TryParse(typeDropdown.SelectedItem, out UIModule.ViewportScaleOption.ViewportScaleType type); - var combineString = type == ViewportScaleType.Aspect ? ":" : "x"; + var combineString = type == UIModule.ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; - var newViewportOption = new ViewportScaleOptions + var newViewportOption = new UIModule.ViewportScaleOption { ScaleType = type, Label = name, Size = new Int2(wValue.Value, hValue.Value), }; - _customViewportScaling.Add(newViewportOption); + customOptions.Add(newViewportOption); + Editor.UI.SaveCustomViewportScalingOptions(); vsMenu.DisposeAllItems(); CreateViewportSizingContextMenu(vsMenu); vsMenu.PerformLayout(); @@ -1221,8 +1178,8 @@ namespace FlaxEditor.Windows writer.WriteAttributeString("ShowGUI", ShowGUI.ToString()); writer.WriteAttributeString("EditGUI", EditGUI.ToString()); writer.WriteAttributeString("ShowDebugDraw", ShowDebugDraw.ToString()); - writer.WriteAttributeString("DefaultViewportScaling", JsonSerializer.Serialize(_defaultViewportScaling)); - writer.WriteAttributeString("CustomViewportScaling", JsonSerializer.Serialize(_customViewportScaling)); + writer.WriteAttributeString("DefaultViewportScalingIndex", _defaultScaleActiveIndex.ToString()); + writer.WriteAttributeString("CustomViewportScalingIndex", _customScaleActiveIndex.ToString()); } /// @@ -1234,22 +1191,21 @@ namespace FlaxEditor.Windows EditGUI = value1; if (bool.TryParse(node.GetAttribute("ShowDebugDraw"), out value1)) ShowDebugDraw = value1; - if (node.HasAttribute("CustomViewportScaling")) - _customViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("CustomViewportScaling")); + if (int.TryParse(node.GetAttribute("DefaultViewportScalingIndex"), out int value2)) + _defaultScaleActiveIndex = value2; + if (int.TryParse(node.GetAttribute("CustomViewportScalingIndex"), out value2)) + _customScaleActiveIndex = value2; - for (int i = 0; i < _customViewportScaling.Count; i++) + if (_defaultScaleActiveIndex != -1) { - if (_customViewportScaling[i].Active) - ChangeViewportRatio(_customViewportScaling[i]); + var options = Editor.UI.DefaultViewportScaleOptions; + ChangeViewportRatio(options[_defaultScaleActiveIndex]); } - - if (node.HasAttribute("DefaultViewportScaling")) - _defaultViewportScaling = JsonSerializer.Deserialize>(node.GetAttribute("DefaultViewportScaling")); - - for (int i = 0; i < _defaultViewportScaling.Count; i++) + + if (_customScaleActiveIndex != -1) { - if (_defaultViewportScaling[i].Active) - ChangeViewportRatio(_defaultViewportScaling[i]); + var options = Editor.UI.CustomViewportScaleOptions; + ChangeViewportRatio(options[_customScaleActiveIndex]); } } From b9cfd054c180d3268805c7f23dc8f35461bd75b2 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 27 Jun 2025 21:38:10 -0500 Subject: [PATCH 2/4] Remove unused variable --- Source/Editor/Viewport/PrefabWindowViewport.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index f7c496703..2ca4feb27 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -77,7 +77,6 @@ namespace FlaxEditor.Viewport private int _defaultScaleActiveIndex = 0; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; - private ContextMenu _uiViewCM; /// /// Event fired when the UI Mode is toggled. From 52b64540ab26624f87b630606d4fc7bebd184a27 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 28 Jun 2025 10:46:11 -0500 Subject: [PATCH 3/4] Add extra index guard logic --- .../Editor/Viewport/PrefabWindowViewport.cs | 27 ++++++++++++++----- Source/Editor/Windows/GameWindow.cs | 13 +++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 2ca4feb27..10a6789a0 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -508,10 +508,17 @@ namespace FlaxEditor.Viewport { if (int.TryParse(defaultData, out var index)) { - _defaultScaleActiveIndex = index; - if (index != -1) + var options = Editor.Instance.UI.DefaultViewportScaleOptions; + if (options.Count > index) { - ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + _defaultScaleActiveIndex = index; + if (index != -1) + ChangeUIView(Editor.Instance.UI.DefaultViewportScaleOptions[index]); + } + // Assume option does not exist anymore so move to default. + else if (index != -1) + { + _defaultScaleActiveIndex = 0; } } } @@ -521,10 +528,18 @@ namespace FlaxEditor.Viewport { if (int.TryParse(data, out var index)) { - _customScaleActiveIndex = index; - if (index != -1) + var options = Editor.Instance.UI.CustomViewportScaleOptions; + if (options.Count > index) { - ChangeUIView(Editor.Instance.UI.CustomViewportScaleOptions[index]); + _customScaleActiveIndex = index; + if (index != -1) + ChangeUIView(options[index]); + } + // Assume option does not exist anymore so move to default. + else if (index != -1) + { + _defaultScaleActiveIndex = 0; + _customScaleActiveIndex = -1; } } } diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 1f5399926..db3ea9d7e 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -1199,13 +1199,22 @@ namespace FlaxEditor.Windows if (_defaultScaleActiveIndex != -1) { var options = Editor.UI.DefaultViewportScaleOptions; - ChangeViewportRatio(options[_defaultScaleActiveIndex]); + if (options.Count > _defaultScaleActiveIndex) + ChangeViewportRatio(options[_defaultScaleActiveIndex]); + else + _defaultScaleActiveIndex = 0; } if (_customScaleActiveIndex != -1) { var options = Editor.UI.CustomViewportScaleOptions; - ChangeViewportRatio(options[_customScaleActiveIndex]); + if (options.Count > _customScaleActiveIndex) + ChangeViewportRatio(options[_customScaleActiveIndex]); + else + { + _defaultScaleActiveIndex = 0; + _customScaleActiveIndex = -1; + } } } From 9749487e2449aff9e5320ae7140266dedd35c033 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Sep 2025 23:59:40 +0200 Subject: [PATCH 4/4] Move prefab and game UI size context menu to shared UI Module #3571 --- Source/Editor/Modules/UIModule.cs | 262 +++++++++++++++++ .../Editor/Viewport/PrefabWindowViewport.cs | 269 ++---------------- Source/Editor/Windows/GameWindow.cs | 269 +----------------- 3 files changed, 283 insertions(+), 517 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index e62dd6b6e..64f8c81e9 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -1185,5 +1185,267 @@ namespace FlaxEditor.Modules MenuTools = null; MenuHelp = null; } + + internal void CreateViewportSizingContextMenu(ContextMenu vsMenu, int defaultScaleActiveIndex, int customScaleActiveIndex, bool prefabViewport, Action changeView, Action changeActiveIndices) + { + // Add default viewport sizing options + var defaultOptions = DefaultViewportScaleOptions; + for (int i = 0; i < defaultOptions.Count; i++) + { + var viewportScale = defaultOptions[i]; + if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect) + continue; // Skip aspect ratio types in prefab + var button = vsMenu.AddButton(viewportScale.Label); + button.CloseMenuOnClick = false; + button.Tag = viewportScale; + + // No default index is active + if (defaultScaleActiveIndex == -1) + { + button.Icon = SpriteHandle.Invalid; + } + // This is the active index + else if (defaultScaleActiveIndex == i) + { + button.Icon = Style.Current.CheckBoxTick; + changeView(viewportScale); + } + + button.Clicked += () => + { + if (button.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (cmb == button) + { + button.Icon = Style.Current.CheckBoxTick; + var index = defaultOptions.FindIndex(x => x == v); + changeActiveIndices(index, -1); // Reset custom index because default was chosen + changeView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + } + if (defaultOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add custom viewport options + var customOptions = CustomViewportScaleOptions; + for (int i = 0; i < customOptions.Count; i++) + { + var viewportScale = customOptions[i]; + if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect) + continue; // Skip aspect ratio types in prefab + var childCM = vsMenu.AddChildMenu(viewportScale.Label); + childCM.CloseMenuOnClick = false; + childCM.Tag = viewportScale; + + // No custom index is active + if (customScaleActiveIndex == -1) + { + childCM.Icon = SpriteHandle.Invalid; + } + // This is the active index + else if (customScaleActiveIndex == i) + { + childCM.Icon = Style.Current.CheckBoxTick; + changeView(viewportScale); + } + + var applyButton = childCM.ContextMenu.AddButton("Apply"); + applyButton.Tag = childCM.Tag = viewportScale; + applyButton.CloseMenuOnClick = false; + applyButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + // Reset selected icon on all buttons + foreach (var child in vsMenu.Items) + { + if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) + { + if (child == childCM) + { + childCM.Icon = Style.Current.CheckBoxTick; + var index = customOptions.FindIndex(x => x == v); + changeActiveIndices(-1, index); // Reset default index because custom was chosen + changeView(v); + } + else if (cmb.Icon != SpriteHandle.Invalid) + { + cmb.Icon = SpriteHandle.Invalid; + } + } + } + }; + + var deleteButton = childCM.ContextMenu.AddButton("Delete"); + deleteButton.CloseMenuOnClick = false; + deleteButton.Clicked += () => + { + if (childCM.Tag == null) + return; + + var v = (ViewportScaleOption)childCM.Tag; + if (childCM.Icon != SpriteHandle.Invalid) + { + changeActiveIndices(-1, 0); + changeView(defaultOptions[0]); + } + customOptions.Remove(v); + SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices); + vsMenu.PerformLayout(); + }; + } + if (customOptions.Count != 0) + vsMenu.AddSeparator(); + + // Add button + var add = vsMenu.AddButton("Add..."); + add.CloseMenuOnClick = false; + add.Clicked += () => + { + var popup = new ContextMenuBase + { + Size = new Float2(230, 125), + ClipChildren = false, + CullChildren = false, + }; + popup.Show(add, new Float2(add.Width, 0)); + + var nameLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Name", + HorizontalAlignment = TextAlignment.Near, + }; + nameLabel.LocalX += 10; + nameLabel.LocalY += 10; + + var nameTextBox = new TextBox + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + IsMultiline = false, + }; + nameTextBox.LocalX += 100; + nameTextBox.LocalY += 10; + + var typeLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Type", + HorizontalAlignment = TextAlignment.Near, + }; + typeLabel.LocalX += 10; + typeLabel.LocalY += 35; + + var typeDropdown = new Dropdown + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Items = { "Aspect", "Resolution" }, + SelectedItem = "Aspect", + Visible = !prefabViewport, + Width = nameTextBox.Width + }; + typeDropdown.LocalY += 35; + typeDropdown.LocalX += 100; + + var whLabel = new Label + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Width & Height", + HorizontalAlignment = TextAlignment.Near, + }; + whLabel.LocalX += 10; + whLabel.LocalY += 60; + + var wValue = new IntValueBox(16) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + wValue.LocalY += 60; + wValue.LocalX += 100; + + var hValue = new IntValueBox(9) + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + MinValue = 1, + Width = 55, + }; + hValue.LocalY += 60; + hValue.LocalX += 165; + + var submitButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Submit", + Width = 70, + }; + submitButton.LocalX += 40; + submitButton.LocalY += 90; + submitButton.Clicked += () => + { + Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleOption.ViewportScaleType type); + if (prefabViewport) + type = ViewportScaleOption.ViewportScaleType.Resolution; + + var combineString = type == ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; + var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; + var newViewportOption = new ViewportScaleOption + { + ScaleType = type, + Label = name, + Size = new Int2(wValue.Value, hValue.Value), + }; + + customOptions.Add(newViewportOption); + SaveCustomViewportScalingOptions(); + vsMenu.DisposeAllItems(); + CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices); + vsMenu.PerformLayout(); + }; + + var cancelButton = new Button + { + Parent = popup, + AnchorPreset = AnchorPresets.TopLeft, + Text = "Cancel", + Width = 70, + }; + cancelButton.LocalX += 120; + cancelButton.LocalY += 90; + cancelButton.Clicked += () => + { + nameTextBox.Clear(); + typeDropdown.SelectedItem = "Aspect"; + hValue.Value = 9; + wValue.Value = 16; + popup.Hide(); + }; + }; + } } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1a018004e..c38b36f1e 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -73,10 +73,11 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; - + private int _defaultScaleActiveIndex = 0; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; + private ContextMenuChildMenu _uiViewOptions; /// /// Event fired when the UI Mode is toggled. @@ -142,6 +143,8 @@ namespace FlaxEditor.Viewport UseAutomaticTaskManagement = defaultFeatures; ShowDefaultSceneActors = defaultFeatures; TintColor = defaultFeatures ? Color.White : Color.Transparent; + if (_uiViewOptions != null) + _uiViewOptions.Visible = _showUI; UIModeToggled?.Invoke(_showUI); } } @@ -215,10 +218,10 @@ namespace FlaxEditor.Viewport _uiParentLink = _uiRoot.UIRoot; // UI mode buton - _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", (button) => ShowUI = button.Checked); + _uiModeButton = ViewWidgetShowMenu.AddButton("UI Mode", button => ShowUI = button.Checked); _uiModeButton.AutoCheck = true; _uiModeButton.VisibleChanged += control => (control as ContextMenuButton).Checked = ShowUI; - + EditorGizmoViewport.AddGizmoViewportWidgets(this, TransformGizmo); // Setup input actions @@ -232,10 +235,16 @@ namespace FlaxEditor.Viewport /// public void CreateViewScalingOptions() { - // View Scaling - var uiViewCM = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + if (_uiViewOptions != null) + return; + _uiViewOptions = ViewWidgetButtonMenu.AddChildMenu("UI View Scaling"); + _uiViewOptions.Visible = _showUI; LoadCustomUIScalingOption(); - CreateUIViewScalingContextMenu(uiViewCM.ContextMenu); + Editor.Instance.UI.CreateViewportSizingContextMenu(_uiViewOptions.ContextMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, true, ChangeUIView, (a, b) => + { + _defaultScaleActiveIndex = a; + _customScaleActiveIndex = b; + }); } private void ChangeUIView(UIModule.ViewportScaleOption uiViewScaleOption) @@ -243,252 +252,6 @@ namespace FlaxEditor.Viewport _uiRoot.SetViewSize((Float2)uiViewScaleOption.Size); } - private void CreateUIViewScalingContextMenu(ContextMenu vsMenu) - { - // Add default viewport sizing options - var defaultOptions = Editor.Instance.UI.DefaultViewportScaleOptions; - for (int i = 0; i < defaultOptions.Count; i++) - { - var viewportScale = defaultOptions[i]; - - // Skip aspect ratio types in prefab - if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) - continue; - - var button = vsMenu.AddButton(viewportScale.Label); - button.CloseMenuOnClick = false; - button.Tag = viewportScale; - - // No default index is active. - if (_defaultScaleActiveIndex == -1) - { - button.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_defaultScaleActiveIndex == i) - { - button.Icon = Style.Current.CheckBoxTick; - ChangeUIView(viewportScale); - } - - button.Clicked += () => - { - if (button.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (cmb == button) - { - var index = defaultOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = index; - _customScaleActiveIndex = -1; // Reset custom index because default was chosen. - button.Icon = Style.Current.CheckBoxTick; - ChangeUIView(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - } - if (defaultOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add custom viewport options - var customOptions = Editor.Instance.UI.CustomViewportScaleOptions; - for (int i = 0; i < customOptions.Count; i++) - { - var viewportScale = customOptions[i]; - - // Skip aspect ratio types for prefabs - if (viewportScale.ScaleType == UIModule.ViewportScaleOption.ViewportScaleType.Aspect) - continue; - - var childCM = vsMenu.AddChildMenu(viewportScale.Label); - childCM.CloseMenuOnClick = false; - childCM.Tag = viewportScale; - - // No custom index is active. - if (_customScaleActiveIndex == -1) - { - childCM.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_customScaleActiveIndex == i) - { - childCM.Icon = Style.Current.CheckBoxTick; - ChangeUIView(viewportScale); - } - - var applyButton = childCM.ContextMenu.AddButton("Apply"); - applyButton.Tag = childCM.Tag = viewportScale; - applyButton.CloseMenuOnClick = false; - applyButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (child == childCM) - { - var index = customOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. - _customScaleActiveIndex = index; - childCM.Icon = Style.Current.CheckBoxTick; - ChangeUIView(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - - var deleteButton = childCM.ContextMenu.AddButton("Delete"); - deleteButton.CloseMenuOnClick = false; - deleteButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - var v = (UIModule.ViewportScaleOption)childCM.Tag; - if (childCM.Icon != SpriteHandle.Invalid) - { - _customScaleActiveIndex = -1; - _defaultScaleActiveIndex = 0; - ChangeUIView(defaultOptions[0]); - } - customOptions.Remove(v); - Editor.Instance.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateUIViewScalingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - } - if (customOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add button - var add = vsMenu.AddButton("Add..."); - add.CloseMenuOnClick = false; - add.Clicked += () => - { - var popup = new ContextMenuBase - { - Size = new Float2(230, 95), - ClipChildren = false, - CullChildren = false, - }; - popup.Show(add, new Float2(add.Width, 0)); - - var nameLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Name", - HorizontalAlignment = TextAlignment.Near, - }; - nameLabel.LocalX += 10; - nameLabel.LocalY += 10; - - var nameTextBox = new TextBox - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - IsMultiline = false, - }; - nameTextBox.LocalX += 100; - nameTextBox.LocalY += 10; - - var whLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Width & Height", - HorizontalAlignment = TextAlignment.Near, - }; - whLabel.LocalX += 10; - whLabel.LocalY += 30; - - var wValue = new IntValueBox(1920) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - wValue.LocalY += 30; - wValue.LocalX += 100; - - var hValue = new IntValueBox(1080) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - hValue.LocalY += 30; - hValue.LocalX += 165; - - var submitButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Submit", - Width = 70, - }; - submitButton.LocalX += 40; - submitButton.LocalY += 60; - - submitButton.Clicked += () => - { - var name = nameTextBox.Text + " (" + wValue.Value + "x" + hValue.Value + ")"; - - var newViewportOption = new UIModule.ViewportScaleOption - { - Label = name, - ScaleType = UIModule.ViewportScaleOption.ViewportScaleType.Resolution, - Size = new Int2(wValue.Value, hValue.Value), - }; - - customOptions.Add(newViewportOption); - Editor.Instance.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateUIViewScalingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - - var cancelButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Cancel", - Width = 70, - }; - cancelButton.LocalX += 120; - cancelButton.LocalY += 60; - - cancelButton.Clicked += () => - { - nameTextBox.Clear(); - hValue.Value = 9; - wValue.Value = 16; - popup.Hide(); - }; - }; - } - /// /// Saves the active ui scaling option. /// @@ -522,7 +285,7 @@ namespace FlaxEditor.Viewport } } } - + var customKey = $"{Prefab.ID}:CustomViewportScalingIndex"; if (Editor.Instance.ProjectCache.TryGetCustomData(customKey, out string data)) { diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index a2b2d472d..63c2a1a48 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -606,10 +606,12 @@ namespace FlaxEditor.Windows // Viewport aspect ratio { - // Create default scaling options if they dont exist from deserialization. var vsMenu = menu.AddChildMenu("Viewport Size").ContextMenu; - - CreateViewportSizingContextMenu(vsMenu); + Editor.UI.CreateViewportSizingContextMenu(vsMenu, _defaultScaleActiveIndex, _customScaleActiveIndex, false, ChangeViewportRatio, (a, b) => + { + _defaultScaleActiveIndex = a; + _customScaleActiveIndex = b; + }); } // Take Screenshot @@ -707,267 +709,6 @@ namespace FlaxEditor.Windows } } - private void CreateViewportSizingContextMenu(ContextMenu vsMenu) - { - // Add default viewport sizing options - var defaultOptions = Editor.UI.DefaultViewportScaleOptions; - for (int i = 0; i < defaultOptions.Count; i++) - { - var viewportScale = defaultOptions[i]; - var button = vsMenu.AddButton(viewportScale.Label); - button.CloseMenuOnClick = false; - button.Tag = viewportScale; - - // No default index is active. - if (_defaultScaleActiveIndex == -1) - { - button.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_defaultScaleActiveIndex == i) - { - button.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(viewportScale); - } - - button.Clicked += () => - { - if (button.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (cmb == button) - { - var index = defaultOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = index; - _customScaleActiveIndex = -1; // Reset custom index because default was chosen. - button.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - } - if (defaultOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add custom viewport options - var customOptions = Editor.UI.CustomViewportScaleOptions; - for (int i = 0; i < customOptions.Count; i++) - { - var viewportScale = customOptions[i]; - var childCM = vsMenu.AddChildMenu(viewportScale.Label); - childCM.CloseMenuOnClick = false; - childCM.Tag = viewportScale; - - // No custom index is active. - if (_customScaleActiveIndex == -1) - { - childCM.Icon = SpriteHandle.Invalid; - } - // This is the active index. - else if (_customScaleActiveIndex == i) - { - childCM.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(viewportScale); - } - - var applyButton = childCM.ContextMenu.AddButton("Apply"); - applyButton.Tag = childCM.Tag = viewportScale; - applyButton.CloseMenuOnClick = false; - applyButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - // Reset selected icon on all buttons - foreach (var child in vsMenu.Items) - { - if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v) - { - if (child == childCM) - { - var index = customOptions.FindIndex(x => x == v); - _defaultScaleActiveIndex = -1; // Reset default index because custom was chosen. - _customScaleActiveIndex = index; - childCM.Icon = Style.Current.CheckBoxTick; - ChangeViewportRatio(v); - } - else if (cmb.Icon != SpriteHandle.Invalid) - { - cmb.Icon = SpriteHandle.Invalid; - } - } - } - }; - - var deleteButton = childCM.ContextMenu.AddButton("Delete"); - deleteButton.CloseMenuOnClick = false; - deleteButton.Clicked += () => - { - if (childCM.Tag == null) - return; - - var v = (UIModule.ViewportScaleOption)childCM.Tag; - if (childCM.Icon != SpriteHandle.Invalid) - { - _customScaleActiveIndex = -1; - _defaultScaleActiveIndex = 0; - ChangeViewportRatio(defaultOptions[0]); - } - customOptions.Remove(v); - Editor.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateViewportSizingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - } - if (customOptions.Count != 0) - vsMenu.AddSeparator(); - - // Add button - var add = vsMenu.AddButton("Add..."); - add.CloseMenuOnClick = false; - add.Clicked += () => - { - var popup = new ContextMenuBase - { - Size = new Float2(230, 125), - ClipChildren = false, - CullChildren = false, - }; - popup.Show(add, new Float2(add.Width, 0)); - - var nameLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Name", - HorizontalAlignment = TextAlignment.Near, - }; - nameLabel.LocalX += 10; - nameLabel.LocalY += 10; - - var nameTextBox = new TextBox - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - IsMultiline = false, - }; - nameTextBox.LocalX += 100; - nameTextBox.LocalY += 10; - - var typeLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Type", - HorizontalAlignment = TextAlignment.Near, - }; - typeLabel.LocalX += 10; - typeLabel.LocalY += 35; - - var typeDropdown = new Dropdown - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Items = { "Aspect", "Resolution" }, - SelectedItem = "Aspect", - Width = nameTextBox.Width - }; - typeDropdown.LocalY += 35; - typeDropdown.LocalX += 100; - - var whLabel = new Label - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Width & Height", - HorizontalAlignment = TextAlignment.Near, - }; - whLabel.LocalX += 10; - whLabel.LocalY += 60; - - var wValue = new IntValueBox(16) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - wValue.LocalY += 60; - wValue.LocalX += 100; - - var hValue = new IntValueBox(9) - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - MinValue = 1, - Width = 55, - }; - hValue.LocalY += 60; - hValue.LocalX += 165; - - var submitButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Submit", - Width = 70, - }; - submitButton.LocalX += 40; - submitButton.LocalY += 90; - - submitButton.Clicked += () => - { - Enum.TryParse(typeDropdown.SelectedItem, out UIModule.ViewportScaleOption.ViewportScaleType type); - - var combineString = type == UIModule.ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x"; - var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem; - - var newViewportOption = new UIModule.ViewportScaleOption - { - ScaleType = type, - Label = name, - Size = new Int2(wValue.Value, hValue.Value), - }; - - customOptions.Add(newViewportOption); - Editor.UI.SaveCustomViewportScalingOptions(); - vsMenu.DisposeAllItems(); - CreateViewportSizingContextMenu(vsMenu); - vsMenu.PerformLayout(); - }; - - var cancelButton = new Button - { - Parent = popup, - AnchorPreset = AnchorPresets.TopLeft, - Text = "Cancel", - Width = 70, - }; - cancelButton.LocalX += 120; - cancelButton.LocalY += 90; - - cancelButton.Clicked += () => - { - nameTextBox.Clear(); - typeDropdown.SelectedItem = "Aspect"; - hValue.Value = 9; - wValue.Value = 16; - popup.Hide(); - }; - }; - } - /// public override void Draw() {