From af34c497c20f228f169ca2b10cd49002870385d6 Mon Sep 17 00:00:00 2001 From: Skye <22365940+Skyedra@users.noreply.github.com> Date: Sat, 8 Apr 2023 19:19:32 -0700 Subject: [PATCH] UI: Close window hotkeys (#15227) --- .../Options/UI/Tabs/KeyRebindTab.xaml.cs | 2 + .../CloseAllWindowsUIController.cs | 34 +++++ .../CloseRecentWindowUIController.cs | 122 ++++++++++++++++++ .../en-US/escape-menu/ui/options-menu.ftl | 4 +- Resources/keybinds.yml | 9 +- 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 Content.Client/UserInterface/Systems/CloseWindow/CloseAllWindowsUIController.cs create mode 100644 Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs index 68003612eb..28ec8d9392 100644 --- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs @@ -145,6 +145,8 @@ namespace Content.Client.Options.UI.Tabs AddButton(ContentKeyFunctions.OpenTileSpawnWindow); AddButton(ContentKeyFunctions.OpenDecalSpawnWindow); AddButton(ContentKeyFunctions.OpenAdminMenu); + AddButton(EngineKeyFunctions.WindowCloseAll); + AddButton(EngineKeyFunctions.WindowCloseRecent); AddHeader("ui-options-header-misc"); AddButton(ContentKeyFunctions.TakeScreenshot); diff --git a/Content.Client/UserInterface/Systems/CloseWindow/CloseAllWindowsUIController.cs b/Content.Client/UserInterface/Systems/CloseWindow/CloseAllWindowsUIController.cs new file mode 100644 index 0000000000..0ec9b53e65 --- /dev/null +++ b/Content.Client/UserInterface/Systems/CloseWindow/CloseAllWindowsUIController.cs @@ -0,0 +1,34 @@ +using Content.Client.Gameplay; +using Content.Client.Info; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controllers; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Input; +using Robust.Shared.Input.Binding; + +namespace Content.Client.UserInterface.Systems.Info; + +public sealed class CloseAllWindowsUIController : UIController +{ + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IUserInterfaceManager _uiManager = default!; + + public override void Initialize() + { + _inputManager.SetInputCommand(EngineKeyFunctions.WindowCloseAll, + InputCmdHandler.FromDelegate(session => CloseAllWindows())); + } + + private void CloseAllWindows() + { + foreach (var childControl in new List(_uiManager.WindowRoot.Children)) // Copy children list as it will be modified on Close() + { + if (childControl is BaseWindow) + { + ((BaseWindow) childControl).Close(); + } + } + } +} + diff --git a/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs b/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs new file mode 100644 index 0000000000..18f20727e1 --- /dev/null +++ b/Content.Client/UserInterface/Systems/CloseWindow/CloseRecentWindowUIController.cs @@ -0,0 +1,122 @@ +using Content.Client.Gameplay; +using Content.Client.Info; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controllers; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Input; +using Robust.Shared.Input.Binding; + +namespace Content.Client.UserInterface.Systems.Info; + +public sealed class CloseRecentWindowUIController : UIController +{ + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IUserInterfaceManager _uiManager = default!; + + /// + /// A list of windows that have been interacted with recently. Windows should only + /// be in this list once, with the most recent window at the end, and the oldest + /// window at the start. + /// + List recentlyInteractedWindows = new List(); + + public override void Initialize() + { + // Add listeners to be able to know when windows are opened. + // (Does not need to be unlistened since UIControllers live forever) + _uiManager.OnKeyBindDown += OnKeyBindDown; + _uiManager.WindowRoot.OnChildAdded += OnRootChildAdded; + + _inputManager.SetInputCommand(EngineKeyFunctions.WindowCloseRecent, + InputCmdHandler.FromDelegate(session => CloseMostRecentWindow())); + } + + private void CloseMostRecentWindow() + { + // Search backwards through the recency list to find a still open window and close it + for (int i=recentlyInteractedWindows.Count-1; i>=0; i--) + { + var window = recentlyInteractedWindows[i]; + recentlyInteractedWindows.RemoveAt(i); // Should always be removed as either the reference is stale or we're closing it + if (window.IsOpen) + { + window.Close(); + return; + } + // continue going down the list, hoping to find a still-open window + } + } + + private void OnKeyBindDown(Control control) + { + // On click, we should set the window that owns this control (if any) to the most recently + // clicked window. By doing this, we can create an ordering of what windows have been + // interacted with. + + // Something was clicked, so find the window corresponding to what was clicked + var window = GetWindowForControl(control); + + // Find the window owning the control + if (window != null) + { + // And move to top of recent stack + //Logger.Debug("Most recent window is " + window.Name); + SetMostRecentlyInteractedWindow(window); + } + } + + /// + /// Sets the window as the one most recently interacted with. This function will update the + /// internal recentlyInteractedWindows tracking. + /// + /// + private void SetMostRecentlyInteractedWindow(BaseWindow window) + { + // Search through the list and see if already added. + // (This search is backwards since it's fairly common that the user is clicking the same + // window multiple times in a row, and so that saves a tiny bit of perf doing it this way) + for (int i=recentlyInteractedWindows.Count-1; i>=0; i--) + { + if (recentlyInteractedWindows[i] == window) + { + // Window already in the list + + // Is window the top most recent entry? + if (i == recentlyInteractedWindows.Count-1) + return; // Then there's nothing to do, it's already in the right spot + else + { + // Need to remove the old entry so it can be readded (no duplicates in list allowed) + recentlyInteractedWindows.RemoveAt(i); + break; + } + } + } + + // Now that the list has been checked for duplicates, okay to add new window at end of tracking + recentlyInteractedWindows.Add(window); + } + + private BaseWindow? GetWindowForControl(Control? control) + { + if (control == null) + return null; + + if (control is BaseWindow) + return (BaseWindow) control; + + // Go up the hierarchy until we find a window (or don't) + return GetWindowForControl(control.Parent); + } + + private void OnRootChildAdded(Control control) + { + if (control is BaseWindow) + { + // On new window open, add to tracking + SetMostRecentlyInteractedWindow((BaseWindow) control); + } + } +} + diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index 58cf96f942..86e4b61336 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -135,6 +135,8 @@ ui-options-function-open-tile-spawn-window = Open tile spawn menu ui-options-function-open-decal-spawn-window = Open decal spawn menu ui-options-function-open-admin-menu = Open admin menu ui-options-function-open-guidebook = Open guidebook +ui-options-function-window-close-all = Close all windows +ui-options-function-window-close-recent = Close recent window ui-options-function-take-screenshot = Take screenshot ui-options-function-take-screenshot-no-ui = Take screenshot (without UI) @@ -214,4 +216,4 @@ ui-options-net-pvs-leave-tooltip = This limits the rate at which the client will ## Toggle window console command cmd-options-desc = Opens options menu, optionally with a specific tab selected. -cmd-options-help = Usage: options [tab] \ No newline at end of file +cmd-options-help = Usage: options [tab] diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 8fa3d13de6..b76d621166 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -79,7 +79,7 @@ binds: # Misc - function: ShowEscapeMenu type: State - key: Escape + key: F12 - function: CycleChatChannelForward type: State key: Tab @@ -404,6 +404,13 @@ binds: type: State key: Tab mod1: Shift +- function: WindowCloseRecent + type: State + key: Escape +- function: WindowCloseAll + type: State + key: Escape + mod1: Shift - function: Point type: State key: MouseMiddle