diff --git a/Source/Editor/Content/Create/CreateFilesDialog.cs b/Source/Editor/Content/Create/CreateFilesDialog.cs index 537cec5d5..710d04144 100644 --- a/Source/Editor/Content/Create/CreateFilesDialog.cs +++ b/Source/Editor/Content/Create/CreateFilesDialog.cs @@ -62,7 +62,7 @@ namespace FlaxEditor.Content.Create Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight), Parent = this }; - createButton.Clicked += OnCreate; + createButton.Clicked += OnSubmit; var cancelButton = new Button { Text = "Cancel", @@ -89,27 +89,12 @@ namespace FlaxEditor.Content.Create _settingsEditor.Select(_entry.Settings); } - private void OnCreate() + /// + public override void OnSubmit() { Editor.Instance.ContentImporting.LetThemBeCreatedxD(_entry); - Close(DialogResult.OK); - } - private void OnCancel() - { - Close(DialogResult.Cancel); - } - - private void OnSelectedChanged(List before, List after) - { - var selection = new List(after.Count); - for (int i = 0; i < after.Count; i++) - { - if (after[i].Tag is CreateFileEntry fileEntry && fileEntry.HasSettings) - selection.Add(fileEntry.Settings); - } - - _settingsEditor.Select(selection); + base.OnSubmit(); } /// diff --git a/Source/Editor/Content/Import/ImportFilesDialog.cs b/Source/Editor/Content/Import/ImportFilesDialog.cs index 84f957733..77b675258 100644 --- a/Source/Editor/Content/Import/ImportFilesDialog.cs +++ b/Source/Editor/Content/Import/ImportFilesDialog.cs @@ -83,7 +83,7 @@ namespace FlaxEditor.Content.Import Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight), Parent = this }; - importButton.Clicked += OnImport; + importButton.Clicked += OnSubmit; var cancelButton = new Button { Text = "Cancel", @@ -223,24 +223,6 @@ namespace FlaxEditor.Content.Import } } - private void OnImport() - { - var entries = new List(_rootNode.ChildrenCount); - for (int i = 0; i < _rootNode.ChildrenCount; i++) - { - if (_rootNode.Children[i].Tag is ImportFileEntry fileEntry) - entries.Add(fileEntry); - } - Editor.Instance.ContentImporting.LetThemBeImportedxD(entries); - - Close(DialogResult.OK); - } - - private void OnCancel() - { - Close(DialogResult.Cancel); - } - private void OnSelectedChanged(List before, List after) { var selection = new List(after.Count); @@ -253,6 +235,20 @@ namespace FlaxEditor.Content.Import _settingsEditor.Select(selection); } + /// + public override void OnSubmit() + { + var entries = new List(_rootNode.ChildrenCount); + for (int i = 0; i < _rootNode.ChildrenCount; i++) + { + if (_rootNode.Children[i].Tag is ImportFileEntry fileEntry) + entries.Add(fileEntry); + } + Editor.Instance.ContentImporting.LetThemBeImportedxD(entries); + + base.OnSubmit(); + } + /// protected override void SetupWindowSettings(ref CreateWindowSettings settings) { @@ -261,24 +257,5 @@ namespace FlaxEditor.Content.Import settings.MinimumSize = new Vector2(300, 400); settings.HasSizingFrame = true; } - - /// - public override bool OnKeyDown(KeyboardKeys key) - { - if (base.OnKeyDown(key)) - return true; - - switch (key) - { - case KeyboardKeys.Escape: - OnCancel(); - return true; - case KeyboardKeys.Return: - OnImport(); - return true; - } - - return false; - } } } diff --git a/Source/Editor/Content/Items/ContentItem.cs b/Source/Editor/Content/Items/ContentItem.cs index 4f0c7c039..c92fdc66d 100644 --- a/Source/Editor/Content/Items/ContentItem.cs +++ b/Source/Editor/Content/Items/ContentItem.cs @@ -612,6 +612,15 @@ namespace FlaxEditor.Content return result; } + /// + public override void NavigationFocus() + { + base.NavigationFocus(); + + if (IsFocused) + (Parent as ContentView)?.Select(this); + } + /// public override void Draw() { @@ -738,6 +747,15 @@ namespace FlaxEditor.Content base.OnMouseLeave(); } + /// + public override void OnSubmit() + { + // Open + (Parent as ContentView).OnItemDoubleClick(this); + + base.OnSubmit(); + } + /// public override int Compare(Control other) { diff --git a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs index c6b3fbe64..c34dddf6f 100644 --- a/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FlaxObjectRefEditor.cs @@ -191,7 +191,7 @@ namespace FlaxEditor.CustomEditors.Editors var button2Rect = new Rectangle(button1Rect.Right + 2, 1, 14, 14); // Draw frame - Render2D.DrawRectangle(frameRect, isEnabled && IsMouseOver ? style.BorderHighlighted : style.BorderNormal); + Render2D.DrawRectangle(frameRect, isEnabled && (IsMouseOver || IsNavFocused) ? style.BorderHighlighted : style.BorderNormal); // Check if has item selected if (isSelected) @@ -331,6 +331,16 @@ namespace FlaxEditor.CustomEditors.Editors return base.OnMouseDoubleClick(location, button); } + /// + public override void OnSubmit() + { + base.OnSubmit(); + + // Picker dropdown menu + if (_supportsPickDropDown) + ShowDropDownMenu(); + } + private void DoDrag() { // Do the drag drop operation if has selected element diff --git a/Source/Editor/GUI/ComboBox.cs b/Source/Editor/GUI/ComboBox.cs index d06c1eef9..333834262 100644 --- a/Source/Editor/GUI/ComboBox.cs +++ b/Source/Editor/GUI/ComboBox.cs @@ -374,6 +374,72 @@ namespace FlaxEditor.GUI } } + /// + /// Shows the context menu popup. + /// + protected void ShowPopup() + { + // Ensure to have valid menu + if (_popupMenu == null) + { + _popupMenu = OnCreatePopup(); + _popupMenu.MaximumItemsInViewCount = MaximumItemsInViewCount; + + // Bind events + _popupMenu.VisibleChanged += cm => + { + var win = Root; + _blockPopup = win != null && new Rectangle(Vector2.Zero, Size).Contains(PointFromWindow(win.MousePosition)); + if (!_blockPopup) + Focus(); + }; + _popupMenu.ButtonClicked += btn => + { + OnItemClicked((int)btn.Tag); + _popupMenu?.Hide(); + }; + } + + // Check if menu hs been already shown + if (_popupMenu.Visible) + { + _popupMenu.Hide(); + return; + } + + PopupShowing?.Invoke(this); + + // Check if has any items + if (_items.Count > 0) + { + // Setup items list + var itemControls = _popupMenu.Items.ToArray(); + foreach (var e in itemControls) + e.Dispose(); + if (Sorted) + _items.Sort(); + var style = Style.Current; + for (int i = 0; i < _items.Count; i++) + { + var btn = _popupMenu.AddButton(_items[i]); + if (_selectedIndices.Contains(i)) + { + btn.Icon = style.CheckBoxTick; + } + if (_tooltips != null && _tooltips.Length > i) + { + btn.TooltipText = _tooltips[i]; + } + + btn.Tag = i; + } + + // Show dropdown list + _popupMenu.MinimumWidth = Width; + _popupMenu.Show(this, new Vector2(1, Height)); + } + } + /// /// Creates the popup menu. /// @@ -426,7 +492,7 @@ namespace FlaxEditor.GUI borderColor = BorderColorSelected; arrowColor = ArrowColorSelected; } - else if (IsMouseOver) + else if (IsMouseOver || IsNavFocused) { backgroundColor = BackgroundColorHighlighted; borderColor = BorderColorHighlighted; @@ -478,11 +544,10 @@ namespace FlaxEditor.GUI /// public override bool OnMouseDown(Vector2 location, MouseButton button) { - // Check mouse buttons if (button == MouseButton.Left) { - // Set flag _mouseDown = true; + Focus(); } return base.OnMouseDown(location, button); @@ -491,72 +556,10 @@ namespace FlaxEditor.GUI /// public override bool OnMouseUp(Vector2 location, MouseButton button) { - // Check flags if (_mouseDown && !_blockPopup) { - // Clear flag _mouseDown = false; - - // Ensure to have valid menu - if (_popupMenu == null) - { - _popupMenu = OnCreatePopup(); - _popupMenu.MaximumItemsInViewCount = MaximumItemsInViewCount; - - // Bind events - _popupMenu.VisibleChanged += cm => - { - var win = Root; - _blockPopup = win != null && new Rectangle(Vector2.Zero, Size).Contains(PointFromWindow(win.MousePosition)); - if (!_blockPopup) - Focus(); - }; - _popupMenu.ButtonClicked += btn => - { - OnItemClicked((int)btn.Tag); - _popupMenu?.Hide(); - }; - } - - // Check if menu hs been already shown - if (_popupMenu.Visible) - { - // Hide - _popupMenu.Hide(); - return true; - } - - PopupShowing?.Invoke(this); - - // Check if has any items - if (_items.Count > 0) - { - // Setup items list - var itemControls = _popupMenu.Items.ToArray(); - foreach (var e in itemControls) - e.Dispose(); - if (Sorted) - _items.Sort(); - var style = Style.Current; - for (int i = 0; i < _items.Count; i++) - { - var btn = _popupMenu.AddButton(_items[i]); - if (_selectedIndices.Contains(i)) - { - btn.Icon = style.CheckBoxTick; - } - if (_tooltips != null && _tooltips.Length > i) - { - btn.TooltipText = _tooltips[i]; - } - - btn.Tag = i; - } - - // Show dropdown list - _popupMenu.MinimumWidth = Width; - _popupMenu.Show(this, new Vector2(1, Height)); - } + ShowPopup(); } else { @@ -565,5 +568,13 @@ namespace FlaxEditor.GUI return true; } + + /// + public override void OnSubmit() + { + base.OnSubmit(); + + ShowPopup(); + } } } diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs index 6e5be2d04..2747817a4 100644 --- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs +++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs @@ -34,7 +34,6 @@ namespace FlaxEditor.GUI.Dialogs private Color _value; private bool _disableEvents; private bool _useDynamicEditing; - private bool _isClosing; private ColorValueBox.ColorPickerEvent _onChanged; private ColorValueBox.ColorPickerClosedEvent _onClosed; @@ -139,7 +138,7 @@ namespace FlaxEditor.GUI.Dialogs _cBlue.ValueChanged += OnRGBAChanged; // Alpha - _cAlpha = new FloatValueBox(0, _cRed.X, _cBlue.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f) + _cAlpha = new FloatValueBox(0, _cRed.X, _cBlue.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f) { Parent = this }; @@ -183,7 +182,7 @@ namespace FlaxEditor.GUI.Dialogs Text = "Cancel", Parent = this }; - _cCancel.Clicked += OnCancelClicked; + _cCancel.Clicked += OnCancel; // OK _cOK = new Button(_cCancel.Left - ButtonsWidth - PickerMargin, _cCancel.Y, ButtonsWidth) @@ -191,42 +190,12 @@ namespace FlaxEditor.GUI.Dialogs Text = "Ok", Parent = this }; - _cOK.Clicked += OnOkClicked; + _cOK.Clicked += OnSubmit; // Set initial color SelectedColor = initialValue; } - private void OnOkClicked() - { - if (_isClosing) - return; - _isClosing = true; - - // Send color event if modified - if (_value != _initialValue) - { - _onChanged?.Invoke(_value, false); - } - - Close(DialogResult.OK); - } - - private void OnCancelClicked() - { - if (_isClosing) - return; - _isClosing = true; - - // Restore color if modified - if (_useDynamicEditing && _initialValue != _value) - { - _onChanged?.Invoke(_initialValue, false); - } - - Close(DialogResult.Cancel); - } - private void OnRGBAChanged() { if (_disableEvents) @@ -300,11 +269,43 @@ namespace FlaxEditor.GUI.Dialogs protected override void OnShow() { // Auto cancel on lost focus - ((WindowRootControl)Root).Window.LostFocus += OnCancelClicked; + ((WindowRootControl)Root).Window.LostFocus += OnCancel; base.OnShow(); } + /// + public override void OnSubmit() + { + if (_disableEvents) + return; + _disableEvents = true; + + // Send color event if modified + if (_value != _initialValue) + { + _onChanged?.Invoke(_value, false); + } + + base.OnSubmit(); + } + + /// + public override void OnCancel() + { + if (_disableEvents) + return; + _disableEvents = true; + + // Restore color if modified + if (_useDynamicEditing && _initialValue != _value) + { + _onChanged?.Invoke(_initialValue, false); + } + + base.OnCancel(); + } + /// public override void OnDestroy() { @@ -316,7 +317,7 @@ namespace FlaxEditor.GUI.Dialogs /// public void ClosePicker() { - OnCancelClicked(); + OnCancel(); } } } diff --git a/Source/Editor/GUI/Dialogs/Dialog.cs b/Source/Editor/GUI/Dialogs/Dialog.cs index c33429609..72aa6edf5 100644 --- a/Source/Editor/GUI/Dialogs/Dialog.cs +++ b/Source/Editor/GUI/Dialogs/Dialog.cs @@ -220,6 +220,14 @@ namespace FlaxEditor.GUI.Dialogs } } + /// + /// Called to cancel action and close the dialog. + /// + public virtual void OnCancel() + { + Close(DialogResult.Cancel); + } + /// /// Closes dialog with the specified result. /// @@ -243,6 +251,7 @@ namespace FlaxEditor.GUI.Dialogs /// protected virtual void OnShow() { + Focus(); } /// @@ -254,5 +263,37 @@ namespace FlaxEditor.GUI.Dialogs { return true; } + + /// + public override void OnSubmit() + { + base.OnSubmit(); + + Close(DialogResult.OK); + } + + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + switch (key) + { + case KeyboardKeys.Return: + if (Root?.FocusedControl != null) + Root.SubmitFocused(); + else + OnSubmit(); + return true; + case KeyboardKeys.Escape: + OnCancel(); + return true; + case KeyboardKeys.Tab: + Root?.Navigate(NavDirection.Next); + return true; + } + return false; + } } } diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index f1ed39954..a8a408fca 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -78,8 +78,6 @@ namespace FlaxEditor.GUI.Input if (_value != value) { _value = value; - - // Fire event OnValueChanged(); } } @@ -128,16 +126,24 @@ namespace FlaxEditor.GUI.Input var r = new Rectangle(2, 2, Width - 4, Height - 4); Render2D.FillRectangle(r, _value); - Render2D.DrawRectangle(r, IsMouseOver ? style.BackgroundSelected : Color.Black); + Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); } /// public override bool OnMouseUp(Vector2 location, MouseButton button) { + Focus(); + OnSubmit(); + return true; + } + + /// + public override void OnSubmit() + { + base.OnSubmit(); + // Show color picker dialog _currentDialog = ShowPickColorDialog?.Invoke(this, _value, OnColorChanged, OnPickerClosed); - - return true; } private void OnColorChanged(Color color, bool sliding) diff --git a/Source/Editor/GUI/ToolStripButton.cs b/Source/Editor/GUI/ToolStripButton.cs index c34669404..edf7a4e8b 100644 --- a/Source/Editor/GUI/ToolStripButton.cs +++ b/Source/Editor/GUI/ToolStripButton.cs @@ -110,7 +110,7 @@ namespace FlaxEditor.GUI bool enabled = EnabledInHierarchy; // Draw background - if (enabled && (IsMouseOver || Checked)) + if (enabled && (IsMouseOver || IsNavFocused || Checked)) Render2D.FillRectangle(clientRect, Checked ? style.BackgroundSelected : _mouseDown ? style.BackgroundHighlighted : (style.LightBackground * 1.3f)); // Draw icon diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index 8755b940d..f9987270a 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -1103,6 +1103,15 @@ namespace FlaxEditor.GUI.Tree Height = Mathf.Max(_headerHeight, y); } + /// + protected override bool CanNavigateChild(Control child) + { + // Closed tree node skips navigation for hidden children + if (IsCollapsed && child is TreeNode) + return false; + return base.CanNavigateChild(child); + } + /// protected override void OnParentChangedInternal() { diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index a7eaaf826..5560afc3b 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -325,7 +325,7 @@ namespace FlaxEditor.Modules }); } } - + /// public override void OnUpdate() { diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 53567221e..d30c25f8d 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -527,7 +527,7 @@ namespace FlaxEditor.Modules Text = "OK", Parent = this, }; - okButton.Clicked += OnOk; + okButton.Clicked += OnSubmit; var cancelButton = new Button(okButton.Left - 54, okButton.Y, 50) { @@ -539,7 +539,8 @@ namespace FlaxEditor.Modules _dialogSize = okButton.BottomRight + new Vector2(8); } - private void OnOk() + /// + public override void OnSubmit() { var name = _textbox.Text; if (name.Length == 0) @@ -553,32 +554,11 @@ namespace FlaxEditor.Modules return; } - Close(DialogResult.OK); + base.OnSubmit(); var path = StringUtils.CombinePaths(Globals.ProjectCacheFolder, "Layout_" + name + ".xml"); Editor.Instance.Windows.SaveLayout(path); } - - private void OnCancel() - { - Close(DialogResult.Cancel); - } - - /// - public override bool OnKeyDown(KeyboardKeys key) - { - switch (key) - { - case KeyboardKeys.Escape: - OnCancel(); - return true; - case KeyboardKeys.Return: - OnOk(); - return true; - } - - return base.OnKeyDown(key); - } } /// diff --git a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs index fe62b474a..18def0b00 100644 --- a/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs +++ b/Source/Editor/Tools/Terrain/CreateTerrainDialog.cs @@ -117,7 +117,7 @@ namespace FlaxEditor.Tools.Terrain Offsets = new Margin(-ButtonsWidth - ButtonsMargin, ButtonsWidth, -ButtonsHeight - ButtonsMargin, ButtonsHeight), Parent = this }; - importButton.Clicked += OnCreate; + importButton.Clicked += OnSubmit; var cancelButton = new Button { Text = "Cancel", @@ -201,12 +201,22 @@ namespace FlaxEditor.Tools.Terrain _isDone = true; } - private void OnCancel() + /// + public override void OnSubmit() { if (_isWorking) return; - Close(DialogResult.Cancel); + base.OnSubmit(); + } + + /// + public override void OnCancel() + { + if (_isWorking) + return; + + base.OnCancel(); } /// diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index 4db8bc094..fae75c641 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -24,6 +24,11 @@ namespace FlaxEditor.Windows /// protected virtual bool CanOpenContentFinder => true; + /// + /// Gets a value indicating whether this window can use UI navigation (tab/enter). + /// + protected virtual bool CanUseNavigation => true; + /// /// Initializes a new instance of the class. /// @@ -184,6 +189,32 @@ namespace FlaxEditor.Windows #endregion + /// + public override bool OnKeyDown(KeyboardKeys key) + { + if (base.OnKeyDown(key)) + return true; + + switch (key) + { + case KeyboardKeys.Return: + if (CanUseNavigation && Root?.FocusedControl != null) + { + Root.SubmitFocused(); + return true; + } + break; + case KeyboardKeys.Tab: + if (CanUseNavigation && Root != null) + { + Root.Navigate(NavDirection.Next); + return true; + } + break; + } + return false; + } + /// public override void OnDestroy() { diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 0b3ee598d..53ab02786 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -323,12 +323,15 @@ namespace FlaxEditor.Windows /// protected override bool CanOpenContentFinder => false; + /// + protected override bool CanUseNavigation => false; + /// public override void OnPlayBegin() { _gameStartTime = Time.UnscaledGameTime; } - + /// public override void OnPlayEnd() { @@ -533,6 +536,22 @@ namespace FlaxEditor.Windows Screen.CursorVisible = true; } + /// + public override Control OnNavigate(NavDirection direction, Vector2 location, Control caller, System.Collections.Generic.List visited) + { + // Block leaking UI navigation focus outside the game window + if (IsFocused && caller != this) + { + // Pick the first UI control if game UI is not focused yet + foreach (var child in _guiRoot.Children) + { + if (child.Visible) + return child.OnNavigate(direction, Vector2.Zero, this, visited); + } + } + return null; + } + /// public override bool OnMouseDown(Vector2 location, MouseButton button) {