Merge pull request #103 from irihitech/issue/102

Dialog CanDragMove/CanClose attached property.
This commit is contained in:
Dong Bin
2024-02-10 01:33:00 +08:00
committed by GitHub
20 changed files with 516 additions and 247 deletions

View File

@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Ursa.Demo.Dialogs"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
d:DesignHeight="450"
@@ -30,6 +31,7 @@
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="8">
<Rectangle Width="10" Height="10" Fill="Red" u:DialogControlBase.CanClose="True"></Rectangle>
<Button Command="{Binding DialogCommand}" Content="Dialog" />
<Button Command="{Binding OKCommand}" Content="OK" />
<Button Command="{Binding CancelCommand}" Content="Cancel" />

View File

@@ -36,8 +36,7 @@
OnContent="Modal" />
<ToggleSwitch
Content="ClickOnMaskToClose"
IsChecked="{Binding CanCloseMaskToClose}"
IsVisible="{Binding #defaultModal.IsChecked}"
IsChecked="{Binding CanLightDismiss}"
OffContent="No"
OnContent="Yes" />
<StackPanel Orientation="Horizontal">
@@ -81,8 +80,7 @@
OnContent="Modal" />
<ToggleSwitch
Content="ClickOnMaskToClose"
IsChecked="{Binding CanCloseMaskToClose}"
IsVisible="{Binding #modal.IsChecked}"
IsChecked="{Binding CanLightDismiss}"
OffContent="No"
OnContent="Yes" />
<Button Command="{Binding ShowCustomDialogCommand}" Content="Show Dialog" />

View File

@@ -23,13 +23,13 @@
OffContent="Local"
OnContent="Global" />
<ToggleSwitch
Content="ShowMask"
IsChecked="{Binding ShowMask}"
Content="Modal"
IsChecked="{Binding IsModal}"
OffContent="No"
OnContent="Yes" />
<ToggleSwitch
Content="ClickOnMaskToClose"
IsChecked="{Binding CanCloseMaskToClose}"
Content="CanLightDismiss"
IsChecked="{Binding CanLightDismiss}"
OffContent="No"
OnContent="Yes" />
<StackPanel Orientation="Horizontal">
@@ -56,13 +56,13 @@
OffContent="Local"
OnContent="Global" />
<ToggleSwitch
Content="ClickOnMaskToClose"
IsChecked="{Binding CanCloseMaskToClose}"
Content="CanLightDismiss"
IsChecked="{Binding CanLightDismiss}"
OffContent="No"
OnContent="Yes" />
<ToggleSwitch
Content="ShowMask"
IsChecked="{Binding ShowMask}"
Content="Modal"
IsChecked="{Binding IsModal}"
OffContent="No"
OnContent="Yes" />
<Button Command="{Binding ShowCustomDialogCommand}" Content="Show Custom Drawer" />

View File

@@ -55,11 +55,11 @@ public class DialogDemoViewModel: ObservableObject
set => SetProperty(ref _isModal, value);
}
private bool _canCloseMaskToClose;
public bool CanCloseMaskToClose
private bool _canLightDismiss;
public bool CanLightDismiss
{
get => _canCloseMaskToClose;
set => SetProperty(ref _canCloseMaskToClose, value);
get => _canLightDismiss;
set => SetProperty(ref _canLightDismiss, value);
}
private DialogResult? _defaultResult;
@@ -118,7 +118,7 @@ public class DialogDemoViewModel: ObservableObject
Title = "Please select a date",
Mode = SelectedMode,
Buttons = SelectedButton,
CanClickOnMaskToClose = CanCloseMaskToClose,
CanLightDismiss = CanLightDismiss,
HorizontalAnchor = HorizontalPosition.Right,
HorizontalOffset = 50,
VerticalAnchor = VerticalPosition.Top,
@@ -136,7 +136,8 @@ public class DialogDemoViewModel: ObservableObject
{
Title = "Please select a date",
Mode = SelectedMode,
Buttons = SelectedButton
Buttons = SelectedButton,
CanLightDismiss = CanLightDismiss,
});
}
}
@@ -169,14 +170,15 @@ public class DialogDemoViewModel: ObservableObject
Result = await OverlayDialog.ShowCustomModal<DialogWithAction, DialogWithActionViewModel, bool>(
vm, IsGlobal ? null : "LocalHost", options: new OverlayDialogOptions()
{
CanClickOnMaskToClose = CanCloseMaskToClose,
CanLightDismiss = CanLightDismiss,
});
Date = vm.Date;
}
else
{
OverlayDialog.ShowCustom<DialogWithAction, DialogWithActionViewModel>(new DialogWithActionViewModel(),
IsGlobal ? null : "LocalHost");
IsGlobal ? null : "LocalHost",
options: new OverlayDialogOptions{ CanLightDismiss = CanLightDismiss });
}
}

View File

