diff --git a/Content.Client/ContextMenu/UI/ContextMenuPopup.xaml.cs b/Content.Client/ContextMenu/UI/ContextMenuPopup.xaml.cs index 7376278aa4..6eab81ded2 100644 --- a/Content.Client/ContextMenu/UI/ContextMenuPopup.xaml.cs +++ b/Content.Client/ContextMenu/UI/ContextMenuPopup.xaml.cs @@ -58,6 +58,14 @@ namespace Content.Client.ContextMenu.UI DebugTools.Assert(ParentElement.SubMenu == null); ParentElement.SubMenu = this; } + + // ensure the menu-stack is properly updated when a pop-up looses focus or otherwise closes without going + // through the menu presenter. + OnPopupHide += () => + { + if (ParentElement != null) + _presenter.CloseSubMenus(ParentElement.ParentMenu); + }; } protected override void Dispose(bool disposing) diff --git a/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs b/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs index 1ad64a4f8c..62e7251861 100644 --- a/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs +++ b/Content.Client/ContextMenu/UI/ContextMenuPresenter.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using Robust.Client.UserInterface; +using Robust.Shared.Log; using Robust.Shared.Maths; using Timer = Robust.Shared.Timing.Timer; namespace Content.Client.ContextMenu.UI @@ -71,6 +72,10 @@ namespace Content.Client.ContextMenu.UI { Menus.Pop().Close(); } + + // ensure no accidental double-closing happens. + CancelClose?.Cancel(); + CancelClose = null; } /// @@ -78,7 +83,11 @@ namespace Content.Client.ContextMenu.UI /// public virtual void OnMouseEntered(ContextMenuElement element) { - var topMenu = Menus.Peek(); + if (!Menus.TryPeek(out var topMenu)) + { + Logger.Error("Context Menu: Mouse entered menu without any open menus?"); + return; + } if (element.ParentMenu == topMenu || element.SubMenu == topMenu) CancelClose?.Cancel(); @@ -120,8 +129,14 @@ namespace Content.Client.ContextMenu.UI /// public virtual void OpenSubMenu(ContextMenuElement element) { + if (!Menus.TryPeek(out var topMenu)) + { + Logger.Error("Context Menu: Attempting to open sub menu without any open menus?"); + return; + } + // If This is already the top most menu, do nothing. - if (element.SubMenu == Menus.Peek()) + if (element.SubMenu == topMenu) return; // Was the parent menu closed or disposed before an open timer completed? @@ -131,6 +146,10 @@ namespace Content.Client.ContextMenu.UI // Close any currently open sub-menus up to this element's parent menu. CloseSubMenus(element.ParentMenu); + // cancel any queued openings to prevent weird double-open scenarios. + CancelOpen?.Cancel(); + CancelOpen = null; + if (element.SubMenu == null) return; @@ -139,8 +158,6 @@ namespace Content.Client.ContextMenu.UI var altPos = element.GlobalPosition; var pos = altPos + (element.Width + 2*ContextMenuElement.ElementMargin, - 2*ContextMenuElement.ElementMargin); element.SubMenu.Open(UIBox2.FromDimensions(pos, (1, 1)), altPos); - element.SubMenu.Close(); - element.SubMenu.Open(UIBox2.FromDimensions(pos, (1, 1)), altPos); // draw on top of other menus element.SubMenu.SetPositionLast(); diff --git a/Content.Client/Verbs/UI/VerbMenuPresenter.cs b/Content.Client/Verbs/UI/VerbMenuPresenter.cs index b857e1b1bf..9410720719 100644 --- a/Content.Client/Verbs/UI/VerbMenuPresenter.cs +++ b/Content.Client/Verbs/UI/VerbMenuPresenter.cs @@ -172,6 +172,7 @@ namespace Content.Client.Verbs.UI if (element is not VerbMenuElement verbElement) return; + args.Handle(); var verb = verbElement.Verb; if (verb == null) @@ -182,8 +183,12 @@ namespace Content.Client.Verbs.UI if (verbElement.SubMenu == null || verbElement.SubMenu.ChildCount == 0) return; - if (verbElement.SubMenu.MenuBody.Children.First() is not VerbMenuElement verbCategoryElement) + if (verbElement.SubMenu.MenuBody.ChildCount != 1 + || verbElement.SubMenu.MenuBody.Children.First() is not VerbMenuElement verbCategoryElement) + { + OpenSubMenu(verbElement); return; + } verb = verbCategoryElement.Verb; @@ -194,8 +199,6 @@ namespace Content.Client.Verbs.UI _verbSystem.ExecuteVerb(CurrentTarget, verb, verbElement.Type); if (verb.CloseMenu) _verbSystem.CloseAllMenus(); - - args.Handle(); } } }