From ea5623df7e645936ff94fdd29543c259fb8e1142 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 15:27:26 -0400
Subject: [PATCH 1/9] Fix initial state/overlay & make TemplateSettings a
readonly DirectProperty
---
src/Avalonia.Controls/SplitView/SplitView.cs | 124 ++++++++++++-------
1 file changed, 76 insertions(+), 48 deletions(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 1099a40f08..a5c87f5843 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -94,8 +94,9 @@ namespace Avalonia.Controls
///
/// Defines the property
///
- public static readonly StyledProperty TemplateSettingsProperty =
- AvaloniaProperty.Register(nameof(TemplateSettings));
+ public static readonly DirectProperty TemplateSettingsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(TemplateSettings),
+ x => x.TemplateSettings);
///
/// Defines the event.
@@ -131,12 +132,12 @@ namespace Avalonia.Controls
private Panel? _pane;
private IDisposable? _pointerDisposable;
+ private SplitViewTemplateSettings _templateSettings;
+ private string _lastDisplayModePseudoclass;
+ private string _lastPlacementPseudoclass;
public SplitView()
{
- PseudoClasses.Add(":overlay");
- PseudoClasses.Add(":left");
-
TemplateSettings = new SplitViewTemplateSettings();
}
@@ -208,7 +209,7 @@ namespace Avalonia.Controls
get => GetValue(PaneProperty);
set => SetValue(PaneProperty, value);
}
-
+
///
/// Gets or sets the data template used to display the header content of the control.
///
@@ -235,8 +236,8 @@ namespace Avalonia.Controls
///
public SplitViewTemplateSettings TemplateSettings
{
- get => GetValue(TemplateSettingsProperty);
- set => SetValue(TemplateSettingsProperty, value);
+ get => _templateSettings;
+ private set => SetAndRaise(TemplateSettingsProperty, ref _templateSettings, value);
}
///
@@ -291,7 +292,7 @@ namespace Avalonia.Controls
{
return true;
}
-
+
return result;
}
@@ -299,12 +300,20 @@ namespace Avalonia.Controls
{
base.OnApplyTemplate(e);
_pane = e.NameScope.Find("PART_PaneRoot");
+
+ UpdateVisualStateForCompactPaneLength(CompactPaneLength);
+ UpdateVisualStateForDisplayMode(DisplayMode);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
+ // :left and :right style triggers contain the template so we need to do this as
+ // soon as we're attached so the template applies. The other visual states can
+ // be updated after the template applies
+ UpdateVisualStateForPanePlacementProperty(PanePlacement);
+
var topLevel = this.VisualRoot;
if (topLevel is Window window)
{
@@ -325,36 +334,11 @@ namespace Avalonia.Controls
if (change.Property == CompactPaneLengthProperty)
{
- var newLen = change.GetNewValue();
- var displayMode = DisplayMode;
- if (displayMode == SplitViewDisplayMode.CompactInline)
- {
- TemplateSettings.ClosedPaneWidth = newLen;
- }
- else if (displayMode == SplitViewDisplayMode.CompactOverlay)
- {
- TemplateSettings.ClosedPaneWidth = newLen;
- TemplateSettings.PaneColumnGridLength = new GridLength(newLen, GridUnitType.Pixel);
- }
+ UpdateVisualStateForCompactPaneLength(change.GetNewValue());
}
else if (change.Property == DisplayModeProperty)
{
- var oldState = GetPseudoClass(change.GetOldValue());
- var newState = GetPseudoClass(change.GetNewValue());
-
- PseudoClasses.Remove($":{oldState}");
- PseudoClasses.Add($":{newState}");
-
- var (closedPaneWidth, paneColumnGridLength) = change.GetNewValue() switch
- {
- SplitViewDisplayMode.Overlay => (0, new GridLength(0, GridUnitType.Pixel)),
- SplitViewDisplayMode.CompactOverlay => (CompactPaneLength, new GridLength(CompactPaneLength, GridUnitType.Pixel)),
- SplitViewDisplayMode.Inline => (0, new GridLength(0, GridUnitType.Auto)),
- SplitViewDisplayMode.CompactInline => (CompactPaneLength, new GridLength(0, GridUnitType.Auto)),
- _ => throw new NotImplementedException(),
- };
- TemplateSettings.ClosedPaneWidth = closedPaneWidth;
- TemplateSettings.PaneColumnGridLength = paneColumnGridLength;
+ UpdateVisualStateForDisplayMode(change.GetNewValue());
}
else if (change.Property == IsPaneOpenProperty)
{
@@ -389,10 +373,7 @@ namespace Avalonia.Controls
}
else if (change.Property == PanePlacementProperty)
{
- var oldState = GetPseudoClass(change.GetOldValue());
- var newState = GetPseudoClass(change.GetNewValue());
- PseudoClasses.Remove($":{oldState}");
- PseudoClasses.Add($":{newState}");
+ UpdateVisualStateForPanePlacementProperty(change.GetNewValue());
}
else if (change.Property == UseLightDismissOverlayModeProperty)
{
@@ -438,7 +419,7 @@ namespace Avalonia.Controls
e.Handled = true;
}
}
-
+
private bool ShouldClosePane()
{
return (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay);
@@ -471,14 +452,14 @@ namespace Avalonia.Controls
{
return mode switch
{
- SplitViewDisplayMode.Inline => "inline",
- SplitViewDisplayMode.CompactInline => "compactinline",
- SplitViewDisplayMode.Overlay => "overlay",
- SplitViewDisplayMode.CompactOverlay => "compactoverlay",
+ SplitViewDisplayMode.Inline => ":inline",
+ SplitViewDisplayMode.CompactInline => ":compactinline",
+ SplitViewDisplayMode.Overlay => ":overlay",
+ SplitViewDisplayMode.CompactOverlay => ":compactoverlay",
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
};
}
-
+
///
/// Gets the appropriate PseudoClass for the given .
///
@@ -486,8 +467,8 @@ namespace Avalonia.Controls
{
return placement switch
{
- SplitViewPanePlacement.Left => "left",
- SplitViewPanePlacement.Right => "right",
+ SplitViewPanePlacement.Left => ":left",
+ SplitViewPanePlacement.Right => ":right",
_ => throw new ArgumentOutOfRangeException(nameof(placement), placement, null)
};
}
@@ -519,6 +500,53 @@ namespace Avalonia.Controls
return value;
}
+ private void UpdateVisualStateForCompactPaneLength(double newLen)
+ {
+ var displayMode = DisplayMode;
+ if (displayMode == SplitViewDisplayMode.CompactInline)
+ {
+ TemplateSettings.ClosedPaneWidth = newLen;
+ }
+ else if (displayMode == SplitViewDisplayMode.CompactOverlay)
+ {
+ TemplateSettings.ClosedPaneWidth = newLen;
+ TemplateSettings.PaneColumnGridLength = new GridLength(newLen, GridUnitType.Pixel);
+ }
+ }
+
+ private void UpdateVisualStateForDisplayMode(SplitViewDisplayMode newValue)
+ {
+ if (!string.IsNullOrEmpty(_lastDisplayModePseudoclass))
+ {
+ PseudoClasses.Remove(_lastDisplayModePseudoclass);
+ }
+
+ _lastDisplayModePseudoclass = GetPseudoClass(newValue);
+ PseudoClasses.Add(_lastDisplayModePseudoclass);
+
+ var (closedPaneWidth, paneColumnGridLength) = newValue switch
+ {
+ SplitViewDisplayMode.Overlay => (0, new GridLength(0, GridUnitType.Pixel)),
+ SplitViewDisplayMode.CompactOverlay => (CompactPaneLength, new GridLength(CompactPaneLength, GridUnitType.Pixel)),
+ SplitViewDisplayMode.Inline => (0, new GridLength(0, GridUnitType.Auto)),
+ SplitViewDisplayMode.CompactInline => (CompactPaneLength, new GridLength(0, GridUnitType.Auto)),
+ _ => throw new NotImplementedException(),
+ };
+ TemplateSettings.ClosedPaneWidth = closedPaneWidth;
+ TemplateSettings.PaneColumnGridLength = paneColumnGridLength;
+ }
+
+ private void UpdateVisualStateForPanePlacementProperty(SplitViewPanePlacement newValue)
+ {
+ if (!string.IsNullOrEmpty(_lastPlacementPseudoclass))
+ {
+ PseudoClasses.Remove(_lastPlacementPseudoclass);
+ }
+
+ _lastPlacementPseudoclass = GetPseudoClass(newValue);
+ PseudoClasses.Add(_lastPlacementPseudoclass);
+ }
+
///
/// Coerces/validates the property value.
///
From 878e18509e36f53f09256b1796b2f1cad6f02b52 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 15:33:03 -0400
Subject: [PATCH 2/9] Use const strings for pseudoclasses
---
src/Avalonia.Controls/SplitView/SplitView.cs | 45 +++++++++++---------
1 file changed, 24 insertions(+), 21 deletions(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index a5c87f5843..786a26ae68 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -15,18 +15,21 @@ namespace Avalonia.Controls
/// A control with two views: A collapsible pane and an area for content
///
[TemplatePart("PART_PaneRoot", typeof(Panel))]
- [PseudoClasses(":open", ":closed")]
- [PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")]
- [PseudoClasses(":left", ":right")]
- [PseudoClasses(":lightdismiss")]
+ [PseudoClasses(pcOpen, pcClosed)]
+ [PseudoClasses(pcCompactOverlay, pcCompactInline, pcOverlay, pcInline)]
+ [PseudoClasses(pcLeft, pcRight)]
+ [PseudoClasses(pcLightDismiss)]
public class SplitView : ContentControl
{
- /*
- Pseudo classes & combos
- :open / :closed
- :compactoverlay :compactinline :overlay :inline
- :left :right
- */
+ protected const string pcOpen = ":open";
+ protected const string pcClosed = ":closed";
+ protected const string pcCompactOverlay = ":compactoverlay";
+ protected const string pcCompactInline = ":compactInline";
+ protected const string pcOverlay = ":overlay";
+ protected const string pcInline = ":inline";
+ protected const string pcLeft = ":left";
+ protected const string pcRight = ":right";
+ protected const string pcLightDismiss = ":lightDismiss";
///
/// Defines the property
@@ -346,15 +349,15 @@ namespace Avalonia.Controls
if (isPaneOpen)
{
- PseudoClasses.Add(":open");
- PseudoClasses.Remove(":closed");
+ PseudoClasses.Add(pcOpen);
+ PseudoClasses.Remove(pcClosed);
OnPaneOpened(new RoutedEventArgs(PaneOpenedEvent, this));
}
else
{
- PseudoClasses.Add(":closed");
- PseudoClasses.Remove(":open");
+ PseudoClasses.Add(pcClosed);
+ PseudoClasses.Remove(pcOpen);
OnPaneClosed(new RoutedEventArgs(PaneClosedEvent, this));
}
@@ -378,7 +381,7 @@ namespace Avalonia.Controls
else if (change.Property == UseLightDismissOverlayModeProperty)
{
var mode = change.GetNewValue();
- PseudoClasses.Set(":lightdismiss", mode);
+ PseudoClasses.Set(pcLightDismiss, mode);
}
}
@@ -452,10 +455,10 @@ namespace Avalonia.Controls
{
return mode switch
{
- SplitViewDisplayMode.Inline => ":inline",
- SplitViewDisplayMode.CompactInline => ":compactinline",
- SplitViewDisplayMode.Overlay => ":overlay",
- SplitViewDisplayMode.CompactOverlay => ":compactoverlay",
+ SplitViewDisplayMode.Inline => pcInline,
+ SplitViewDisplayMode.CompactInline => pcCompactInline,
+ SplitViewDisplayMode.Overlay => pcOverlay,
+ SplitViewDisplayMode.CompactOverlay => pcCompactOverlay,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
};
}
@@ -467,8 +470,8 @@ namespace Avalonia.Controls
{
return placement switch
{
- SplitViewPanePlacement.Left => ":left",
- SplitViewPanePlacement.Right => ":right",
+ SplitViewPanePlacement.Left => pcLeft,
+ SplitViewPanePlacement.Right => pcRight,
_ => throw new ArgumentOutOfRangeException(nameof(placement), placement, null)
};
}
From 25825e4eabc8e7c1525b89511de94ab0eba5e561 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 16:57:55 -0400
Subject: [PATCH 3/9] Fix typo in pseudoclass
---
src/Avalonia.Controls/SplitView/SplitView.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 786a26ae68..9fd316a9f7 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls
protected const string pcOpen = ":open";
protected const string pcClosed = ":closed";
protected const string pcCompactOverlay = ":compactoverlay";
- protected const string pcCompactInline = ":compactInline";
+ protected const string pcCompactInline = ":compactinline";
protected const string pcOverlay = ":overlay";
protected const string pcInline = ":inline";
protected const string pcLeft = ":left";
From 87a2bb6bda76d5370bae07fd62951971c4d2f70e Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 16:58:20 -0400
Subject: [PATCH 4/9] Improve light dismiss behavior
---
src/Avalonia.Controls/SplitView/SplitView.cs | 70 +++++++++++++++-----
1 file changed, 52 insertions(+), 18 deletions(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 9fd316a9f7..b8a697e309 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -8,6 +8,7 @@ using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
+using Avalonia.Reactive;
namespace Avalonia.Controls
{
@@ -144,10 +145,6 @@ namespace Avalonia.Controls
TemplateSettings = new SplitViewTemplateSettings();
}
- static SplitView()
- {
- }
-
///
/// Gets or sets the length of the pane when in
/// or mode
@@ -316,12 +313,6 @@ namespace Avalonia.Controls
// soon as we're attached so the template applies. The other visual states can
// be updated after the template applies
UpdateVisualStateForPanePlacementProperty(PanePlacement);
-
- var topLevel = this.VisualRoot;
- if (topLevel is Window window)
- {
- _pointerDisposable = window.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
- }
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
@@ -385,21 +376,27 @@ namespace Avalonia.Controls
}
}
- private void PointerPressedOutside(object? sender, PointerPressedEventArgs e)
+ protected override void OnKeyDown(KeyEventArgs e)
{
- if (!IsPaneOpen)
+ if (!e.Handled && e.Key == Key.Escape)
{
- return;
+ if (IsPaneOpen && IsInOverlayMode())
+ {
+ SetCurrentValue(IsPaneOpenProperty, false);
+ }
}
- //If we click within the Pane, don't do anything
- //Otherwise, ClosePane if open & using an overlay display mode
- bool closePane = ShouldClosePane();
- if (!closePane)
+ base.OnKeyDown(e);
+ }
+
+ private void PointerReleasedOutside(object? sender, PointerReleasedEventArgs e)
+ {
+ if (!IsPaneOpen || _pane == null)
{
return;
}
+ var closePane = true;
var src = e.Source as Visual;
while (src != null)
{
@@ -416,6 +413,7 @@ namespace Avalonia.Controls
src = src.VisualParent;
}
+
if (closePane)
{
SetCurrentValue(IsPaneOpenProperty, false);
@@ -423,7 +421,7 @@ namespace Avalonia.Controls
}
}
- private bool ShouldClosePane()
+ private bool IsInOverlayMode()
{
return (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay);
}
@@ -435,6 +433,7 @@ namespace Avalonia.Controls
protected virtual void OnPaneOpened(RoutedEventArgs args)
{
+ EnableLightDismiss();
RaiseEvent(args);
}
@@ -445,6 +444,8 @@ namespace Avalonia.Controls
protected virtual void OnPaneClosed(RoutedEventArgs args)
{
+ _pointerDisposable?.Dispose();
+ _pointerDisposable = null;
RaiseEvent(args);
}
@@ -550,6 +551,39 @@ namespace Avalonia.Controls
PseudoClasses.Add(_lastPlacementPseudoclass);
}
+ private void EnableLightDismiss()
+ {
+ if (_pane == null)
+ return;
+
+ // If this returns false, we're not in Overlay or CompactOverlay DisplayMode
+ // and don't need the light dismiss behavior
+ if (!IsInOverlayMode())
+ return;
+
+ var topLevel = TopLevel.GetTopLevel(this);
+ if (topLevel != null)
+ {
+ _pointerDisposable = Disposable.Create(() =>
+ {
+ topLevel.PointerReleased -= PointerReleasedOutside;
+ topLevel.BackRequested -= TopLevelBackRequested;
+ });
+
+ topLevel.PointerReleased += PointerReleasedOutside;
+ topLevel.BackRequested += TopLevelBackRequested;
+ }
+ }
+
+ private void TopLevelBackRequested(object sender, RoutedEventArgs e)
+ {
+ if (!IsInOverlayMode())
+ return;
+
+ SetCurrentValue(IsPaneOpenProperty, false);
+ e.Handled = true;
+ }
+
///
/// Coerces/validates the property value.
///
From ffdab9436563b24c4705b659571ebb45047f5015 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 18:27:17 -0400
Subject: [PATCH 5/9] Only need to update display mode with ApplyTemplate
---
src/Avalonia.Controls/SplitView/SplitView.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index b8a697e309..72e8140d09 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -301,7 +301,6 @@ namespace Avalonia.Controls
base.OnApplyTemplate(e);
_pane = e.NameScope.Find("PART_PaneRoot");
- UpdateVisualStateForCompactPaneLength(CompactPaneLength);
UpdateVisualStateForDisplayMode(DisplayMode);
}
From a3c8210f2ee7c0041ebfe678f1609958390766e6 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 18:27:29 -0400
Subject: [PATCH 6/9] Add new tests
---
.../SplitViewTests.cs | 224 +++++++++++++++++-
1 file changed, 221 insertions(+), 3 deletions(-)
diff --git a/tests/Avalonia.Controls.UnitTests/SplitViewTests.cs b/tests/Avalonia.Controls.UnitTests/SplitViewTests.cs
index 03653ec42c..9b43c469ba 100644
--- a/tests/Avalonia.Controls.UnitTests/SplitViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/SplitViewTests.cs
@@ -1,6 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using Avalonia.Input;
+using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
@@ -62,5 +61,224 @@ namespace Avalonia.Controls.UnitTests
Assert.True(splitView.IsPaneOpen);
}
+
+ [Fact]
+ public void SplitView_TemplateSettings_Are_Correct_For_Display_Modes()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow);
+ var wnd = new Window
+ {
+ Width = 1280,
+ Height = 720
+ };
+ var splitView = new SplitView();
+ wnd.Content = splitView;
+ wnd.Show();
+
+ var zeroGridLength = new GridLength(0);
+ var compactLength = splitView.CompactPaneLength;
+ var compactGridLength = new GridLength(compactLength);
+
+ // Overlay is default DisplayMode
+ Assert.Equal(0, splitView.TemplateSettings.ClosedPaneWidth);
+ Assert.Equal(zeroGridLength, splitView.TemplateSettings.PaneColumnGridLength);
+
+ splitView.DisplayMode = SplitViewDisplayMode.CompactOverlay;
+ Assert.Equal(compactLength, splitView.TemplateSettings.ClosedPaneWidth);
+ Assert.Equal(compactGridLength, splitView.TemplateSettings.PaneColumnGridLength);
+
+ splitView.DisplayMode = SplitViewDisplayMode.Inline;
+ Assert.Equal(0, splitView.TemplateSettings.ClosedPaneWidth);
+ Assert.Equal(GridLength.Auto, splitView.TemplateSettings.PaneColumnGridLength);
+
+ splitView.DisplayMode = SplitViewDisplayMode.CompactInline;
+ Assert.Equal(compactLength, splitView.TemplateSettings.ClosedPaneWidth);
+ Assert.Equal(GridLength.Auto, splitView.TemplateSettings.PaneColumnGridLength);
+ }
+
+ [Fact]
+ public void SplitView_TemplateSettings_Update_With_CompactPaneLength()
+ {
+ var splitView = new SplitView();
+
+ // CompactInline:
+ // - ClosedPaneWidth = CompactPaneLength
+ // - PaneColumnGridLength = Auto
+ splitView.DisplayMode = SplitViewDisplayMode.CompactInline;
+
+ var compactLength = splitView.CompactPaneLength;
+
+ Assert.Equal(GridLength.Auto, splitView.TemplateSettings.PaneColumnGridLength);
+ Assert.Equal(compactLength, splitView.TemplateSettings.ClosedPaneWidth);
+
+ splitView.CompactPaneLength = 100;
+
+ Assert.Equal(GridLength.Auto, splitView.TemplateSettings.PaneColumnGridLength);
+ Assert.Equal(100, splitView.TemplateSettings.ClosedPaneWidth);
+
+ // CompactOverlay:
+ // - ClosedPaneWidth = CompactPaneLength
+ // - PaneColumnGridLength = GridLength { CompactPaneLength, Pixel }
+ splitView.DisplayMode = SplitViewDisplayMode.CompactOverlay;
+ splitView.CompactPaneLength = 50;
+
+ Assert.Equal(new GridLength(50), splitView.TemplateSettings.PaneColumnGridLength);
+ Assert.Equal(50, splitView.TemplateSettings.ClosedPaneWidth);
+
+ // Value shouldn't change for these - changing the display mode will update
+ // the template settings with the right value
+ splitView.DisplayMode = SplitViewDisplayMode.Inline;
+ splitView.CompactPaneLength = 1;
+
+ Assert.Equal(GridLength.Auto, splitView.TemplateSettings.PaneColumnGridLength);
+ Assert.Equal(0, splitView.TemplateSettings.ClosedPaneWidth);
+
+ splitView.DisplayMode = SplitViewDisplayMode.Overlay;
+ splitView.CompactPaneLength = 2;
+
+ Assert.Equal(new GridLength(0), splitView.TemplateSettings.PaneColumnGridLength);
+ Assert.Equal(0, splitView.TemplateSettings.ClosedPaneWidth);
+ }
+
+ [Fact]
+ public void SplitView_Pointer_Closes_Pane_In_Overlay_Mode()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow
+ .With(globalClock: new MockGlobalClock()));
+ var wnd = new Window
+ {
+ Width = 1280,
+ Height = 720
+ };
+ var splitView = new SplitView();
+ 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.DisplayMode = SplitViewDisplayMode.Inline;
+ splitView.IsPaneOpen = true;
+
+ splitView.RaiseEvent(new PointerReleasedEventArgs(splitView,
+ null, wnd, new Point(1270, 30), 0,
+ new PointerPointProperties(),
+ KeyModifiers.None,
+ MouseButton.Left));
+
+ Assert.True(splitView.IsPaneOpen);
+ }
+
+ [Fact]
+ public void SplitView_Pointer_Should_Not_Close_Pane_If_Over_Pane()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow
+ .With(globalClock: new MockGlobalClock()));
+ var wnd = new Window
+ {
+ Width = 1280,
+ Height = 720
+ };
+ var clickBorder = new Border
+ {
+ Width = 100,
+ Height = 100,
+ HorizontalAlignment = Layout.HorizontalAlignment.Left,
+ VerticalAlignment = Layout.VerticalAlignment.Top
+ };
+ var splitView = new SplitView
+ {
+ Pane = clickBorder
+ };
+ wnd.Content = splitView;
+ wnd.Show();
+
+ splitView.IsPaneOpen = true;
+
+ clickBorder.RaiseEvent(new PointerReleasedEventArgs(splitView,
+ null, wnd, new Point(5, 5), 0,
+ new PointerPointProperties(),
+ KeyModifiers.None,
+ MouseButton.Left));
+
+ Assert.True(splitView.IsPaneOpen);
+ }
+
+ [Fact]
+ public void SplitView_Escape_Key_Closes_Light_Dismissable_Pane()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow
+ .With(globalClock: new MockGlobalClock()));
+ var wnd = new Window
+ {
+ Width = 1280,
+ Height = 720
+ };
+ var button = new Button();
+ var splitView = new SplitView
+ {
+ Pane = button
+ };
+ wnd.Content = splitView;
+ wnd.Show();
+
+ splitView.IsPaneOpen = true;
+
+ button.RaiseEvent(new KeyEventArgs
+ {
+ Key = Key.Escape,
+ RoutedEvent = InputElement.KeyDownEvent
+ });
+
+ Assert.False(splitView.IsPaneOpen);
+
+ splitView.DisplayMode = SplitViewDisplayMode.Inline;
+
+ splitView.IsPaneOpen = true;
+
+ button.RaiseEvent(new KeyEventArgs
+ {
+ Key = Key.Escape,
+ RoutedEvent = InputElement.KeyDownEvent
+ });
+
+ Assert.True(splitView.IsPaneOpen);
+ }
+
+ [Fact]
+ public void Top_Level_Back_Requested_Closes_Light_Dismissable_Pane()
+ {
+ using var app = UnitTestApplication.Start(TestServices.StyledWindow
+ .With(globalClock: new MockGlobalClock()));
+ var wnd = new Window
+ {
+ Width = 1280,
+ Height = 720
+ };
+ var splitView = new SplitView();
+ wnd.Content = splitView;
+ wnd.Show();
+
+ splitView.IsPaneOpen = true;
+
+ wnd.RaiseEvent(new Interactivity.RoutedEventArgs(TopLevel.BackRequestedEvent));
+
+ Assert.False(splitView.IsPaneOpen);
+
+ splitView.DisplayMode = SplitViewDisplayMode.Inline;
+ splitView.IsPaneOpen = true;
+
+ wnd.RaiseEvent(new Interactivity.RoutedEventArgs(TopLevel.BackRequestedEvent));
+
+ Assert.True(splitView.IsPaneOpen);
+ }
}
}
From acc8b9fe52a734af6810a2f411b1ab6aba391123 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 18:28:04 -0400
Subject: [PATCH 7/9] Handle KeyDown if pane close is triggered
---
src/Avalonia.Controls/SplitView/SplitView.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 72e8140d09..8501d518a7 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -382,6 +382,7 @@ namespace Avalonia.Controls
if (IsPaneOpen && IsInOverlayMode())
{
SetCurrentValue(IsPaneOpenProperty, false);
+ e.Handled = true;
}
}
From b5be1ef9b0494c8e357037c39f20fee1e2a39fa6 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 18:38:57 -0400
Subject: [PATCH 8/9] Fix nullable stuff
---
src/Avalonia.Controls/SplitView/SplitView.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 8501d518a7..93b0ffe57f 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -136,9 +136,9 @@ namespace Avalonia.Controls
private Panel? _pane;
private IDisposable? _pointerDisposable;
- private SplitViewTemplateSettings _templateSettings;
- private string _lastDisplayModePseudoclass;
- private string _lastPlacementPseudoclass;
+ private SplitViewTemplateSettings _templateSettings = null!;
+ private string? _lastDisplayModePseudoclass;
+ private string? _lastPlacementPseudoclass;
public SplitView()
{
@@ -575,7 +575,7 @@ namespace Avalonia.Controls
}
}
- private void TopLevelBackRequested(object sender, RoutedEventArgs e)
+ private void TopLevelBackRequested(object? sender, RoutedEventArgs e)
{
if (!IsInOverlayMode())
return;
From ed8a4fa7d5b13e751b5c7f7c1f2c477ddba95420 Mon Sep 17 00:00:00 2001
From: amwx <40413319+amwx@users.noreply.github.com>
Date: Fri, 17 Mar 2023 21:29:45 -0400
Subject: [PATCH 9/9] Just init TemplateSettings
---
src/Avalonia.Controls/SplitView/SplitView.cs | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/Avalonia.Controls/SplitView/SplitView.cs b/src/Avalonia.Controls/SplitView/SplitView.cs
index 93b0ffe57f..8060ca9594 100644
--- a/src/Avalonia.Controls/SplitView/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView/SplitView.cs
@@ -136,15 +136,10 @@ namespace Avalonia.Controls
private Panel? _pane;
private IDisposable? _pointerDisposable;
- private SplitViewTemplateSettings _templateSettings = null!;
+ private SplitViewTemplateSettings _templateSettings = new SplitViewTemplateSettings();
private string? _lastDisplayModePseudoclass;
private string? _lastPlacementPseudoclass;
- public SplitView()
- {
- TemplateSettings = new SplitViewTemplateSettings();
- }
-
///
/// Gets or sets the length of the pane when in
/// or mode