From 623435422bdd1feeb1141cac74355917128041d5 Mon Sep 17 00:00:00 2001 From: EmoSaru Date: Mon, 13 Apr 2026 01:32:02 -0700 Subject: [PATCH] Fix capture grid popup dismissed by map light-dismiss handler (#42) The tunneling PointerPressed handler that closes the location details popup on outside clicks was also closing it when clicking inside child popups (e.g. the capture grid). Popup content renders on an overlay layer with a disconnected visual tree, so the walk from e.Source never reached LocationDetailsContent for nested popups. Fix: detect clicks inside popup overlays by checking whether the visual root of e.Source is the TopLevel window. If not, the click is inside a popup overlay and should not dismiss the location details. Co-Authored-By: Claude Opus 4.6 --- EmoTracker/UI/LocationMapControl.axaml.cs | 29 ++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/EmoTracker/UI/LocationMapControl.axaml.cs b/EmoTracker/UI/LocationMapControl.axaml.cs index bdd417e..25eadff 100644 --- a/EmoTracker/UI/LocationMapControl.axaml.cs +++ b/EmoTracker/UI/LocationMapControl.axaml.cs @@ -184,22 +184,39 @@ private void TopLevel_PointerPressedTunnel(object? sender, PointerPressedEventAr if (!LocationDetails.IsOpen) return; - // Check whether the press landed inside the popup content. - // The popup's child renders on the overlay layer, outside the normal visual tree, - // so we walk up from e.Source looking for our LocationDetailsContent control. + // Check whether the press landed inside the location details popup or any + // child popup (e.g. the capture grid). Popup content renders on an overlay + // layer whose visual tree is disconnected from the main window tree. Walking + // up from e.Source will reach LocationDetailsContent if the click is inside + // the location details popup itself. For child popups (capture grid), the + // walk terminates at the overlay host without reaching LocationDetailsContent + // or the TopLevel — so we treat any click whose visual root is NOT the + // TopLevel as "inside a popup" and leave it alone. var source = e.Source as Visual; - bool insidePopup = false; + var topLevel = sender as Visual; + bool insidePopupOrOverlay = false; + Visual? root = null; + while (source != null) { if (source == LocationDetailsContent) { - insidePopup = true; + insidePopupOrOverlay = true; break; } + root = source; source = source.GetVisualParent(); } - if (!insidePopup) + // If the walk ended without finding LocationDetailsContent, check whether + // we're inside a popup overlay. Popup overlays have a visual root that is + // NOT the TopLevel window — it's an OverlayPopupHost or PopupRoot. If the + // root IS the TopLevel, the click landed on the main window content and we + // should dismiss. + if (!insidePopupOrOverlay && root != null && root != topLevel) + insidePopupOrOverlay = true; + + if (!insidePopupOrOverlay) { LocationDetails.IsOpen = false; }