From 54c6b52069668dfe3170dd36f6fa7fddcb99e005 Mon Sep 17 00:00:00 2001 From: chinboy Date: Fri, 7 Aug 2026 09:16:23 +0800 Subject: [PATCH] fix(NavMenu): ignore stale selected nodes after item replacement --- .../NavMenu/NavMenuSelectionCoordinator.cs | 24 ++++++++- .../NavMenu/NavMenuSelectionTests.cs | 54 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/AtomUI.Desktop.Controls/NavMenu/NavMenuSelectionCoordinator.cs b/src/AtomUI.Desktop.Controls/NavMenu/NavMenuSelectionCoordinator.cs index d478911dc..82d67b016 100644 --- a/src/AtomUI.Desktop.Controls/NavMenu/NavMenuSelectionCoordinator.cs +++ b/src/AtomUI.Desktop.Controls/NavMenu/NavMenuSelectionCoordinator.cs @@ -118,7 +118,29 @@ internal sealed class NavMenuSelectionCoordinator } var selectedNode = _appliedSelectedNode ?? menu.SelectedItem; - return selectedNode is null ? null : menu.FindRealizedMenuItem(selectedNode); + if (selectedNode is null) + { + return null; + } + + if (!BelongsToMenu(menu, selectedNode)) + { + Reset(); + return null; + } + + return menu.FindRealizedMenuItem(selectedNode); + } + + private static bool BelongsToMenu(NavMenu menu, INavMenuNode node) + { + var rootNode = node; + while (rootNode.ParentNode is INavMenuNode parentNode) + { + rootNode = parentNode; + } + + return NavMenuEntryGraph.ContainsDirectNode(menu.Items, rootNode); } private void Reset() diff --git a/tests/AtomUI.Desktop.Controls.Tests/NavMenu/NavMenuSelectionTests.cs b/tests/AtomUI.Desktop.Controls.Tests/NavMenu/NavMenuSelectionTests.cs index e4cfd7382..4c47bd5d8 100644 --- a/tests/AtomUI.Desktop.Controls.Tests/NavMenu/NavMenuSelectionTests.cs +++ b/tests/AtomUI.Desktop.Controls.Tests/NavMenu/NavMenuSelectionTests.cs @@ -302,6 +302,60 @@ public class NavMenuSelectionTests } } + [Fact] + public void NavMenu_Selects_New_Item_After_Replacing_The_Selected_Item_Tree() + { + var first = new NavMenuNode + { + Header = "First", + ItemKey = "first" + }; + var replacement = new NavMenuNode + { + Header = "Replacement", + ItemKey = "replacement" + }; + var menu = new AtomUI.Desktop.Controls.NavMenu + { + Mode = NavMenuMode.Inline, + IsMotionEnabled = false + }; + menu.Items.Add(first); + + var window = new Avalonia.Controls.Window + { + Width = 320, + Height = 240, + Content = menu + }; + + try + { + window.Show(); + Dispatcher.UIThread.RunJobs(); + + var firstContainer = menu.ContainerFromItem(first).ShouldBeOfType(); + menu.InteractionHandler.ShouldNotBeNull(); + menu.InteractionHandler.Select(firstContainer); + + menu.Items.Clear(); + menu.Items.Add(replacement); + Dispatcher.UIThread.RunJobs(); + + var replacementContainer = menu.ContainerFromItem(replacement).ShouldBeOfType(); + var replacementHeader = GetItemHeader(replacementContainer); + Should.NotThrow(() => MouseDown(replacementHeader, window)); + Dispatcher.UIThread.RunJobs(); + + replacementContainer.IsSelected.ShouldBeTrue(); + menu.SelectedItem.ShouldBeSameAs(replacement); + } + finally + { + window.Close(); + } + } + [Fact] public void NavMenu_Detach_Then_Programmatic_Sibling_Selection_Clears_Previous_Selection() {