Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch SplitView.IsPaneOpen to a StyledProperty #10138

Merged
merged 11 commits into from
Feb 22, 2023
157 changes: 100 additions & 57 deletions src/Avalonia.Controls/SplitView.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using Avalonia.Controls.Metadata;
using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.VisualTree;
using System;
using Avalonia.Reactive;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.LogicalTree;

namespace Avalonia.Controls
{
Expand Down Expand Up @@ -54,10 +52,13 @@ public class SplitViewTemplateSettings : AvaloniaObject
internal SplitViewTemplateSettings() { }

public static readonly StyledProperty<double> ClosedPaneWidthProperty =
AvaloniaProperty.Register<SplitViewTemplateSettings, double>(nameof(ClosedPaneWidth), 0d);
AvaloniaProperty.Register<SplitViewTemplateSettings,
double>(nameof(ClosedPaneWidth),
0d);

public static readonly StyledProperty<GridLength> PaneColumnGridLengthProperty =
AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>(nameof(PaneColumnGridLength));
AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>(
nameof(PaneColumnGridLength));

public double ClosedPaneWidth
{
Expand Down Expand Up @@ -93,26 +94,34 @@ Pseudo classes & combos
/// Defines the <see cref="CompactPaneLength"/> property
/// </summary>
public static readonly StyledProperty<double> CompactPaneLengthProperty =
AvaloniaProperty.Register<SplitView, double>(nameof(CompactPaneLength), defaultValue: 48);
AvaloniaProperty.Register<SplitView, double>(
nameof(CompactPaneLength),
defaultValue: 48);

/// <summary>
/// Defines the <see cref="DisplayMode"/> property
/// </summary>
public static readonly StyledProperty<SplitViewDisplayMode> DisplayModeProperty =
AvaloniaProperty.Register<SplitView, SplitViewDisplayMode>(nameof(DisplayMode), defaultValue: SplitViewDisplayMode.Overlay);
AvaloniaProperty.Register<SplitView, SplitViewDisplayMode>(
nameof(DisplayMode),
defaultValue: SplitViewDisplayMode.Overlay);

/// <summary>
/// Defines the <see cref="IsPaneOpen"/> property
/// </summary>
public static readonly DirectProperty<SplitView, bool> IsPaneOpenProperty =
AvaloniaProperty.RegisterDirect<SplitView, bool>(nameof(IsPaneOpen),
x => x.IsPaneOpen, (x, v) => x.IsPaneOpen = v);
public static readonly StyledProperty<bool> IsPaneOpenProperty =
AvaloniaProperty.Register<SplitView, bool>(
nameof(IsPaneOpen),
defaultValue: false,
coerce: CoerceIsPaneOpen);

/// <summary>
/// Defines the <see cref="OpenPaneLength"/> property
/// </summary>
public static readonly StyledProperty<double> OpenPaneLengthProperty =
AvaloniaProperty.Register<SplitView, double>(nameof(OpenPaneLength), defaultValue: 320);
AvaloniaProperty.Register<SplitView, double>(
nameof(OpenPaneLength),
defaultValue: 320);

/// <summary>
/// Defines the <see cref="PaneBackground"/> property
Expand Down Expand Up @@ -150,7 +159,6 @@ Pseudo classes & combos
public static readonly StyledProperty<SplitViewTemplateSettings> TemplateSettingsProperty =
AvaloniaProperty.Register<SplitView, SplitViewTemplateSettings>(nameof(TemplateSettings));

private bool _isPaneOpen;
private Panel? _pane;
private IDisposable? _pointerDisposable;

Expand All @@ -169,7 +177,7 @@ static SplitView()
PanePlacementProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnPanePlacementChanged(v));
DisplayModeProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnDisplayModeChanged(v));

PaneProperty.Changed.AddClassHandler<SplitView>((x, e) => x.PaneChanged(e));
PaneProperty.Changed.AddClassHandler<SplitView>((x, e) => x.OnPaneChanged(e));
robloo marked this conversation as resolved.
Show resolved Hide resolved
}

/// <summary>
Expand All @@ -196,37 +204,8 @@ public SplitViewDisplayMode DisplayMode
/// </summary>
public bool IsPaneOpen
{
get => _isPaneOpen;
set
{
if (value == _isPaneOpen)
{
return;
}

if (value)
{
OnPaneOpening(this, EventArgs.Empty);
SetAndRaise(IsPaneOpenProperty, ref _isPaneOpen, value);

PseudoClasses.Add(":open");
PseudoClasses.Remove(":closed");
OnPaneOpened(this, EventArgs.Empty);
}
else
{
SplitViewPaneClosingEventArgs args = new SplitViewPaneClosingEventArgs(false);
OnPaneClosing(this, args);
if (!args.Cancel)
{
SetAndRaise(IsPaneOpenProperty, ref _isPaneOpen, value);

PseudoClasses.Add(":closed");
PseudoClasses.Remove(":open");
OnPaneClosed(this, EventArgs.Empty);
}
}
}
get => GetValue(IsPaneOpenProperty);
set => SetValue(IsPaneOpenProperty, value);
}

/// <summary>
Expand Down Expand Up @@ -351,6 +330,30 @@ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e
_pointerDisposable?.Dispose();
}

/// <inheritdoc/>
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == IsPaneOpenProperty)
{
bool isPaneOpen = change.GetNewValue<bool>();

if (isPaneOpen)
{
PseudoClasses.Add(":open");
PseudoClasses.Remove(":closed");
OnPaneOpened(EventArgs.Empty);
}
else
{
PseudoClasses.Add(":closed");
PseudoClasses.Remove(":open");
OnPaneClosed(EventArgs.Empty);
}
}
}

private void PointerPressedOutside(object? sender, PointerPressedEventArgs e)
{
if (!IsPaneOpen)
Expand Down Expand Up @@ -394,24 +397,24 @@ private bool ShouldClosePane()
return (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay);
}

protected virtual void OnPaneOpening(SplitView sender, EventArgs args)
protected virtual void OnPaneOpening(EventArgs args)
robloo marked this conversation as resolved.
Show resolved Hide resolved
{
PaneOpening?.Invoke(sender, args);
PaneOpening?.Invoke(this, args);
}

protected virtual void OnPaneOpened(SplitView sender, EventArgs args)
protected virtual void OnPaneOpened(EventArgs args)
{
PaneOpened?.Invoke(sender, args);
PaneOpened?.Invoke(this, args);
}

protected virtual void OnPaneClosing(SplitView sender, SplitViewPaneClosingEventArgs args)
protected virtual void OnPaneClosing(SplitViewPaneClosingEventArgs args)
{
PaneClosing?.Invoke(sender, args);
PaneClosing?.Invoke(this, args);
}

protected virtual void OnPaneClosed(SplitView sender, EventArgs args)
protected virtual void OnPaneClosed(EventArgs args)
{
PaneClosed?.Invoke(sender, args);
PaneClosed?.Invoke(this, args);
}

private void OnCompactPaneLengthChanged(AvaloniaPropertyChangedEventArgs e)
Expand Down Expand Up @@ -485,7 +488,7 @@ private void OnUseLightDismissChanged(AvaloniaPropertyChangedEventArgs e)
PseudoClasses.Set(":lightdismiss", mode);
}

private void PaneChanged(AvaloniaPropertyChangedEventArgs e)
private void OnPaneChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.OldValue is ILogical oldChild)
{
Expand All @@ -497,5 +500,45 @@ private void PaneChanged(AvaloniaPropertyChangedEventArgs e)
LogicalChildren.Add(newChild);
}
}

/// <summary>
/// Called when the <see cref="IsPaneOpen"/> property has to be coerced.
/// </summary>
/// <param name="value">The value to coerce.</param>
protected virtual bool OnCoerceIsPaneOpen(bool value)
{
if (value)
{
OnPaneOpening(EventArgs.Empty);
}
else
{
var eventArgs = new SplitViewPaneClosingEventArgs(false);
OnPaneClosing(eventArgs);

if (eventArgs.Cancel)
{
return !value;
}
robloo marked this conversation as resolved.
Show resolved Hide resolved
}

return value;
}

/// <summary>
/// Coerces/validates the <see cref="IsPaneOpen"/> property value.
/// </summary>
/// <param name="instance">The <see cref="SplitView"/> instance.</param>
/// <param name="value">The value to coerce.</param>
/// <returns>The coerced/validated value.</returns>
private static bool CoerceIsPaneOpen(AvaloniaObject instance, bool value)
{
if (instance is SplitView splitView)
{
return splitView.OnCoerceIsPaneOpen(value);
}

return value;
}
}
}