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())); } /// /// Closes the most recently focused window. /// public 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. /// /// public 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); } } /// /// Checks whether there are any windows that can be closed. /// /// public bool HasClosableWindow() { for (var i = recentlyInteractedWindows.Count - 1; i >= 0; i--) { var window = recentlyInteractedWindows[i]; if (window.IsOpen) return true; // continue going down the list, hoping to find a still-open window } return false; } }