@@ -20,10 +20,10 @@ public partial class DrawerDemoViewModel: ObservableObject
[ObservableProperty] private Position _selectedPosition;
[ObservableProperty] private DialogButton _selectedButton;
[ObservableProperty] private bool _isGlobal;
[ObservableProperty] private bool _canCloseMaskToClose;
[ObservableProperty] private bool _canLightDismiss;
[ObservableProperty] private DialogResult? _defaultResult;
[ObservableProperty] private bool _result;
[ObservableProperty] private bool _showMask;
[ObservableProperty] private bool _isModal;
[ObservableProperty] private DateTime? _date;
@@ -36,32 +36,60 @@ public partial class DrawerDemoViewModel: ObservableObject
private async Task ShowDefaultDialog()
{
var vm = new PlainDialogViewModel();
DefaultResult = await Drawer.Show<PlainDialog, PlainDialogViewModel>(
vm,
IsGlobal ? null : "LocalHost",
new DefaultDrawerOptions()
{
Title = "Please select a date",
Position = SelectedPosition,
Buttons = SelectedButton,
CanClickOnMaskToClose = CanCloseMaskToClose,
ShowMask = ShowMask,
});
Date = vm.Date;
if (IsModal)
{
DefaultResult = await Drawer.ShowModal<PlainDialog, PlainDialogViewModel>(
vm,
IsGlobal ? null : "LocalHost",
new DrawerOptions()
{
Title = "Please select a date",
Position = SelectedPosition,
Buttons = SelectedButton,
CanLightDismiss = CanLightDismiss,
});
Date = vm.Date;
}
else
{
Drawer.Show<PlainDialog, PlainDialogViewModel>(
vm,
IsGlobal ? null : "LocalHost",
new DrawerOptions()
{
Title = "Please select a date",
Position = SelectedPosition,
Buttons = SelectedButton,
CanLightDismiss = CanLightDismiss,
});
}
}
private async Task ShowCustomDrawer()
{
var vm = new DialogWithActionViewModel();
Result = await Drawer.ShowCustom<DialogWithAction, DialogWithActionViewModel, bool>(
vm,
IsGlobal ? null : "LocalHost",
new CustomDrawerOptions()
{
Position = SelectedPosition,
CanClickOnMaskToClose = CanCloseMaskToClose,
ShowMask = ShowMask,
});
Date = vm.Date;
if (IsModal)
{
Result = await Drawer.ShowCustomModal<DialogWithAction, DialogWithActionViewModel, bool>(
vm,
IsGlobal ? null : "LocalHost",
new DrawerOptions()
{
Position = SelectedPosition,
CanLightDismiss = CanLightDismiss,
});
Date = vm.Date;
}
else
{
Drawer.ShowCustom<DialogWithAction, DialogWithActionViewModel>(
vm,
IsGlobal ? null : "LocalHost",
new DrawerOptions()
{
Position = SelectedPosition,
CanLightDismiss = CanLightDismiss,
});
}
}
}

View File

@@ -16,81 +16,89 @@
<Design.DataContext>
<vm:MainViewViewModel />
</Design.DataContext>
<Panel>
<Grid
Classes.Blur="{Binding #host.HasModal}"
ColumnDefinitions="Auto, *"
RowDefinitions="Auto, *">
<Grid.Styles>
<Style Selector="Grid.Blur">
<Setter Property="Effect" Value="blur(20)" />
</Style>
</Grid.Styles>
<Border
Grid.RowSpan="2"
Padding="8,4"
VerticalAlignment="Stretch"
Theme="{DynamicResource CardBorder}">
<u:NavigationMenu ItemsSource="{Binding Menus.MenuItems}" ShowCollapseButton="True">
<u:NavigationMenu.Header>
<TextBlock
Classes="H4"
Text="Ursa"
Theme="{DynamicResource TitleTextBlock}" />
</u:NavigationMenu.Header>
<u:NavigationMenu.Icon>
<Image
Width="48"
Height="48"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="../Assets/Ursa.ico" />
</u:NavigationMenu.Icon>
<u:NavigationMenu.ItemTemplate>
<converters:MenuDataTemplateSelector>
<converters:MenuDataTemplateSelector.MenuTemplate>
<DataTemplate DataType="vm:MenuItemViewModel">
<u:NavigationMenuItem
Command="{Binding ActivateCommand}"
Header="{Binding}"
ItemsSource="{Binding Children}">
<u:NavigationMenuItem.HeaderTemplate>
<DataTemplate x:DataType="vm:MenuItemViewModel">
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Text="{Binding MenuHeader}" />
<u:Badge
Margin="8,0,0,0"
HorizontalAlignment="Left"
BadgeContent="{Binding Status}"
CornerPosition="TopRight" />
</StackPanel>
<Grid ColumnDefinitions="Auto, *" RowDefinitions="Auto, *">
<Border
Grid.RowSpan="2"
Padding="8,4"
VerticalAlignment="Stretch"
Theme="{DynamicResource CardBorder}">
<u:NavigationMenu ItemsSource="{Binding Menus.MenuItems}" ShowCollapseButton="True">
<u:NavigationMenu.Header>
<TextBlock
Classes="H4"
Text="Ursa"
Theme="{DynamicResource TitleTextBlock}" />
</u:NavigationMenu.Header>
<u:NavigationMenu.Icon>
<Image
Width="48"
Height="48"
RenderOptions.BitmapInterpolationMode="HighQuality"
Source="../Assets/Ursa.ico" />
</u:NavigationMenu.Icon>
<u:NavigationMenu.ItemTemplate>
<converters:MenuDataTemplateSelector>
<converters:MenuDataTemplateSelector.MenuTemplate>
<DataTemplate DataType="vm:MenuItemViewModel">
<u:NavigationMenuItem
Command="{Binding ActivateCommand}"
Header="{Binding}"
ItemsSource="{Binding Children}">
<u:NavigationMenuItem.HeaderTemplate>
<DataTemplate x:DataType="vm:MenuItemViewModel">
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Text="{Binding MenuHeader}" />
<u:Badge
Margin="8,0,0,0"
HorizontalAlignment="Left"
BadgeContent="{Binding Status}"
CornerPosition="TopRight" />
</StackPanel>
</DataTemplate>
</u:NavigationMenuItem.HeaderTemplate>
<u:NavigationMenuItem.Icon>
<Border
Width="10"
Height="10"
Background="{DynamicResource SemiBlue6}"
CornerRadius="3" />
</u:NavigationMenuItem.Icon>
</u:NavigationMenuItem>
</DataTemplate>
</converters:MenuDataTemplateSelector.MenuTemplate>
<converters:MenuDataTemplateSelector.SeparatorTemplate>
<DataTemplate DataType="vm:MenuItemViewModel">
<u:NavigationMenuSeparator Header="{Binding MenuHeader}" />
</DataTemplate>
</converters:MenuDataTemplateSelector.SeparatorTemplate>
</converters:MenuDataTemplateSelector>
</u:NavigationMenu.ItemTemplate>
</u:NavigationMenu>
</Border>
<u:ThemeToggleButton
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"/>
<ContentControl
Grid.Row="1"
Grid.Column="1"
Margin="12"
Content="{Binding Content}">
<ContentControl.ContentTemplate>
<converters:ViewLocator />
</ContentControl.ContentTemplate>
</ContentControl>
<u:OverlayDialogHost Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" SnapThickness="20"/>
</Grid>
</DataTemplate>
</u:NavigationMenuItem.HeaderTemplate>
<u:NavigationMenuItem.Icon>
<Border
Width="10"
Height="10"
Background="{DynamicResource SemiBlue6}"
CornerRadius="3" />
</u:NavigationMenuItem.Icon>
</u:NavigationMenuItem>
</DataTemplate>
</converters:MenuDataTemplateSelector.MenuTemplate>
<converters:MenuDataTemplateSelector.SeparatorTemplate>
<DataTemplate DataType="vm:MenuItemViewModel">
<u:NavigationMenuSeparator Header="{Binding MenuHeader}" />
</DataTemplate>
</converters:MenuDataTemplateSelector.SeparatorTemplate>
</converters:MenuDataTemplateSelector>
</u:NavigationMenu.ItemTemplate>
</u:NavigationMenu>
</Border>
<u:ThemeToggleButton
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right" />
<ContentControl
Grid.Row="1"
Grid.Column="1"
Margin="12"
Content="{Binding Content}">
<ContentControl.ContentTemplate>
<converters:ViewLocator />
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
<u:OverlayDialogHost Name="host" SnapThickness="20" />
</Panel>
</UserControl>

View File

@@ -8,7 +8,8 @@
Title="Ursa.PrismDialogDemo">
<Grid>
<StackPanel>
<Button Click="Button_OnClick">Show Dialog</Button>
<Button Click="DialogButton_OnClick">Show Dialog</Button>
<Button Click="DrawerButton_OnClick"></Button>
</StackPanel>
<u:OverlayDialogHost/>
</Grid>

View File

@@ -7,15 +7,22 @@ namespace Ursa.PrismDialogDemo;
public partial class MainWindow : Window
{
private IUrsaOverlayDialogService _ursa;
public MainWindow(IUrsaOverlayDialogService ursa)
private IUrsaOverlayDialogService _dialogService;
private IUrsaDrawerService _drawerService;
public MainWindow(IUrsaOverlayDialogService dialogService, IUrsaDrawerService drawerService)
{
InitializeComponent();
_ursa = ursa;
_dialogService = dialogService;
_drawerService = drawerService;
}
private void Button_OnClick(object? sender, RoutedEventArgs e)
private void DialogButton_OnClick(object? sender, RoutedEventArgs e)
{
_ursa.ShowModal("Default", null, null, null);
_dialogService.ShowModal("Default", null, null, null);
}
private void DrawerButton_OnClick(object? sender, RoutedEventArgs e)
{
_drawerService.ShowModal("Default", null, null, null);
}
}

View File

@@ -5,6 +5,8 @@ namespace Ursa.PrismExtension;
public interface IUrsaDrawerService
{
public Task<DialogResult> ShowDrawer(string viewName, object? vm, string? hostId = null, DefaultDrawerOptions? options = null);
public Task<TResult?> ShowCustomDrawer<TResult>(string viewName, object? vm, string? hostId = null, CustomDrawerOptions? options = null);
public void Show(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null);
public void ShowCustom<TResult>(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null);
public Task<DialogResult> ShowModal(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null);
public Task<TResult?> ShowCustomModal<TResult>(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null);
}

View File

@@ -7,16 +7,27 @@ namespace Ursa.PrismExtension;
public class UrsaDrawerService(IContainerExtension container): IUrsaDrawerService
{
public Task<DialogResult> ShowDrawer(string viewName, object? vm, string? hostId = null, DefaultDrawerOptions? options = null)
public void Show(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var v = container.Resolve<Control>(UrsaDialogServiceExtension.UrsaDialogViewPrefix + viewName);
return Drawer.Show(v, vm, hostId, options);
Drawer.Show(v, vm, hostId, options);
}
public Task<TResult?> ShowCustomDrawer<TResult>(string viewName, object? vm, string? hostId = null,
CustomDrawerOptions? options = null)
public void ShowCustom<TResult>(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var v = container.Resolve<Control>(UrsaDialogServiceExtension.UrsaDialogViewPrefix + viewName);
return Drawer.ShowCustom<TResult?>(v, vm, hostId, options);
Drawer.ShowCustom(v, vm, hostId, options);
}
public Task<DialogResult> ShowModal(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var v = container.Resolve<Control>(UrsaDialogServiceExtension.UrsaDialogViewPrefix + viewName);
return Drawer.ShowModal(v, vm, hostId, options);
}
public Task<TResult?> ShowCustomModal<TResult>(string viewName, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var v = container.Resolve<Control>(UrsaDialogServiceExtension.UrsaDialogViewPrefix + viewName);
return Drawer.ShowCustomModal<TResult?>(v, vm, hostId, options);
}
}

View File

@@ -1,8 +1,10 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Irihi.Avalonia.Shared.Helpers;
using Ursa.Common;
using Ursa.Controls.OverlayShared;
@@ -13,12 +15,12 @@ namespace Ursa.Controls;
[TemplatePart(PART_CloseButton, typeof(Button))]
[TemplatePart(PART_TitleArea, typeof(Panel))]
[PseudoClasses(PC_Modal)]
public abstract class DialogControlBase: OverlayFeedbackElement
public abstract class DialogControlBase : OverlayFeedbackElement
{
public const string PART_CloseButton = "PART_CloseButton";
public const string PART_TitleArea = "PART_TitleArea";
public const string PC_Modal = ":modal";
internal HorizontalPosition HorizontalAnchor { get; set; } = HorizontalPosition.Center;
internal VerticalPosition VerticalAnchor { get; set; } = VerticalPosition.Center;
internal HorizontalPosition ActualHorizontalAnchor { get; set; }
@@ -27,19 +29,18 @@ public abstract class DialogControlBase: OverlayFeedbackElement
internal double? VerticalOffset { get; set; }
internal double? HorizontalOffsetRatio { get; set; }
internal double? VerticalOffsetRatio { get; set; }
internal bool CanClickOnMaskToClose { get; set; }
internal bool CanLightDismiss { get; set; }
internal bool CanDragMove { get; set; }
protected internal Button? _closeButton;
private Panel? _titleArea;
internal void SetAsModal(bool modal)
{
PseudoClasses.Set(PC_Modal, modal);
}
public static readonly RoutedEvent<DialogLayerChangeEventArgs> LayerChangedEvent = RoutedEvent.Register<CustomDialogControl, DialogLayerChangeEventArgs>(
nameof(LayerChanged), RoutingStrategies.Bubble);
#region Layer Management
public static readonly RoutedEvent<DialogLayerChangeEventArgs> LayerChangedEvent =
RoutedEvent.Register<CustomDialogControl, DialogLayerChangeEventArgs>(
nameof(LayerChanged), RoutingStrategies.Bubble);
public event EventHandler<DialogLayerChangeEventArgs> LayerChanged
{
add => AddHandler(LayerChangedEvent, value);
@@ -54,35 +55,133 @@ public abstract class DialogControlBase: OverlayFeedbackElement
}
}
#endregion
#region DragMove AttachedPropert
public static readonly AttachedProperty<bool> CanDragMoveProperty =
AvaloniaProperty.RegisterAttached<DialogControlBase, InputElement, bool>("CanDragMove");
public static void SetCanDragMove(InputElement obj, bool value) => obj.SetValue(CanDragMoveProperty, value);
public static bool GetCanDragMove(InputElement obj) => obj.GetValue(CanDragMoveProperty);
private static void OnCanDragMoveChanged(InputElement arg1, AvaloniaPropertyChangedEventArgs<bool> arg2)
{
if (arg2.NewValue.Value)
{
arg1.AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Bubble);
arg1.AddHandler(PointerMovedEvent, OnPointerMoved, RoutingStrategies.Bubble);
arg1.AddHandler(PointerReleasedEvent, OnPointerReleased, RoutingStrategies.Bubble);
}
else
{
arg1.RemoveHandler(PointerPressedEvent, OnPointerPressed);
arg1.RemoveHandler(PointerMovedEvent, OnPointerMoved);
arg1.RemoveHandler(PointerReleasedEvent, OnPointerReleased);
}
void OnPointerPressed(InputElement sender, PointerPressedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
void OnPointerMoved(InputElement sender, PointerEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
void OnPointerReleased(InputElement sender, PointerReleasedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
e.Source = dialog;
}
}
}
#endregion
#region Close AttachedProperty
public static readonly AttachedProperty<bool> CanCloseProperty =
AvaloniaProperty.RegisterAttached<DialogControlBase, InputElement, bool>("CanClose");
public static void SetCanClose(InputElement obj, bool value) => obj.SetValue(CanCloseProperty, value);
public static bool GetCanClose(InputElement obj) => obj.GetValue(CanCloseProperty);
private static void OnCanCloseChanged(InputElement arg1, AvaloniaPropertyChangedEventArgs<bool> arg2)
{
if (arg2.NewValue.Value)
{
arg1.AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Bubble);
}
void OnPointerPressed(InputElement sender, PointerPressedEventArgs e)
{
if (sender.FindLogicalAncestorOfType<DialogControlBase>() is { } dialog)
{
dialog.Close();
}
}
}
#endregion
static DialogControlBase()
{
CanDragMoveProperty.Changed.AddClassHandler<InputElement, bool>(OnCanDragMoveChanged);
CanCloseProperty.Changed.AddClassHandler<InputElement, bool>(OnCanCloseChanged);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_titleArea?.RemoveHandler(PointerMovedEvent, OnTitlePointerMove);
_titleArea?.RemoveHandler(PointerPressedEvent, OnTitlePointerPressed);
_titleArea?.RemoveHandler(PointerReleasedEvent, OnTitlePointerRelease);
_titleArea = e.NameScope.Find<Panel>(PART_TitleArea);
_titleArea?.AddHandler(PointerMovedEvent, OnTitlePointerMove, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerPressedEvent, OnTitlePointerPressed, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerReleasedEvent, OnTitlePointerRelease, RoutingStrategies.Bubble);
if (CanDragMove)
{
_titleArea?.RemoveHandler(PointerMovedEvent, OnTitlePointerMove);
_titleArea?.RemoveHandler(PointerPressedEvent, OnTitlePointerPressed);
_titleArea?.RemoveHandler(PointerReleasedEvent, OnTitlePointerRelease);
_titleArea?.AddHandler(PointerMovedEvent, OnTitlePointerMove, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerPressedEvent, OnTitlePointerPressed, RoutingStrategies.Bubble);
_titleArea?.AddHandler(PointerReleasedEvent, OnTitlePointerRelease, RoutingStrategies.Bubble);
}
else
{
if (_titleArea is not null) _titleArea.IsHitTestVisible = false;
}
Button.ClickEvent.RemoveHandler(OnCloseButtonClick, _closeButton);
_closeButton = e.NameScope.Find<Button>(PART_CloseButton);
Button.ClickEvent.AddHandler(OnCloseButtonClick, _closeButton);
}
private void OnTitlePointerPressed(object sender, PointerPressedEventArgs e)
private void OnTitlePointerPressed(InputElement sender, PointerPressedEventArgs e)
{
e.Source = this;
}
private void OnTitlePointerMove(object sender, PointerEventArgs e)
private void OnTitlePointerMove(InputElement sender, PointerEventArgs e)
{
e.Source = this;
}
private void OnTitlePointerRelease(object sender, PointerReleasedEventArgs e)
private void OnTitlePointerRelease(InputElement sender, PointerReleasedEventArgs e)
{
e.Source = this;
}
private void OnCloseButtonClick(object sender, RoutedEventArgs args) => Close();
internal void SetAsModal(bool modal)
{
PseudoClasses.Set(PC_Modal, modal);
}
}

View File

@@ -17,15 +17,32 @@ public enum VerticalPosition
public class OverlayDialogOptions
{
internal static OverlayDialogOptions Default { get; } = new OverlayDialogOptions();
public bool CanClickOnMaskToClose { get; set; } = false;
public HorizontalPosition HorizontalAnchor { get; set; } = HorizontalPosition.Center;
public VerticalPosition VerticalAnchor { get; set; } = VerticalPosition.Center;
/// <summary>
/// This attribute is only used when HorizontalAnchor is not Center
/// </summary>
public double? HorizontalOffset { get; set; } = null;
/// <summary>
/// This attribute is only used when VerticalAnchor is not Center
/// </summary>
public double? VerticalOffset { get; set; } = null;
/// <summary>
/// Only works for DefaultDialogControl
/// </summary>
public DialogMode Mode { get; set; } = DialogMode.None;
/// <summary>
/// Only works for DefaultDialogControl
/// </summary>
public DialogButton Buttons { get; set; } = DialogButton.OKCancel;
/// <summary>
/// Only works for DefaultDialogControl
/// </summary>
public string? Title { get; set; } = null;
public bool IsCloseButtonVisible { get; set; } = true;
/// <summary>
/// Only works for CustomDialogControl
/// </summary>
public bool ShowCloseButton { get; set; } = true;
public bool CanLightDismiss { get; set; }
public bool CanDragMove { get; set; } = true;
}

View File

@@ -18,7 +18,7 @@ public static class OverlayDialog
DataContext = vm,
};
ConfigureDefaultDialogControl(t, options);
host?.AddDialog(t);
host.AddDialog(t);
}
public static void Show(Control control, object? vm, string? hostId = null,
@@ -32,7 +32,7 @@ public static class OverlayDialog
DataContext = vm,
};
ConfigureDefaultDialogControl(t, options);
host?.AddDialog(t);
host.AddDialog(t);
}
@@ -63,8 +63,8 @@ public static class OverlayDialog
Content = new TView(),
DataContext = vm,
};
ConfigureDialogControl(t, options);
host?.AddDialog(t);
ConfigureCustomDialogControl(t, options);
host.AddDialog(t);
}
public static void ShowCustom(Control control, object? vm, string? hostId = null,
@@ -77,8 +77,8 @@ public static class OverlayDialog
Content = control,
DataContext = vm,
};
ConfigureDialogControl(t, options);
host?.AddDialog(t);
ConfigureCustomDialogControl(t, options);
host.AddDialog(t);
}
public static void ShowCustom(object? vm, string? hostId = null,
@@ -94,7 +94,7 @@ public static class OverlayDialog
Content = view,
DataContext = vm,
};
ConfigureDialogControl(t, options);
ConfigureCustomDialogControl(t, options);
host.AddDialog(t);
}
@@ -110,7 +110,7 @@ public static class OverlayDialog
DataContext = vm,
};
ConfigureDefaultDialogControl(t, options);
host?.AddModalDialog(t);
host.AddModalDialog(t);
return t.ShowAsync<DialogResult>(token);
}
@@ -125,7 +125,7 @@ public static class OverlayDialog
DataContext = vm,
};
ConfigureDefaultDialogControl(t, options);
host?.AddModalDialog(t);
host.AddModalDialog(t);
return t.ShowAsync<DialogResult>(token);
}
@@ -140,8 +140,8 @@ public static class OverlayDialog
Content = new TView(),
DataContext = vm,
};
ConfigureDialogControl(t, options);
host?.AddModalDialog(t);
ConfigureCustomDialogControl(t, options);
host.AddModalDialog(t);
return t.ShowAsync<TResult?>(token);
}
@@ -155,8 +155,8 @@ public static class OverlayDialog
Content = control,
DataContext = vm,
};
ConfigureDialogControl(t, options);
host?.AddModalDialog(t);
ConfigureCustomDialogControl(t, options);
host.AddModalDialog(t);
return t.ShowAsync<TResult?>(token);
}
@@ -173,12 +173,12 @@ public static class OverlayDialog
Content = view,
DataContext = vm,
};
ConfigureDialogControl(t, options);
host?.AddModalDialog(t);
ConfigureCustomDialogControl(t, options);
host.AddModalDialog(t);
return t.ShowAsync<TResult?>(token);
}
private static void ConfigureDialogControl(CustomDialogControl control, OverlayDialogOptions? options)
private static void ConfigureCustomDialogControl(CustomDialogControl control, OverlayDialogOptions? options)
{
options ??= OverlayDialogOptions.Default;
control.HorizontalAnchor = options.HorizontalAnchor;
@@ -189,9 +189,9 @@ public static class OverlayDialog
control.HorizontalAnchor == HorizontalPosition.Center ? null : options.HorizontalOffset;
control.VerticalOffset =
options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset;
control.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
control.IsCloseButtonVisible = options.IsCloseButtonVisible;
control.IsCloseButtonVisible = options.ShowCloseButton;
control.CanLightDismiss = options.CanLightDismiss;
control.CanDragMove = options.CanDragMove;
}
private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options)
@@ -205,11 +205,11 @@ public static class OverlayDialog
control.HorizontalAnchor == HorizontalPosition.Center ? null : options.HorizontalOffset;
control.VerticalOffset =
options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset;
control.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
control.Mode = options.Mode;
control.Buttons = options.Buttons;
control.Title = options.Title;
control.CanLightDismiss = options.CanLightDismiss;
control.CanDragMove = options.CanDragMove;
}

View File

@@ -7,11 +7,11 @@ namespace Ursa.Controls;
public static class Drawer
{
public static Task<DialogResult> Show<TView, TViewModel>(TViewModel vm, string? hostId = null, DefaultDrawerOptions? options = null)
public static void Show<TView, TViewModel>(TViewModel vm, string? hostId = null, DrawerOptions? options = null)
where TView: Control, new()
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(DialogResult));
if (host is null) return;
var drawer = new DefaultDrawerControl()
{
Content = new TView(),
@@ -19,14 +19,13 @@ public static class Drawer
};
ConfigureDefaultDrawer(drawer, options);
host.AddDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static Task<DialogResult> Show(Control control, object? vm, string? hostId = null,
DefaultDrawerOptions? options = null)
public static void Show(Control control, object? vm, string? hostId = null,
DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(DialogResult));
if (host is null) return;
var drawer = new DefaultDrawerControl()
{
Content = control,
@@ -34,13 +33,12 @@ public static class Drawer
};
ConfigureDefaultDrawer(drawer, options);
host.AddDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static Task<DialogResult> Show(object? vm, string? hostId = null, DefaultDrawerOptions? options = null)
public static void Show(object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(DialogResult));
if (host is null) return;
var view = host.GetDataTemplate(vm)?.Build(vm);
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
view.DataContext = vm;
@@ -51,14 +49,60 @@ public static class Drawer
};
ConfigureDefaultDrawer(drawer, options);
host.AddDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static Task<TResult?> ShowCustom<TView, TViewModel, TResult>(TViewModel vm, string? hostId = null, CustomDrawerOptions? options = null)
public static Task<DialogResult> ShowModal<TView, TViewModel>(TViewModel vm, string? hostId = null, DrawerOptions? options = null)
where TView: Control, new()
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(TResult));
if (host is null) return Task.FromResult(DialogResult.None);
var drawer = new DefaultDrawerControl()
{
Content = new TView(),
DataContext = vm,
};
ConfigureDefaultDrawer(drawer, options);
host.AddModalDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static Task<DialogResult> ShowModal(Control control, object? vm, string? hostId = null,
DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(DialogResult.None);
var drawer = new DefaultDrawerControl()
{
Content = control,
DataContext = vm,
};
ConfigureDefaultDrawer(drawer, options);
host.AddModalDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static Task<DialogResult> ShowModal(object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(DialogResult.None);
var view = host.GetDataTemplate(vm)?.Build(vm);
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
view.DataContext = vm;
var drawer = new DefaultDrawerControl()
{
Content = view,
DataContext = vm,
};
ConfigureDefaultDrawer(drawer, options);
host.AddModalDrawer(drawer);
return drawer.ShowAsync<DialogResult>();
}
public static void ShowCustom<TView, TViewModel>(TViewModel vm, string? hostId = null, DrawerOptions? options = null)
where TView: Control, new()
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return;
var dialog = new CustomDrawerControl()
{
Content = new TView(),
@@ -66,13 +110,12 @@ public static class Drawer
};
ConfigureCustomDrawer(dialog, options);
host.AddDrawer(dialog);
return dialog.ShowAsync<TResult>();
}
public static Task<TResult?> ShowCustom<TResult>(Control control, object? vm, string? hostId = null, CustomDrawerOptions? options = null)
public static void ShowCustom(Control control, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(TResult));
if (host is null) return;
var dialog = new CustomDrawerControl()
{
Content = control,
@@ -80,13 +123,12 @@ public static class Drawer
};
ConfigureCustomDrawer(dialog, options);
host.AddDrawer(dialog);
return dialog.ShowAsync<TResult>();
}
public static Task<TResult?> ShowCustom<TResult>(object? vm, string? hostId = null, CustomDrawerOptions? options = null)
public static void ShowCustom(object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult(default(TResult));
if (host is null) return;
var view = host.GetDataTemplate(vm)?.Build(vm);
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
view.DataContext = vm;
@@ -97,17 +139,59 @@ public static class Drawer
};
ConfigureCustomDrawer(dialog, options);
host.AddDrawer(dialog);
return dialog.ShowAsync<TResult>();
}
private static void ConfigureCustomDrawer(CustomDrawerControl drawer, CustomDrawerOptions? options)
public static Task<TResult?> ShowCustomModal<TView, TViewModel, TResult>(TViewModel vm, string? hostId = null, DrawerOptions? options = null)
where TView: Control, new()
{
options ??= CustomDrawerOptions.Default;
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult<TResult?>(default);
var dialog = new CustomDrawerControl()
{
Content = new TView(),
DataContext = vm,
};
ConfigureCustomDrawer(dialog, options);
host.AddModalDrawer(dialog);
return dialog.ShowAsync<TResult?>();
}
public static Task<TResult?> ShowCustomModal<TResult>(Control control, object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult<TResult?>(default);
var dialog = new CustomDrawerControl()
{
Content = control,
DataContext = vm,
};
ConfigureCustomDrawer(dialog, options);
host.AddModalDrawer(dialog);
return dialog.ShowAsync<TResult?>();
}
public static Task<TResult?> ShowCustomModal<TResult>(object? vm, string? hostId = null, DrawerOptions? options = null)
{
var host = OverlayDialogManager.GetHost(hostId);
if (host is null) return Task.FromResult<TResult?>(default);
var view = host.GetDataTemplate(vm)?.Build(vm);
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
view.DataContext = vm;
var dialog = new CustomDrawerControl()
{
Content = view,
DataContext = vm,
};
ConfigureCustomDrawer(dialog, options);
host.AddModalDrawer(dialog);
return dialog.ShowAsync<TResult?>();
}
private static void ConfigureCustomDrawer(CustomDrawerControl drawer, DrawerOptions? options)
{
options ??= DrawerOptions.Default;
drawer.Position = options.Position;
drawer.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
drawer.IsCloseButtonVisible = options.IsCloseButtonVisible;
drawer.ShowMask = options.ShowMask;
drawer.IsCloseButtonVisible = options.ShowCloseButton;
drawer.CanLightDismiss = options.CanLightDismiss;
if (options.Position == Position.Left || options.Position == Position.Right)
{
@@ -121,16 +205,14 @@ public static class Drawer
}
}
private static void ConfigureDefaultDrawer(DefaultDrawerControl drawer, DefaultDrawerOptions? options)
private static void ConfigureDefaultDrawer(DefaultDrawerControl drawer, DrawerOptions? options)
{
options ??= DefaultDrawerOptions.Default;
options ??= DrawerOptions.Default;
drawer.Position = options.Position;
drawer.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
drawer.IsCloseButtonVisible = options.IsCloseButtonVisible;
drawer.CanLightDismiss = options.CanLightDismiss;
drawer.Buttons = options.Buttons;
drawer.Title = options.Title;
drawer.ShowMask = options.ShowMask;
drawer.CanLightDismiss = options.CanLightDismiss;
if (options.Position == Position.Left || options.Position == Position.Right)
{
drawer.MinWidth = options.MinWidth ?? 0.0;

View File

@@ -15,9 +15,7 @@ namespace Ursa.Controls;
[TemplatePart(PART_CloseButton, typeof(Button))]
public abstract class DrawerControlBase: OverlayFeedbackElement
{
public const string PART_CloseButton = "PART_CloseButton";
internal bool CanClickOnMaskToClose { get; set; }
public const string PART_CloseButton = "PART_CloseButton";
protected internal Button? _closeButton;
@@ -50,7 +48,6 @@ public const string PART_CloseButton = "PART_CloseButton";
set => SetValue(IsCloseButtonVisibleProperty, value);
}
protected internal bool ShowMask { get; set; }
protected internal bool CanLightDismiss { get; set; }
static DrawerControlBase()

View File

@@ -1,17 +0,0 @@
using Ursa.Common;
namespace Ursa.Controls.Options;
public class CustomDrawerOptions
{
internal static CustomDrawerOptions Default => new ();
public Position Position { get; set; } = Position.Right;
public bool CanClickOnMaskToClose { get; set; } = true;
public bool CanLightDismiss { get; set; } = false;
public bool ShowMask { get; set; } = true;
public bool IsCloseButtonVisible { get; set; } = true;
public double? MinWidth { get; set; } = null;
public double? MinHeight { get; set; } = null;
public double? MaxWidth { get; set; } = null;
public double? MaxHeight { get; set; } = null;
}

View File

@@ -2,13 +2,11 @@
namespace Ursa.Controls.Options;
public class DefaultDrawerOptions
public class DrawerOptions
{
internal static DefaultDrawerOptions Default => new ();
internal static DrawerOptions Default => new ();
public Position Position { get; set; } = Position.Right;
public bool CanClickOnMaskToClose { get; set; } = true;
public bool CanLightDismiss { get; set; } = false;
public bool ShowMask { get; set; } = true;
public bool CanLightDismiss { get; set; } = true;
public bool IsCloseButtonVisible { get; set; } = true;
public double? MinWidth { get; set; } = null;
public double? MinHeight { get; set; } = null;
@@ -16,4 +14,5 @@ public class DefaultDrawerOptions
public double? MaxHeight { get; set; } = null;
public DialogButton Buttons { get; set; } = DialogButton.OKCancel;
public string? Title { get; set; }
public bool ShowCloseButton { get; set; } = true;
}

View File

@@ -85,14 +85,14 @@ public partial class OverlayDialogHost
PureRectangle? mask = null;
if (control.CanLightDismiss)
{
CreateOverlayMask(false, control.CanLightDismiss);
mask = CreateOverlayMask(false, control.CanLightDismiss);
}
if (mask is not null)
{
Children.Add(mask);
}
this.Children.Add(control);
_layers.Add(new DialogPair(mask, control));
_layers.Add(new DialogPair(mask, control, false));
control.Measure(this.Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetToPosition(control);
@@ -116,8 +116,14 @@ public partial class OverlayDialogHost
if (layer.Mask is not null)
{
await _maskDisappearAnimation.RunAsync(layer.Mask);
Children.Remove(layer.Mask);
if (layer.Modal)
{
_modalCount--;
HasModal = _modalCount > 0;
await _maskDisappearAnimation.RunAsync(layer.Mask);
}
}
ResetZIndices();
@@ -130,7 +136,7 @@ public partial class OverlayDialogHost
/// <param name="control"></param>
internal void AddModalDialog(DialogControlBase control)
{
var mask = CreateOverlayMask(true, control.CanClickOnMaskToClose);
var mask = CreateOverlayMask(true, control.CanLightDismiss);
_layers.Add(new DialogPair(mask, control));
control.SetAsModal(true);
ResetZIndices();
@@ -142,6 +148,8 @@ public partial class OverlayDialogHost
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
_maskAppearAnimation.RunAsync(mask);
_modalCount++;
HasModal = _modalCount > 0;
control.IsClosed = false;
}

View File

@@ -15,14 +15,10 @@ public partial class OverlayDialogHost
internal async void AddDrawer(DrawerControlBase control)
{
PureRectangle? mask = null;
if (control.ShowMask == false && control.CanLightDismiss)
if (control.CanLightDismiss)
{
mask = CreateOverlayMask(false, true);
}
else if (control.ShowMask)
{
mask = CreateOverlayMask(control.ShowMask, control.CanClickOnMaskToClose);
}
_layers.Add(new DialogPair(mask, control));
ResetZIndices();
if(mask is not null)this.Children.Add(mask);
@@ -40,7 +36,21 @@ public partial class OverlayDialogHost
{
await Task.WhenAll(animation.RunAsync(control), _maskAppearAnimation.RunAsync(mask));
}
}
internal async void AddModalDrawer(DrawerControlBase control)
{
PureRectangle? mask = CreateOverlayMask(true, control.CanLightDismiss);
_layers.Add(new DialogPair(mask, control));
this.Children.Add(mask);
this.Children.Add(control);
ResetZIndices();
control.Measure(this.Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetDrawerPosition(control);
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDrawerControlClosing);
var animation = CreateAnimation(control.Bounds.Size, control.Position);
await Task.WhenAll(animation.RunAsync(control), _maskAppearAnimation.RunAsync(mask));
}
private void SetDrawerPosition(DrawerControlBase control)

View File

@@ -24,13 +24,28 @@ public partial class OverlayDialogHost: Canvas
{
internal PureRectangle? Mask;
internal OverlayFeedbackElement Element;
internal bool Modal;
public DialogPair(PureRectangle? mask, OverlayFeedbackElement element)
public DialogPair(PureRectangle? mask, OverlayFeedbackElement element, bool modal = true)
{
Mask = mask;
Element = element;
Modal = modal;
}
}
private int _modalCount = 0;
public static readonly DirectProperty<OverlayDialogHost, bool> HasModalProperty = AvaloniaProperty.RegisterDirect<OverlayDialogHost, bool>(
nameof(HasModal), o => o.HasModal);
private bool _hasModal;
public bool HasModal
{
get => _hasModal;
private set => SetAndRaise(HasModalProperty, ref _hasModal, value);
}
static OverlayDialogHost()
{
@@ -81,7 +96,7 @@ public partial class OverlayDialogHost: Canvas
}
else if(canCloseOnClick)
{
rec.SetCurrentValue(Shape.FillProperty, Brushes.Transparent);
rec.SetCurrentValue(PureRectangle.BackgroundProperty, Brushes.Transparent);
}
if (canCloseOnClick)
{