16 changed files with 1183 additions and 19 deletions
@ -0,0 +1,97 @@ |
|||||
|
<UserControl xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" |
||||
|
x:Class="ControlCatalog.Pages.SplitViewPage"> |
||||
|
|
||||
|
<Border> |
||||
|
|
||||
|
<Grid ColumnDefinitions="*,225"> |
||||
|
|
||||
|
<StackPanel Grid.Column="1" Orientation="Vertical" Spacing="4" Margin="5"> |
||||
|
<ToggleButton Name="PaneOpenButton" |
||||
|
Content="IsPaneOpen" |
||||
|
IsChecked="{Binding IsPaneOpen, ElementName=SplitView}" /> |
||||
|
|
||||
|
<ToggleButton Name="UseLightDismissOverlayModeButton" |
||||
|
Content="UseLightDismissOverlayMode" |
||||
|
IsChecked="{Binding UseLightDismissOverlayMode, ElementName=SplitView}" /> |
||||
|
|
||||
|
<ToggleSwitch OffContent="Left" OnContent="Right" Content="Placement" IsChecked="{Binding !IsLeft}" /> |
||||
|
|
||||
|
<TextBlock Text="DisplayMode" /> |
||||
|
<ComboBox Name="DisplayModeSelector" Width="170" Margin="10" SelectedIndex="{Binding DisplayMode}"> |
||||
|
<ComboBoxItem>Inline</ComboBoxItem> |
||||
|
<ComboBoxItem>CompactInline</ComboBoxItem> |
||||
|
<ComboBoxItem>Overlay</ComboBoxItem> |
||||
|
<ComboBoxItem>CompactOverlay</ComboBoxItem> |
||||
|
</ComboBox> |
||||
|
|
||||
|
<TextBlock Text="PaneBackground" /> |
||||
|
<ComboBox Name="PaneBackgroundSelector" SelectedIndex="0" Width="170" Margin="10"> |
||||
|
<ComboBoxItem Tag="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">SystemControlBackgroundChromeMediumLowBrush</ComboBoxItem> |
||||
|
<ComboBoxItem Tag="Red">Red</ComboBoxItem> |
||||
|
<ComboBoxItem Tag="Blue">Blue</ComboBoxItem> |
||||
|
<ComboBoxItem Tag="Green">Green</ComboBoxItem> |
||||
|
</ComboBox> |
||||
|
|
||||
|
<TextBlock Text="{Binding Value, ElementName=OpenPaneLengthSlider, StringFormat='{}OpenPaneLength: {0}'}" /> |
||||
|
<Slider Name="OpenPaneLengthSlider" Value="256" Minimum="128" Maximum="500" |
||||
|
Width="150" /> |
||||
|
|
||||
|
<TextBlock Text="{Binding Value, ElementName=CompactPaneLengthSlider, StringFormat='{}CompactPaneLength: {0}'}" /> |
||||
|
<Slider Name="CompactPaneLengthSlider" Value="48" Minimum="24" Maximum="128" |
||||
|
Width="150" /> |
||||
|
|
||||
|
</StackPanel> |
||||
|
|
||||
|
<Border BorderBrush="{DynamicResource SystemControlHighlightBaseLowBrush}" |
||||
|
BorderThickness="1"> |
||||
|
<!--{Binding SelectedItem.Tag, ElementName=PaneBackgroundSelector}--> |
||||
|
<SplitView Name="SplitView" |
||||
|
PanePlacement="{Binding PanePlacement}" |
||||
|
PaneBackground="{Binding SelectedItem.Tag, ElementName=PaneBackgroundSelector}" |
||||
|
OpenPaneLength="{Binding Value, ElementName=OpenPaneLengthSlider}" |
||||
|
CompactPaneLength="{Binding Value, ElementName=CompactPaneLengthSlider}" |
||||
|
DisplayMode="{Binding CurrentDisplayMode}"> |
||||
|
<SplitView.Pane> |
||||
|
<Grid> |
||||
|
<Grid.RowDefinitions> |
||||
|
<RowDefinition Height="Auto" /> |
||||
|
<RowDefinition Height="*" /> |
||||
|
<RowDefinition Height="Auto" /> |
||||
|
</Grid.RowDefinitions> |
||||
|
<TextBlock Text="PANE CONTENT" FontWeight="Bold" Name="PaneHeader" Margin="5,12,0,0" /> |
||||
|
<ListBoxItem Grid.Row="1" VerticalAlignment="Top" Margin="0 10"> |
||||
|
<StackPanel Orientation="Horizontal"> |
||||
|
<!--Path glyph from materialdesignicons.com--> |
||||
|
<Border Width="48"> |
||||
|
<Viewbox Width="24" Height="24" HorizontalAlignment="Left"> |
||||
|
<Canvas Width="24" Height="24"> |
||||
|
<Path Fill="{DynamicResource SystemControlForegroundBaseHighBrush}" Data="M16 17V19H2V17S2 13 9 13 16 17 16 17M12.5 7.5A3.5 3.5 0 1 0 9 11A3.5 3.5 0 0 0 12.5 7.5M15.94 13A5.32 5.32 0 0 1 18 17V19H22V17S22 13.37 15.94 13M15 4A3.39 3.39 0 0 0 13.07 4.59A5 5 0 0 1 13.07 10.41A3.39 3.39 0 0 0 15 11A3.5 3.5 0 0 0 15 4Z" /> |
||||
|
</Canvas> |
||||
|
</Viewbox> |
||||
|
</Border> |
||||
|
<TextBlock Text="People" VerticalAlignment="Center" /> |
||||
|
</StackPanel> |
||||
|
</ListBoxItem> |
||||
|
<TextBlock Grid.Row="2" Text="Item at bottom" Margin="60,12" /> |
||||
|
</Grid> |
||||
|
</SplitView.Pane> |
||||
|
|
||||
|
<Grid> |
||||
|
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
||||
|
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
||||
|
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" HorizontalAlignment="Right" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
||||
|
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" VerticalAlignment="Bottom" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
||||
|
<TextBlock FontSize="14" FontWeight="700" Text="SplitViewContent" VerticalAlignment="Bottom" HorizontalAlignment="Right" TextAlignment="Left" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" /> |
||||
|
</Grid> |
||||
|
|
||||
|
</SplitView> |
||||
|
</Border> |
||||
|
|
||||
|
</Grid> |
||||
|
</Border> |
||||
|
|
||||
|
</UserControl> |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Avalonia; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using ControlCatalog.ViewModels; |
||||
|
|
||||
|
namespace ControlCatalog.Pages |
||||
|
{ |
||||
|
public class SplitViewPage : UserControl |
||||
|
{ |
||||
|
public SplitViewPage() |
||||
|
{ |
||||
|
this.InitializeComponent(); |
||||
|
DataContext = new SplitViewPageViewModel(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Controls; |
||||
|
using ReactiveUI; |
||||
|
|
||||
|
namespace ControlCatalog.ViewModels |
||||
|
{ |
||||
|
public class SplitViewPageViewModel : ReactiveObject |
||||
|
{ |
||||
|
private bool _isLeft = true; |
||||
|
private int _displayMode = 3; //CompactOverlay
|
||||
|
|
||||
|
public bool IsLeft |
||||
|
{ |
||||
|
get => _isLeft; |
||||
|
set |
||||
|
{ |
||||
|
this.RaiseAndSetIfChanged(ref _isLeft, value); |
||||
|
this.RaisePropertyChanged(nameof(PanePlacement)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int DisplayMode |
||||
|
{ |
||||
|
get => _displayMode; |
||||
|
set |
||||
|
{ |
||||
|
this.RaiseAndSetIfChanged(ref _displayMode, value); |
||||
|
this.RaisePropertyChanged(nameof(CurrentDisplayMode)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public SplitViewPanePlacement PanePlacement => _isLeft ? SplitViewPanePlacement.Left : SplitViewPanePlacement.Right; |
||||
|
|
||||
|
public SplitViewDisplayMode CurrentDisplayMode |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (Enum.IsDefined(typeof(SplitViewDisplayMode), _displayMode)) |
||||
|
{ |
||||
|
return (SplitViewDisplayMode)_displayMode; |
||||
|
} |
||||
|
return SplitViewDisplayMode.CompactOverlay; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
namespace Avalonia.Animation.Easings |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Eases a <see cref="double"/> value
|
||||
|
/// using a user-defined cubic bezier curve.
|
||||
|
/// Good for custom easing functions that doesn't quite
|
||||
|
/// fit with the built-in ones.
|
||||
|
/// </summary>
|
||||
|
public class SplineEasing : Easing |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// X coordinate of the first control point
|
||||
|
/// </summary>
|
||||
|
public double X1 |
||||
|
{ |
||||
|
get => _internalKeySpline.ControlPointX1; |
||||
|
set |
||||
|
{ |
||||
|
_internalKeySpline.ControlPointX1 = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Y coordinate of the first control point
|
||||
|
/// </summary>
|
||||
|
public double Y1 |
||||
|
{ |
||||
|
get => _internalKeySpline.ControlPointY1; |
||||
|
set |
||||
|
{ |
||||
|
_internalKeySpline.ControlPointY1 = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X coordinate of the second control point
|
||||
|
/// </summary>
|
||||
|
public double X2 |
||||
|
{ |
||||
|
get => _internalKeySpline.ControlPointX2; |
||||
|
set |
||||
|
{ |
||||
|
_internalKeySpline.ControlPointX2 = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Y coordinate of the second control point
|
||||
|
/// </summary>
|
||||
|
public double Y2 |
||||
|
{ |
||||
|
get => _internalKeySpline.ControlPointY2; |
||||
|
set |
||||
|
{ |
||||
|
_internalKeySpline.ControlPointY2 = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private readonly KeySpline _internalKeySpline; |
||||
|
|
||||
|
public SplineEasing(double x1 = 0d, double y1 = 0d, double x2 = 1d, double y2 = 1d) |
||||
|
{ |
||||
|
_internalKeySpline = new KeySpline(); |
||||
|
|
||||
|
this.X1 = x1; |
||||
|
this.Y1 = y1; |
||||
|
this.X2 = x2; |
||||
|
this.Y1 = y2; |
||||
|
} |
||||
|
|
||||
|
public SplineEasing(KeySpline keySpline) |
||||
|
{ |
||||
|
_internalKeySpline = keySpline; |
||||
|
} |
||||
|
|
||||
|
public SplineEasing() |
||||
|
{ |
||||
|
_internalKeySpline = new KeySpline(); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public override double Ease(double progress) => |
||||
|
_internalKeySpline.GetSplineProgress(progress); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Globalization; |
||||
|
|
||||
|
// Ported from WPF open-source code.
|
||||
|
// https://github.com/dotnet/wpf/blob/ae1790531c3b993b56eba8b1f0dd395a3ed7de75/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySpline.cs
|
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Converts string values to <see cref="KeySpline"/> values
|
||||
|
/// </summary>
|
||||
|
public class KeySplineTypeConverter : TypeConverter |
||||
|
{ |
||||
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
||||
|
{ |
||||
|
return sourceType == typeof(string); |
||||
|
} |
||||
|
|
||||
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
||||
|
{ |
||||
|
return KeySpline.Parse((string)value, culture); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,487 @@ |
|||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Input; |
||||
|
using Avalonia.Input.Raw; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.VisualTree; |
||||
|
using System; |
||||
|
using System.Reactive.Disposables; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines constants for how the SplitView Pane should display
|
||||
|
/// </summary>
|
||||
|
public enum SplitViewDisplayMode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Pane is displayed next to content, and does not auto collapse
|
||||
|
/// when tapped outside
|
||||
|
/// </summary>
|
||||
|
Inline, |
||||
|
/// <summary>
|
||||
|
/// Pane is displayed next to content. When collapsed, pane is still
|
||||
|
/// visible according to CompactPaneLength. Pane does not auto collapse
|
||||
|
/// when tapped outside
|
||||
|
/// </summary>
|
||||
|
CompactInline, |
||||
|
/// <summary>
|
||||
|
/// Pane is displayed above content. Pane collapses when tapped outside
|
||||
|
/// </summary>
|
||||
|
Overlay, |
||||
|
/// <summary>
|
||||
|
/// Pane is displayed above content. When collapsed, pane is still
|
||||
|
/// visible according to CompactPaneLength. Pane collapses when tapped outside
|
||||
|
/// </summary>
|
||||
|
CompactOverlay |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines constants for where the Pane should appear
|
||||
|
/// </summary>
|
||||
|
public enum SplitViewPanePlacement |
||||
|
{ |
||||
|
Left, |
||||
|
Right |
||||
|
} |
||||
|
|
||||
|
public class SplitViewTemplateSettings : AvaloniaObject |
||||
|
{ |
||||
|
internal SplitViewTemplateSettings() { } |
||||
|
|
||||
|
public static readonly StyledProperty<double> ClosedPaneWidthProperty = |
||||
|
AvaloniaProperty.Register<SplitViewTemplateSettings, double>(nameof(ClosedPaneWidth), 0d); |
||||
|
|
||||
|
public static readonly StyledProperty<GridLength> PaneColumnGridLengthProperty = |
||||
|
AvaloniaProperty.Register<SplitViewTemplateSettings, GridLength>(nameof(PaneColumnGridLength)); |
||||
|
|
||||
|
public double ClosedPaneWidth |
||||
|
{ |
||||
|
get => GetValue(ClosedPaneWidthProperty); |
||||
|
internal set => SetValue(ClosedPaneWidthProperty, value); |
||||
|
} |
||||
|
|
||||
|
public GridLength PaneColumnGridLength |
||||
|
{ |
||||
|
get => GetValue(PaneColumnGridLengthProperty); |
||||
|
internal set => SetValue(PaneColumnGridLengthProperty, value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// A control with two views: A collapsible pane and an area for content
|
||||
|
/// </summary>
|
||||
|
public class SplitView : TemplatedControl |
||||
|
{ |
||||
|
/* |
||||
|
Pseudo classes & combos |
||||
|
:open / :closed |
||||
|
:compactoverlay :compactinline :overlay :inline |
||||
|
:left :right |
||||
|
*/ |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Content"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IControl> ContentProperty = |
||||
|
AvaloniaProperty.Register<SplitView, IControl>(nameof(Content)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="CompactPaneLength"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> CompactPaneLengthProperty = |
||||
|
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); |
||||
|
|
||||
|
/// <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); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="OpenPaneLength"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> OpenPaneLengthProperty = |
||||
|
AvaloniaProperty.Register<SplitView, double>(nameof(OpenPaneLength), defaultValue: 320); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="PaneBackground"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IBrush> PaneBackgroundProperty = |
||||
|
AvaloniaProperty.Register<SplitView, IBrush>(nameof(PaneBackground)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="PanePlacement"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<SplitViewPanePlacement> PanePlacementProperty = |
||||
|
AvaloniaProperty.Register<SplitView, SplitViewPanePlacement>(nameof(PanePlacement)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Pane"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IControl> PaneProperty = |
||||
|
AvaloniaProperty.Register<SplitView, IControl>(nameof(Pane)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="UseLightDismissOverlayMode"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<bool> UseLightDismissOverlayModeProperty = |
||||
|
AvaloniaProperty.Register<SplitView, bool>(nameof(UseLightDismissOverlayMode)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="TemplateSettings"/> property
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<SplitViewTemplateSettings> TemplateSettingsProperty = |
||||
|
AvaloniaProperty.Register<SplitView, SplitViewTemplateSettings>(nameof(TemplateSettings)); |
||||
|
|
||||
|
private bool _isPaneOpen; |
||||
|
private Panel _pane; |
||||
|
private CompositeDisposable _pointerDisposables; |
||||
|
|
||||
|
public SplitView() |
||||
|
{ |
||||
|
PseudoClasses.Add(":overlay"); |
||||
|
PseudoClasses.Add(":left"); |
||||
|
|
||||
|
TemplateSettings = new SplitViewTemplateSettings(); |
||||
|
} |
||||
|
|
||||
|
static SplitView() |
||||
|
{ |
||||
|
UseLightDismissOverlayModeProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnUseLightDismissChanged(v)); |
||||
|
CompactPaneLengthProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnCompactPaneLengthChanged(v)); |
||||
|
PanePlacementProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnPanePlacementChanged(v)); |
||||
|
DisplayModeProperty.Changed.AddClassHandler<SplitView>((x, v) => x.OnDisplayModeChanged(v)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the content of the SplitView
|
||||
|
/// </summary>
|
||||
|
[Content] |
||||
|
public IControl Content |
||||
|
{ |
||||
|
get => GetValue(ContentProperty); |
||||
|
set => SetValue(ContentProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the length of the pane when in <see cref="SplitViewDisplayMode.CompactOverlay"/>
|
||||
|
/// or <see cref="SplitViewDisplayMode.CompactInline"/> mode
|
||||
|
/// </summary>
|
||||
|
public double CompactPaneLength |
||||
|
{ |
||||
|
get => GetValue(CompactPaneLengthProperty); |
||||
|
set => SetValue(CompactPaneLengthProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the <see cref="SplitViewDisplayMode"/> for the SplitView
|
||||
|
/// </summary>
|
||||
|
public SplitViewDisplayMode DisplayMode |
||||
|
{ |
||||
|
get => GetValue(DisplayModeProperty); |
||||
|
set => SetValue(DisplayModeProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets whether the pane is open or closed
|
||||
|
/// </summary>
|
||||
|
public bool IsPaneOpen |
||||
|
{ |
||||
|
get => _isPaneOpen; |
||||
|
set |
||||
|
{ |
||||
|
if (value == _isPaneOpen) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (value) |
||||
|
{ |
||||
|
OnPaneOpening(this, null); |
||||
|
SetAndRaise(IsPaneOpenProperty, ref _isPaneOpen, value); |
||||
|
|
||||
|
PseudoClasses.Add(":open"); |
||||
|
PseudoClasses.Remove(":closed"); |
||||
|
OnPaneOpened(this, null); |
||||
|
} |
||||
|
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, null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the length of the pane when open
|
||||
|
/// </summary>
|
||||
|
public double OpenPaneLength |
||||
|
{ |
||||
|
get => GetValue(OpenPaneLengthProperty); |
||||
|
set => SetValue(OpenPaneLengthProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the background of the pane
|
||||
|
/// </summary>
|
||||
|
public IBrush PaneBackground |
||||
|
{ |
||||
|
get => GetValue(PaneBackgroundProperty); |
||||
|
set => SetValue(PaneBackgroundProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the <see cref="SplitViewPanePlacement"/> for the SplitView
|
||||
|
/// </summary>
|
||||
|
public SplitViewPanePlacement PanePlacement |
||||
|
{ |
||||
|
get => GetValue(PanePlacementProperty); |
||||
|
set => SetValue(PanePlacementProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the Pane for the SplitView
|
||||
|
/// </summary>
|
||||
|
public IControl Pane |
||||
|
{ |
||||
|
get => GetValue(PaneProperty); |
||||
|
set => SetValue(PaneProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled
|
||||
|
/// <para>When enabled, and the pane is open in Overlay or CompactOverlay mode,
|
||||
|
/// the contents of the splitview are darkened to visually separate the open pane
|
||||
|
/// and the rest of the SplitView</para>
|
||||
|
/// </summary>
|
||||
|
public bool UseLightDismissOverlayMode |
||||
|
{ |
||||
|
get => GetValue(UseLightDismissOverlayModeProperty); |
||||
|
set => SetValue(UseLightDismissOverlayModeProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the TemplateSettings for the SplitView
|
||||
|
/// </summary>
|
||||
|
public SplitViewTemplateSettings TemplateSettings |
||||
|
{ |
||||
|
get => GetValue(TemplateSettingsProperty); |
||||
|
set => SetValue(TemplateSettingsProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fired when the pane is closed
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<EventArgs> PaneClosed; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fired when the pane is closing
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<SplitViewPaneClosingEventArgs> PaneClosing; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fired when the pane is opened
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<EventArgs> PaneOpened; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fired when the pane is opening
|
||||
|
/// </summary>
|
||||
|
public event EventHandler<EventArgs> PaneOpening; |
||||
|
|
||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) |
||||
|
{ |
||||
|
base.OnApplyTemplate(e); |
||||
|
_pane = e.NameScope.Find<Panel>("PART_PaneRoot"); |
||||
|
} |
||||
|
|
||||
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) |
||||
|
{ |
||||
|
base.OnAttachedToVisualTree(e); |
||||
|
|
||||
|
var topLevel = this.VisualRoot; |
||||
|
if (topLevel is Window window) |
||||
|
{ |
||||
|
//Logic adapted from Popup
|
||||
|
//Basically if we're using an overlay DisplayMode, close the pane if we don't click on the pane
|
||||
|
IDisposable subscribeToEventHandler<T, TEventHandler>(T target, TEventHandler handler, |
||||
|
Action<T, TEventHandler> subscribe, Action<T, TEventHandler> unsubscribe) |
||||
|
{ |
||||
|
subscribe(target, handler); |
||||
|
return Disposable.Create((unsubscribe, target, handler), state => state.unsubscribe(state.target, state.handler)); |
||||
|
} |
||||
|
|
||||
|
_pointerDisposables = new CompositeDisposable( |
||||
|
window.AddDisposableHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel), |
||||
|
InputManager.Instance?.Process.Subscribe(OnNonClientClick), |
||||
|
subscribeToEventHandler<Window, EventHandler>(window, Window_Deactivated, |
||||
|
(x, handler) => x.Deactivated += handler, (x, handler) => x.Deactivated -= handler), |
||||
|
subscribeToEventHandler<IWindowImpl, Action>(window.PlatformImpl, OnWindowLostFocus, |
||||
|
(x, handler) => x.LostFocus += handler, (x, handler) => x.LostFocus -= handler)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) |
||||
|
{ |
||||
|
base.OnDetachedFromVisualTree(e); |
||||
|
_pointerDisposables?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
private void OnWindowLostFocus() |
||||
|
{ |
||||
|
if (IsPaneOpen && ShouldClosePane()) |
||||
|
{ |
||||
|
IsPaneOpen = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void PointerPressedOutside(object sender, PointerPressedEventArgs e) |
||||
|
{ |
||||
|
if (!IsPaneOpen) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
//If we click within the Pane, don't do anything
|
||||
|
//Otherwise, ClosePane if open & using an overlay display mode
|
||||
|
bool closePane = ShouldClosePane(); |
||||
|
if (!closePane) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var src = e.Source as IVisual; |
||||
|
while (src != null) |
||||
|
{ |
||||
|
if (src == _pane) |
||||
|
{ |
||||
|
closePane = false; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
src = src.VisualParent; |
||||
|
} |
||||
|
if (closePane) |
||||
|
{ |
||||
|
IsPaneOpen = false; |
||||
|
e.Handled = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnNonClientClick(RawInputEventArgs obj) |
||||
|
{ |
||||
|
if (!IsPaneOpen) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var mouse = obj as RawPointerEventArgs; |
||||
|
if (mouse?.Type == RawPointerEventType.NonClientLeftButtonDown) |
||||
|
|
||||
|
{ |
||||
|
if (ShouldClosePane()) |
||||
|
IsPaneOpen = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void Window_Deactivated(object sender, EventArgs e) |
||||
|
{ |
||||
|
if (IsPaneOpen && ShouldClosePane()) |
||||
|
{ |
||||
|
IsPaneOpen = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool ShouldClosePane() |
||||
|
{ |
||||
|
return (DisplayMode == SplitViewDisplayMode.CompactOverlay || DisplayMode == SplitViewDisplayMode.Overlay); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnPaneOpening(SplitView sender, EventArgs args) |
||||
|
{ |
||||
|
PaneOpening?.Invoke(sender, args); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnPaneOpened(SplitView sender, EventArgs args) |
||||
|
{ |
||||
|
PaneOpened?.Invoke(sender, args); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnPaneClosing(SplitView sender, SplitViewPaneClosingEventArgs args) |
||||
|
{ |
||||
|
PaneClosing?.Invoke(sender, args); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnPaneClosed(SplitView sender, EventArgs args) |
||||
|
{ |
||||
|
PaneClosed?.Invoke(sender, args); |
||||
|
} |
||||
|
|
||||
|
private void OnCompactPaneLengthChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
var newLen = (double)e.NewValue; |
||||
|
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 OnPanePlacementChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
var oldState = e.OldValue.ToString().ToLower(); |
||||
|
var newState = e.NewValue.ToString().ToLower(); |
||||
|
PseudoClasses.Remove($":{oldState}"); |
||||
|
PseudoClasses.Add($":{newState}"); |
||||
|
} |
||||
|
|
||||
|
private void OnDisplayModeChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
var oldState = e.OldValue.ToString().ToLower(); |
||||
|
var newState = e.NewValue.ToString().ToLower(); |
||||
|
|
||||
|
PseudoClasses.Remove($":{oldState}"); |
||||
|
PseudoClasses.Add($":{newState}"); |
||||
|
|
||||
|
var (closedPaneWidth, paneColumnGridLength) = (SplitViewDisplayMode)e.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 OnUseLightDismissChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
var mode = (bool)e.NewValue; |
||||
|
PseudoClasses.Set(":lightdismiss", mode); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public class SplitViewPaneClosingEventArgs : EventArgs |
||||
|
{ |
||||
|
public bool Cancel { get; set; } |
||||
|
|
||||
|
public SplitViewPaneClosingEventArgs(bool cancel) |
||||
|
{ |
||||
|
Cancel = cancel; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,219 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
|
||||
|
<Styles.Resources> |
||||
|
<x:Double x:Key="SplitViewOpenPaneThemeLength">320</x:Double> |
||||
|
<x:Double x:Key="SplitViewCompactPaneThemeLength">48</x:Double> |
||||
|
|
||||
|
<!-- Not used here (directly) since they're strings, but preserving for reference |
||||
|
<x:String x:Key="SplitViewPaneAnimationOpenDuration">00:00:00.2</x:String> |
||||
|
<x:String x:Key="SplitViewPaneAnimationOpenPreDuration">00:00:00.19999</x:String> |
||||
|
<x:String x:Key="SplitViewPaneAnimationCloseDuration">00:00:00.1</x:String>--> |
||||
|
</Styles.Resources> |
||||
|
|
||||
|
<Style Selector="SplitView"> |
||||
|
<Setter Property="OpenPaneLength" Value="{DynamicResource SplitViewOpenPaneThemeLength}" /> |
||||
|
<Setter Property="CompactPaneLength" Value="{DynamicResource SplitViewCompactPaneThemeLength}" /> |
||||
|
<Setter Property="PaneBackground" Value="{DynamicResource SystemControlPageBackgroundChromeLowBrush}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Left --> |
||||
|
<Style Selector="SplitView:left"> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<Grid Name="Container" Background="{TemplateBinding Background}"> |
||||
|
<Grid.ColumnDefinitions> |
||||
|
<!-- why is this throwing a binding error? --> |
||||
|
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PaneColumnGridLength}"/> |
||||
|
<ColumnDefinition Width="*"/> |
||||
|
</Grid.ColumnDefinitions> |
||||
|
|
||||
|
<Panel Name="PART_PaneRoot" Background="{TemplateBinding PaneBackground}" |
||||
|
ClipToBounds="True" |
||||
|
HorizontalAlignment="Left" |
||||
|
ZIndex="100"> |
||||
|
<Border Child="{TemplateBinding Pane}"/> |
||||
|
<Rectangle Name="HCPaneBorder" Fill="{DynamicResource SystemControlForegroundTransparentBrush}" Width="1" HorizontalAlignment="Right" /> |
||||
|
</Panel> |
||||
|
|
||||
|
<Panel Name="ContentRoot"> |
||||
|
<Border Child="{TemplateBinding Content}" /> |
||||
|
<Rectangle Name="LightDismissLayer"/> |
||||
|
</Panel> |
||||
|
|
||||
|
</Grid> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Overlay --> |
||||
|
<Style Selector="SplitView:overlay:left /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
<!-- ColumnSpan should be 2 --> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:overlay:left /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="2"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CompactInline --> |
||||
|
<Style Selector="SplitView:compactinline:left /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:compactinline:left /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CompactOverlay --> |
||||
|
<Style Selector="SplitView:compactoverlay:left /template/ Panel#PART_PaneRoot"> |
||||
|
<!-- ColumnSpan should be 2 --> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:compactoverlay:left /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Inline --> |
||||
|
<Style Selector="SplitView:inline:left /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:inline:left /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Right --> |
||||
|
<Style Selector="SplitView:right"> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<Grid Name="Container" Background="{TemplateBinding Background}"> |
||||
|
<Grid.ColumnDefinitions> |
||||
|
<ColumnDefinition Width="*"/> |
||||
|
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.PaneColumnGridLength}"/> |
||||
|
</Grid.ColumnDefinitions> |
||||
|
|
||||
|
<Panel Name="PART_PaneRoot" Background="{TemplateBinding PaneBackground}" |
||||
|
ClipToBounds="True" |
||||
|
HorizontalAlignment="Right" |
||||
|
ZIndex="100"> |
||||
|
<Border Child="{TemplateBinding Pane}"/> |
||||
|
<Rectangle Name="HCPaneBorder" |
||||
|
Fill="{DynamicResource SystemControlForegroundTransparentBrush}" |
||||
|
Width="1" HorizontalAlignment="Left" /> |
||||
|
</Panel> |
||||
|
|
||||
|
<Panel Name="ContentRoot"> |
||||
|
<Border Child="{TemplateBinding Content}" /> |
||||
|
<Rectangle Name="LightDismissLayer"/> |
||||
|
</Panel> |
||||
|
|
||||
|
</Grid> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Overlay --> |
||||
|
<Style Selector="SplitView:overlay:right /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="2"/> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:overlay:right /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="2"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CompactInline --> |
||||
|
<Style Selector="SplitView:compactinline:right /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:compactinline:right /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CompactOverlay --> |
||||
|
<Style Selector="SplitView:compactoverlay:right /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="2"/> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:compactoverlay:right /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Inline --> |
||||
|
<Style Selector="SplitView:inline:right /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
<Setter Property="Grid.Column" Value="1"/> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:inline:right /template/ Panel#ContentRoot"> |
||||
|
<Setter Property="Grid.Column" Value="0"/> |
||||
|
<Setter Property="Grid.ColumnSpan" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- Open/Close Pane animation --> |
||||
|
<Style Selector="SplitView:open /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Width" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" /> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=OpenPaneLength}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:open /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Opacity" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" /> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
<Setter Property="Opacity" Value="1.0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="SplitView:closed /template/ Panel#PART_PaneRoot"> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Width" Duration="00:00:00.1" Easing="0.1,0.9,0.2,1.0" /> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ClosedPaneWidth}" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:closed /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Opacity" Duration="00:00:00.2" Easing="0.1,0.9,0.2,1.0" /> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
<Setter Property="Opacity" Value="0.0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="SplitView /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="IsVisible" Value="False"/> |
||||
|
<Setter Property="Fill" Value="Transparent" /> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:lightdismiss /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource SplitViewLightDismissOverlayBackground}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="SplitView:overlay:open /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="IsVisible" Value="True"/> |
||||
|
</Style> |
||||
|
<Style Selector="SplitView:compactoverlay:open /template/ Rectangle#LightDismissLayer"> |
||||
|
<Setter Property="IsVisible" Value="True"/> |
||||
|
</Style> |
||||
|
|
||||
|
</Styles> |
||||
@ -0,0 +1,66 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Controls.UnitTests |
||||
|
{ |
||||
|
|
||||
|
public class SplitViewTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void SplitView_PaneOpening_Should_Fire_Before_PaneOpened() |
||||
|
{ |
||||
|
var splitView = new SplitView(); |
||||
|
|
||||
|
bool handledOpening = false; |
||||
|
splitView.PaneOpening += (x, e) => |
||||
|
{ |
||||
|
handledOpening = true; |
||||
|
}; |
||||
|
|
||||
|
splitView.PaneOpened += (x, e) => |
||||
|
{ |
||||
|
Assert.True(handledOpening); |
||||
|
}; |
||||
|
|
||||
|
splitView.IsPaneOpen = true; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SplitView_PaneClosing_Should_Fire_Before_PaneClosed() |
||||
|
{ |
||||
|
var splitView = new SplitView(); |
||||
|
splitView.IsPaneOpen = true; |
||||
|
|
||||
|
bool handledClosing = false; |
||||
|
splitView.PaneClosing += (x, e) => |
||||
|
{ |
||||
|
handledClosing = true; |
||||
|
}; |
||||
|
|
||||
|
splitView.PaneClosed += (x, e) => |
||||
|
{ |
||||
|
Assert.True(handledClosing); |
||||
|
}; |
||||
|
|
||||
|
splitView.IsPaneOpen = false; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SplitView_Cancel_Close_Should_Prevent_Pane_From_Closing() |
||||
|
{ |
||||
|
var splitView = new SplitView(); |
||||
|
splitView.IsPaneOpen = true; |
||||
|
|
||||
|
splitView.PaneClosing += (x, e) => |
||||
|
{ |
||||
|
e.Cancel = true; |
||||
|
}; |
||||
|
|
||||
|
splitView.IsPaneOpen = false; |
||||
|
|
||||
|
Assert.True(splitView.IsPaneOpen); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue