Explorar el Código

Fix SplitView incorrectly closing when the user changes DisplayMode to inline when IsPaneOpen is already true (#19460)

* add a failing unit test for issue #19457

* fix #19457 by invalidating the pointerReleased subscription when DisplayMode changes.

* [SplitView] ensure we always set _pointerDisposable to null when disposing.
Dan Walmsley hace 2 meses
padre
commit
5ab26da0fd

+ 21 - 11
src/Avalonia.Controls/SplitView/SplitView.cs

@@ -314,6 +314,7 @@ namespace Avalonia.Controls
         {
             base.OnDetachedFromVisualTree(e);
             _pointerDisposable?.Dispose();
+            _pointerDisposable = null;
         }
 
         /// <inheritdoc/>
@@ -423,7 +424,7 @@ namespace Avalonia.Controls
 
         protected virtual void OnPaneOpened(RoutedEventArgs args)
         {
-            EnableLightDismiss();
+            InvalidateLightDismissSubscription();
             RaiseEvent(args);
         }
 
@@ -528,6 +529,8 @@ namespace Avalonia.Controls
             };
             TemplateSettings.ClosedPaneWidth = closedPaneWidth;
             TemplateSettings.PaneColumnGridLength = paneColumnGridLength;
+            
+            InvalidateLightDismissSubscription();
         }
 
         private void UpdateVisualStateForPanePlacementProperty(SplitViewPanePlacement newValue)
@@ -541,7 +544,7 @@ namespace Avalonia.Controls
             PseudoClasses.Add(_lastPlacementPseudoclass);
         }
 
-        private void EnableLightDismiss()
+        private void InvalidateLightDismissSubscription()
         {
             if (_pane == null)
                 return;
@@ -549,19 +552,26 @@ namespace Avalonia.Controls
             // If this returns false, we're not in Overlay or CompactOverlay DisplayMode
             // and don't need the light dismiss behavior
             if (!IsInOverlayMode())
+            {
+                _pointerDisposable?.Dispose();
+                _pointerDisposable = null;
                 return;
+            }
 
-            var topLevel = TopLevel.GetTopLevel(this);
-            if (topLevel != null)
+            if (_pointerDisposable == null)
             {
-                _pointerDisposable = Disposable.Create(() =>
+                var topLevel = TopLevel.GetTopLevel(this);
+                if (topLevel != null)
                 {
-                    topLevel.PointerReleased -= PointerReleasedOutside;
-                    topLevel.BackRequested -= TopLevelBackRequested;
-                });
-
-                topLevel.PointerReleased += PointerReleasedOutside;
-                topLevel.BackRequested += TopLevelBackRequested;
+                    _pointerDisposable = Disposable.Create(() =>
+                    {
+                        topLevel.PointerReleased -= PointerReleasedOutside;
+                        topLevel.BackRequested -= TopLevelBackRequested;
+                    });
+
+                    topLevel.PointerReleased += PointerReleasedOutside;
+                    topLevel.BackRequested += TopLevelBackRequested;
+                }
             }
         }
 

+ 40 - 0
tests/Avalonia.Controls.UnitTests/SplitViewTests.cs

@@ -303,5 +303,45 @@ namespace Avalonia.Controls.UnitTests
 
             Assert.Contains(splitView.Classes, ":closed".Equals);
         }
+        
+        [Fact]
+        public void SplitView_Shouldnt_Close_Panel_When_IsPaneOpen_True_Then_Display_Mode_Changed()
+        {
+            using var app = UnitTestApplication.Start(TestServices.StyledWindow
+                .With(globalClock: new MockGlobalClock()));
+            var wnd = new Window
+            {
+                Width = 1280,
+                Height = 720
+            };
+            var splitView = new SplitView();
+            splitView.DisplayMode = SplitViewDisplayMode.CompactOverlay;
+            wnd.Content = splitView;
+            wnd.Show();
+
+            splitView.IsPaneOpen = true;
+
+            splitView.RaiseEvent(new PointerReleasedEventArgs(splitView,
+                null, wnd, new Point(1270, 30), 0,
+                new PointerPointProperties(),
+                KeyModifiers.None,
+                MouseButton.Left));
+
+            Assert.False(splitView.IsPaneOpen);
+
+            // Inline shouldn't close the pane
+            splitView.IsPaneOpen = true;
+            
+            // Change the display mode once the pane is already open.
+            splitView.DisplayMode = SplitViewDisplayMode.Inline;
+
+            splitView.RaiseEvent(new PointerReleasedEventArgs(splitView,
+                null, wnd, new Point(1270, 30), 0,
+                new PointerPointProperties(),
+                KeyModifiers.None,
+                MouseButton.Left));
+
+            Assert.True(splitView.IsPaneOpen);
+        }
     }
 }