@@ -1,25 +1,43 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
x:Class="Ursa.Demo.Dialogs.DialogWithAction"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Ursa.Demo.Dialogs"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
x:DataType="local:DialogWithActionViewModel"
|
xmlns:local="clr-namespace:Ursa.Demo.Dialogs"
|
||||||
x:CompileBindings="True"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Background="{DynamicResource SemiYellow1}"
|
HorizontalAlignment="Stretch"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
VerticalAlignment="Stretch"
|
||||||
x:Class="Ursa.Demo.Dialogs.DialogWithAction">
|
d:DesignHeight="450"
|
||||||
<StackPanel Margin="24">
|
d:DesignWidth="800"
|
||||||
<TextBlock FontSize="16" FontWeight="600" Margin="8" Text="{Binding Title}"></TextBlock>
|
x:CompileBindings="True"
|
||||||
<Calendar SelectedDate="{Binding Date}" ></Calendar>
|
x:DataType="local:DialogWithActionViewModel"
|
||||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Spacing="8">
|
Background="{DynamicResource SemiYellow1}"
|
||||||
<Button Content="Dialog" Command="{Binding DialogCommand}"></Button>
|
mc:Ignorable="d">
|
||||||
<Button Content="OK" Command="{Binding OKCommand}"></Button>
|
<Grid Margin="24" RowDefinitions="Auto, *, Auto">
|
||||||
<Button Content="Cancel" Command="{Binding CancelCommand}"></Button>
|
<TextBlock
|
||||||
|
Grid.Row="0"
|
||||||
|
Margin="8"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="600"
|
||||||
|
Text="{Binding Title}" />
|
||||||
|
<Calendar
|
||||||
|
Grid.Row="1"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
SelectedDate="{Binding Date}" />
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Spacing="8">
|
||||||
|
<Button Command="{Binding DialogCommand}" Content="Dialog" />
|
||||||
|
<Button Command="{Binding OKCommand}" Content="OK" />
|
||||||
|
<Button Command="{Binding CancelCommand}" Content="Cancel" />
|
||||||
<ComboBox>
|
<ComboBox>
|
||||||
<ComboBoxItem>A</ComboBoxItem>
|
<ComboBoxItem>A</ComboBoxItem>
|
||||||
<ComboBoxItem>B</ComboBoxItem>
|
<ComboBoxItem>B</ComboBoxItem>
|
||||||
<ComboBoxItem>C</ComboBoxItem>
|
<ComboBoxItem>C</ComboBoxItem>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -9,10 +9,5 @@
|
|||||||
x:Class="Ursa.Demo.Dialogs.PlainDialog">
|
x:Class="Ursa.Demo.Dialogs.PlainDialog">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<Calendar SelectedDate="{Binding Date}" ></Calendar>
|
<Calendar SelectedDate="{Binding Date}" ></Calendar>
|
||||||
<ComboBox>
|
|
||||||
<ComboBoxItem>A</ComboBoxItem>
|
|
||||||
<ComboBoxItem>B</ComboBoxItem>
|
|
||||||
<ComboBoxItem>C</ComboBoxItem>
|
|
||||||
</ComboBox>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public static class MenuKeys
|
|||||||
public const string MenuKeyClassInput = "Class Input";
|
public const string MenuKeyClassInput = "Class Input";
|
||||||
public const string MenuKeyDialog = "Dialog";
|
public const string MenuKeyDialog = "Dialog";
|
||||||
public const string MenuKeyDivider = "Divider";
|
public const string MenuKeyDivider = "Divider";
|
||||||
|
public const string MenuKeyDrawer = "Drawer";
|
||||||
public const string MenuKeyDualBadge = "DualBadge";
|
public const string MenuKeyDualBadge = "DualBadge";
|
||||||
public const string MenuKeyEnumSelector = "EnumSelector";
|
public const string MenuKeyEnumSelector = "EnumSelector";
|
||||||
public const string MenuKeyImageViewer = "ImageViewer";
|
public const string MenuKeyImageViewer = "ImageViewer";
|
||||||
|
|||||||
99
demo/Ursa.Demo/Pages/DrawerDemo.axaml
Normal file
99
demo/Ursa.Demo/Pages/DrawerDemo.axaml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="Ursa.Demo.Pages.DrawerDemo"
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:common="clr-namespace:Ursa.Common;assembly=Ursa"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:u="https://irihi.tech/ursa"
|
||||||
|
xmlns:vm="clr-namespace:Ursa.Demo.ViewModels;assembly=Ursa.Demo"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
x:DataType="vm:DrawerDemoViewModel"
|
||||||
|
mc:Ignorable="d">
|
||||||
|
<Grid ColumnDefinitions="Auto, *">
|
||||||
|
<TabControl Grid.Column="0" Width="300">
|
||||||
|
<TabItem Header="Default">
|
||||||
|
<StackPanel>
|
||||||
|
<u:EnumSelector EnumType="common:Position" Value="{Binding SelectedPosition}" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="Global/Local"
|
||||||
|
IsChecked="{Binding IsGlobal}"
|
||||||
|
OffContent="Local"
|
||||||
|
OnContent="Global" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="ShowMask"
|
||||||
|
IsChecked="{Binding ShowMask}"
|
||||||
|
OffContent="No"
|
||||||
|
OnContent="Yes" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="ClickOnMaskToClose"
|
||||||
|
IsChecked="{Binding CanCloseMaskToClose}"
|
||||||
|
OffContent="No"
|
||||||
|
OnContent="Yes" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="Buttons" />
|
||||||
|
<u:EnumSelector EnumType="{x:Type u:DialogButton}" Value="{Binding SelectedButton}" />
|
||||||
|
</StackPanel>
|
||||||
|
<Button Command="{Binding ShowDialogCommand}" Content="Show Default Drawer" />
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Default Result: " />
|
||||||
|
<Run Text="{Binding DefaultResult}" />
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Dialog Date: " />
|
||||||
|
<Run Text="{Binding Date}" />
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</TabItem>
|
||||||
|
<TabItem Header="Custom">
|
||||||
|
<StackPanel>
|
||||||
|
<u:EnumSelector EnumType="common:Position" Value="{Binding SelectedPosition}" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="Global/Local"
|
||||||
|
IsChecked="{Binding IsGlobal}"
|
||||||
|
OffContent="Local"
|
||||||
|
OnContent="Global" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="ClickOnMaskToClose"
|
||||||
|
IsChecked="{Binding CanCloseMaskToClose}"
|
||||||
|
OffContent="No"
|
||||||
|
OnContent="Yes" />
|
||||||
|
<ToggleSwitch
|
||||||
|
Content="ShowMask"
|
||||||
|
IsChecked="{Binding ShowMask}"
|
||||||
|
OffContent="No"
|
||||||
|
OnContent="Yes" />
|
||||||
|
<Button Command="{Binding ShowCustomDialogCommand}" Content="Show Custom Drawer" />
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Custom Result: " />
|
||||||
|
<Run Text="{Binding Result}" />
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Dialog Date: " />
|
||||||
|
<Run Text="{Binding Date}" />
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
<Grid Grid.Column="1" ClipToBounds="True">
|
||||||
|
<Border
|
||||||
|
BorderBrush="{DynamicResource SemiGrey1}"
|
||||||
|
BorderThickness="1"
|
||||||
|
ClipToBounds="True"
|
||||||
|
CornerRadius="20">
|
||||||
|
<u:OverlayDialogHost HostId="LocalHost">
|
||||||
|
<u:OverlayDialogHost.DialogDataTemplates>
|
||||||
|
<DataTemplate DataType="x:String">
|
||||||
|
<TextBlock
|
||||||
|
Margin="24,24,48,24"
|
||||||
|
Foreground="Red"
|
||||||
|
Text="{Binding Path=.}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</u:OverlayDialogHost.DialogDataTemplates>
|
||||||
|
</u:OverlayDialogHost>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
13
demo/Ursa.Demo/Pages/DrawerDemo.axaml.cs
Normal file
13
demo/Ursa.Demo/Pages/DrawerDemo.axaml.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.Pages;
|
||||||
|
|
||||||
|
public partial class DrawerDemo : UserControl
|
||||||
|
{
|
||||||
|
public DrawerDemo()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<StackPanel HorizontalAlignment="Left" Spacing="16">
|
<StackPanel HorizontalAlignment="Left" Spacing="16">
|
||||||
<ToggleSwitch Name="loading" Content="Toggle Loading" />
|
<ToggleSwitch Name="loading" Content="Toggle Loading" />
|
||||||
<u:EnumSelector Name="placement" EnumType="{x:Type common:IconPlacement}" />
|
<u:EnumSelector Name="placement" EnumType="{x:Type common:Position}" />
|
||||||
<u:IconButton
|
<u:IconButton
|
||||||
Content="Hello World"
|
Content="Hello World"
|
||||||
IconPlacement="{Binding #placement.Value}"
|
IconPlacement="{Binding #placement.Value}"
|
||||||
|
|||||||
67
demo/Ursa.Demo/ViewModels/DrawerDemoViewModel.cs
Normal file
67
demo/Ursa.Demo/ViewModels/DrawerDemoViewModel.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls;
|
||||||
|
using Ursa.Controls.Options;
|
||||||
|
using Ursa.Demo.Dialogs;
|
||||||
|
|
||||||
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
public partial class DrawerDemoViewModel: ObservableObject
|
||||||
|
{
|
||||||
|
public ICommand ShowDialogCommand { get; set; }
|
||||||
|
public ICommand ShowCustomDialogCommand { get; set; }
|
||||||
|
|
||||||
|
[ObservableProperty] private Position _selectedPosition;
|
||||||
|
[ObservableProperty] private DialogButton _selectedButton;
|
||||||
|
[ObservableProperty] private bool _isGlobal;
|
||||||
|
[ObservableProperty] private bool _canCloseMaskToClose;
|
||||||
|
[ObservableProperty] private DialogResult? _defaultResult;
|
||||||
|
[ObservableProperty] private bool _result;
|
||||||
|
[ObservableProperty] private bool _showMask;
|
||||||
|
[ObservableProperty] private DateTime? _date;
|
||||||
|
|
||||||
|
|
||||||
|
public DrawerDemoViewModel()
|
||||||
|
{
|
||||||
|
ShowDialogCommand = new AsyncRelayCommand(ShowDefaultDialog);
|
||||||
|
ShowCustomDialogCommand = new AsyncRelayCommand(ShowCustomDrawer);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ public class MainViewViewModel : ViewModelBase
|
|||||||
MenuKeys.MenuKeyClassInput => new ClassInputDemoViewModel(),
|
MenuKeys.MenuKeyClassInput => new ClassInputDemoViewModel(),
|
||||||
MenuKeys.MenuKeyDialog => new DialogDemoViewModel(),
|
MenuKeys.MenuKeyDialog => new DialogDemoViewModel(),
|
||||||
MenuKeys.MenuKeyDivider => new DividerDemoViewModel(),
|
MenuKeys.MenuKeyDivider => new DividerDemoViewModel(),
|
||||||
|
MenuKeys.MenuKeyDrawer => new DrawerDemoViewModel(),
|
||||||
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
|
MenuKeys.MenuKeyDualBadge => new DualBadgeDemoViewModel(),
|
||||||
MenuKeys.MenuKeyEnumSelector => new EnumSelectorDemoViewModel(),
|
MenuKeys.MenuKeyEnumSelector => new EnumSelectorDemoViewModel(),
|
||||||
MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(),
|
MenuKeys.MenuKeyImageViewer => new ImageViewerDemoViewModel(),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class MenuViewModel: ViewModelBase
|
|||||||
new() { MenuHeader = "Class Input", Key = MenuKeys.MenuKeyClassInput, Status = "New" },
|
new() { MenuHeader = "Class Input", Key = MenuKeys.MenuKeyClassInput, Status = "New" },
|
||||||
new() { MenuHeader = "Dialog", Key = MenuKeys.MenuKeyDialog },
|
new() { MenuHeader = "Dialog", Key = MenuKeys.MenuKeyDialog },
|
||||||
new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider },
|
new() { MenuHeader = "Divider", Key = MenuKeys.MenuKeyDivider },
|
||||||
|
new() { MenuHeader = "Drawer", Key = MenuKeys.MenuKeyDrawer },
|
||||||
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
|
new() { MenuHeader = "DualBadge", Key = MenuKeys.MenuKeyDualBadge },
|
||||||
new() { MenuHeader = "Enum Selector", Key = MenuKeys.MenuKeyEnumSelector },
|
new() { MenuHeader = "Enum Selector", Key = MenuKeys.MenuKeyEnumSelector },
|
||||||
new() { MenuHeader = "Icon Button", Key = MenuKeys.MenuKeyIconButton },
|
new() { MenuHeader = "Icon Button", Key = MenuKeys.MenuKeyIconButton },
|
||||||
|
|||||||
@@ -7,10 +7,16 @@
|
|||||||
<ControlTheme x:Key="{x:Type u:OverlayDialogHost}" TargetType="u:OverlayDialogHost">
|
<ControlTheme x:Key="{x:Type u:OverlayDialogHost}" TargetType="u:OverlayDialogHost">
|
||||||
<Setter Property="OverlayMaskBrush" Value="{DynamicResource OverlayDialogMaskBrush}" />
|
<Setter Property="OverlayMaskBrush" Value="{DynamicResource OverlayDialogMaskBrush}" />
|
||||||
</ControlTheme>
|
</ControlTheme>
|
||||||
<ControlTheme x:Key="{x:Type u:DialogControl}" TargetType="u:DialogControl">
|
<ControlTheme x:Key="{x:Type u:CustomDialogControl}" TargetType="u:CustomDialogControl">
|
||||||
<Setter Property="CornerRadius" Value="12" />
|
<Setter Property="CornerRadius" Value="12" />
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<TransformOperationsTransition Duration="0.2" Property="RenderTransform"/>
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="RenderTransform" Value="scale(1.0)"></Setter>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:DialogControl">
|
<ControlTemplate TargetType="u:CustomDialogControl">
|
||||||
<Border
|
<Border
|
||||||
Margin="8"
|
Margin="8"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
@@ -30,7 +36,7 @@
|
|||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="*, Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="*, Auto">
|
||||||
<Panel
|
<Panel
|
||||||
Name="{x:Static u:DialogControl.PART_TitleArea}"
|
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="2"
|
Grid.ColumnSpan="2"
|
||||||
Background="Transparent" />
|
Background="Transparent" />
|
||||||
@@ -46,12 +52,14 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
<Style Selector="^[IsClosed=True]">
|
||||||
|
<Setter Property="RenderTransform" Value="scale(0.95)"/>
|
||||||
|
</Style>
|
||||||
<Style Selector="^ /template/ Panel#PART_TitleArea">
|
<Style Selector="^ /template/ Panel#PART_TitleArea">
|
||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].CloseDialog}"
|
Command="{Binding $parent[u:DialogControlBase].Close}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
|
||||||
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<PathIcon
|
<PathIcon
|
||||||
@@ -67,7 +75,7 @@
|
|||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
||||||
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
|
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -78,7 +86,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
|
||||||
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
|
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -89,7 +97,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
|
||||||
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
|
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -100,7 +108,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
|
||||||
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
|
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -111,8 +119,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].CloseDialog}"
|
Command="{Binding $parent[u:DialogControlBase].Close}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
|
||||||
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<PathIcon
|
<PathIcon
|
||||||
@@ -128,6 +135,11 @@
|
|||||||
|
|
||||||
<ControlTheme x:Key="{x:Type u:DefaultDialogControl}" TargetType="u:DefaultDialogControl">
|
<ControlTheme x:Key="{x:Type u:DefaultDialogControl}" TargetType="u:DefaultDialogControl">
|
||||||
<Setter Property="CornerRadius" Value="12" />
|
<Setter Property="CornerRadius" Value="12" />
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<TransformOperationsTransition Duration="0.2" Property="RenderTransform"/>
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:DefaultDialogControl">
|
<ControlTemplate TargetType="u:DefaultDialogControl">
|
||||||
<Border
|
<Border
|
||||||
@@ -152,7 +164,7 @@
|
|||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
|
||||||
<Panel
|
<Panel
|
||||||
Name="{x:Static u:DialogControl.PART_TitleArea}"
|
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Background="Transparent" />
|
Background="Transparent" />
|
||||||
@@ -216,6 +228,9 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
<Style Selector="^[IsClosed=True]">
|
||||||
|
<Setter Property="RenderTransform" Value="scale(0.95)"/>
|
||||||
|
</Style>
|
||||||
<Style Selector="^[Mode=None]">
|
<Style Selector="^[Mode=None]">
|
||||||
<Style Selector="^ /template/ PathIcon#PART_Icon">
|
<Style Selector="^ /template/ PathIcon#PART_Icon">
|
||||||
<Setter Property="IsVisible" Value="False" />
|
<Setter Property="IsVisible" Value="False" />
|
||||||
@@ -338,8 +353,7 @@
|
|||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].CloseDialog}"
|
Command="{Binding $parent[u:DialogControlBase].Close}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
|
||||||
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<PathIcon
|
<PathIcon
|
||||||
@@ -355,7 +369,7 @@
|
|||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
||||||
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
|
Header="{DynamicResource STRING_MENU_BRING_FORWARD}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -366,7 +380,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.BringToFront}"
|
||||||
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
|
Header="{DynamicResource STRING_MENU_BRING_TO_FRONT}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -377,7 +391,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.SendBackward}"
|
||||||
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
|
Header="{DynamicResource STRING_MENU_SEND_BACKWARD}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -388,7 +402,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].UpdateLayer}"
|
Command="{Binding $parent[u:DialogControlBase].UpdateLayer}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.SendToBack}"
|
||||||
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
|
Header="{DynamicResource STRING_MENU_SEND_TO_BACK}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
@@ -399,7 +413,7 @@
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding $parent[u:DialogControl].CloseDialog}"
|
Command="{Binding $parent[u:DialogControlBase].Close}"
|
||||||
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
CommandParameter="{x:Static u:DialogLayerChangeType.BringForward}"
|
||||||
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
|
|||||||
167
src/Ursa.Themes.Semi/Controls/Drawer.axaml
Normal file
167
src/Ursa.Themes.Semi/Controls/Drawer.axaml
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<ResourceDictionary xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:u="https://irihi.tech/ursa">
|
||||||
|
<ControlTheme TargetType="u:CustomDrawerControl" x:Key="{x:Type u:CustomDrawerControl}">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="u:CustomDrawerControl">
|
||||||
|
<Border Name="PART_Root"
|
||||||
|
Margin="8 -1 -1 -1"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Classes="Shadow"
|
||||||
|
ClipToBounds="False"
|
||||||
|
CornerRadius="12 0 0 12"
|
||||||
|
BorderThickness="1 0 0 0"
|
||||||
|
IsHitTestVisible="True"
|
||||||
|
Theme="{DynamicResource CardBorder}">
|
||||||
|
<Border ClipToBounds="True" CornerRadius="{Binding #PART_Root.CornerRadius}">
|
||||||
|
<Grid RowDefinitions="Auto, *">
|
||||||
|
<ContentPresenter
|
||||||
|
Name="PART_ContentPresenter"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.RowSpan="2"
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
|
<Grid Grid.Row="0" ColumnDefinitions="*, Auto">
|
||||||
|
<Panel
|
||||||
|
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"
|
||||||
|
Background="Transparent" />
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,24,24,0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
Theme="{DynamicResource CloseButton}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^[Position=Right] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="8 0 0 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="12 0 0 12" />
|
||||||
|
<Setter Property="BorderThickness" Value="1 0 0 0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Left] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 0 8 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="0 12 12 0" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 1 0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Top] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
|
<Setter Property="CornerRadius" Value="0 0 12 12" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 0 1" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Bottom] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 8 0 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="12 12 0 0" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 1 0 0" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
|
||||||
|
<ControlTheme x:Key="{x:Type u:DefaultDrawerControl}" TargetType="u:DefaultDrawerControl">
|
||||||
|
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate TargetType="u:DefaultDrawerControl">
|
||||||
|
<Border Name="PART_Root"
|
||||||
|
Margin="8 -1 -1 -1"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
Classes="Shadow"
|
||||||
|
ClipToBounds="False"
|
||||||
|
CornerRadius="12 0 0 12"
|
||||||
|
BorderThickness="1 0 0 0"
|
||||||
|
IsHitTestVisible="True"
|
||||||
|
Theme="{DynamicResource CardBorder}">
|
||||||
|
<Border ClipToBounds="True" CornerRadius="{Binding #PART_Root.CornerRadius}">
|
||||||
|
<Grid RowDefinitions="Auto, *, Auto">
|
||||||
|
<ScrollViewer Grid.Row="1">
|
||||||
|
<ContentPresenter
|
||||||
|
Name="PART_ContentPresenter"
|
||||||
|
Grid.Row="1"
|
||||||
|
Margin="24,8"
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
|
</ScrollViewer>
|
||||||
|
<Grid Grid.Row="0" ColumnDefinitions=" *, Auto">
|
||||||
|
<TextBlock
|
||||||
|
Name="PART_Title"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="24,24,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="Bold"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
IsVisible="{TemplateBinding Title,
|
||||||
|
Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||||
|
Text="{TemplateBinding Title}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:DrawerControlBase.PART_CloseButton}"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="0,24,24,0"
|
||||||
|
DockPanel.Dock="Right"
|
||||||
|
Theme="{DynamicResource CloseButton}" />
|
||||||
|
</Grid>
|
||||||
|
<StackPanel
|
||||||
|
Grid.Row="2"
|
||||||
|
Margin="24,0,24,24"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:DefaultDialogControl.PART_CancelButton}"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="Tertiary"
|
||||||
|
Content="{DynamicResource STRING_MENU_DIALOG_CANCEL}" />
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:DefaultDialogControl.PART_NoButton}"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="Danger"
|
||||||
|
Content="{DynamicResource STRING_MENU_DIALOG_NO}"
|
||||||
|
Theme="{DynamicResource SolidButton}" />
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:DefaultDialogControl.PART_YesButton}"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="Primary"
|
||||||
|
Content="{DynamicResource STRING_MENU_DIALOG_YES}"
|
||||||
|
Theme="{DynamicResource SolidButton}" />
|
||||||
|
<Button
|
||||||
|
Name="{x:Static u:DefaultDialogControl.PART_OKButton}"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="Primary"
|
||||||
|
Content="{DynamicResource STRING_MENU_DIALOG_OK}"
|
||||||
|
Theme="{DynamicResource SolidButton}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
<Style Selector="^[Position=Right] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="8 0 0 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="12 0 0 12" />
|
||||||
|
<Setter Property="BorderThickness" Value="1 0 0 0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Left] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 0 8 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="0 12 12 0" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 1 0" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Top] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 0 0 8" />
|
||||||
|
<Setter Property="CornerRadius" Value="0 0 12 12" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 0 0 1" />
|
||||||
|
</Style>
|
||||||
|
<Style Selector="^[Position=Bottom] /template/ Border#PART_Root">
|
||||||
|
<Setter Property="Margin" Value="0 8 0 0" />
|
||||||
|
<Setter Property="CornerRadius" Value="12 12 0 0" />
|
||||||
|
<Setter Property="BorderThickness" Value="0 1 0 0" />
|
||||||
|
</Style>
|
||||||
|
</ControlTheme>
|
||||||
|
</ResourceDictionary>
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
<Grid RowDefinitions="Auto, *, Auto">
|
<Grid RowDefinitions="Auto, *, Auto">
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
|
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
|
||||||
<Panel
|
<Panel
|
||||||
Name="{x:Static u:DialogControl.PART_TitleArea}"
|
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
Background="Transparent" />
|
Background="Transparent" />
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
<Style Selector="^ /template/ Panel#PART_TitleArea">
|
<Style Selector="^ /template/ Panel#PART_TitleArea">
|
||||||
<Setter Property="ContextFlyout">
|
<Setter Property="ContextFlyout">
|
||||||
<MenuFlyout>
|
<MenuFlyout>
|
||||||
<MenuItem Command="{Binding $parent[u:DialogControl].CloseDialog}" Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
<MenuItem Command="{Binding $parent[u:CustomDialogControl].CloseDialog}" Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<PathIcon
|
<PathIcon
|
||||||
Width="12"
|
Width="12"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<ResourceInclude Source="Dialog.axaml" />
|
<ResourceInclude Source="Dialog.axaml" />
|
||||||
<ResourceInclude Source="DialogShared.axaml" />
|
<ResourceInclude Source="DialogShared.axaml" />
|
||||||
<ResourceInclude Source="Divider.axaml" />
|
<ResourceInclude Source="Divider.axaml" />
|
||||||
|
<ResourceInclude Source="Drawer.axaml" />
|
||||||
<ResourceInclude Source="DualBadge.axaml" />
|
<ResourceInclude Source="DualBadge.axaml" />
|
||||||
<ResourceInclude Source="EnumSelector.axaml" />
|
<ResourceInclude Source="EnumSelector.axaml" />
|
||||||
<ResourceInclude Source="IconButton.axaml" />
|
<ResourceInclude Source="IconButton.axaml" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Ursa.Common;
|
namespace Ursa.Common;
|
||||||
|
|
||||||
public enum IconPlacement
|
public enum Position
|
||||||
{
|
{
|
||||||
Left,
|
Left,
|
||||||
Top,
|
Top,
|
||||||
39
src/Ursa/Controls/Dialog/CustomDialogControl.cs
Normal file
39
src/Ursa/Controls/Dialog/CustomDialogControl.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Input.GestureRecognizers;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public class CustomDialogControl: DialogControlBase
|
||||||
|
{
|
||||||
|
internal bool IsCloseButtonVisible { get; set; }
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
if (_closeButton is not null)
|
||||||
|
{
|
||||||
|
_closeButton.IsVisible = IsCloseButtonVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (DataContext is IDialogContext context)
|
||||||
|
{
|
||||||
|
context.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnElementClosing(this, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,16 +5,15 @@ using Avalonia.Controls.Primitives;
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Ursa.Common;
|
using Ursa.Common;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
|
||||||
[TemplatePart(PART_OKButton, typeof(Button))]
|
[TemplatePart(PART_OKButton, typeof(Button))]
|
||||||
[TemplatePart(PART_CancelButton, typeof(Button))]
|
[TemplatePart(PART_CancelButton, typeof(Button))]
|
||||||
[TemplatePart(PART_YesButton, typeof(Button))]
|
[TemplatePart(PART_YesButton, typeof(Button))]
|
||||||
[TemplatePart(PART_NoButton, typeof(Button))]
|
[TemplatePart(PART_NoButton, typeof(Button))]
|
||||||
[TemplatePart(PART_TitleArea, typeof(Panel))]
|
public class DefaultDialogControl: DialogControlBase
|
||||||
public class DefaultDialogControl: DialogControl
|
|
||||||
{
|
{
|
||||||
public const string PART_OKButton = "PART_OKButton";
|
public const string PART_OKButton = "PART_OKButton";
|
||||||
public const string PART_CancelButton = "PART_CancelButton";
|
public const string PART_CancelButton = "PART_CancelButton";
|
||||||
@@ -105,35 +104,30 @@ public class DefaultDialogControl: DialogControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVisibility(Button? button, bool visible)
|
|
||||||
{
|
|
||||||
if (button is not null) button.IsVisible = visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DefaultButtonsClose(object sender, RoutedEventArgs args)
|
private void DefaultButtonsClose(object sender, RoutedEventArgs args)
|
||||||
{
|
{
|
||||||
if (sender is Button button)
|
if (sender is Button button)
|
||||||
{
|
{
|
||||||
if (button == _okButton)
|
if (button == _okButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, DialogResult.OK);
|
OnElementClosing(this, DialogResult.OK);
|
||||||
}
|
}
|
||||||
else if (button == _cancelButton)
|
else if (button == _cancelButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, DialogResult.Cancel);
|
OnElementClosing(this, DialogResult.Cancel);
|
||||||
}
|
}
|
||||||
else if (button == _yesButton)
|
else if (button == _yesButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, DialogResult.Yes);
|
OnElementClosing(this, DialogResult.Yes);
|
||||||
}
|
}
|
||||||
else if (button == _noButton)
|
else if (button == _noButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, DialogResult.No);
|
OnElementClosing(this, DialogResult.No);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void CloseDialog()
|
public override void Close()
|
||||||
{
|
{
|
||||||
if (DataContext is IDialogContext context)
|
if (DataContext is IDialogContext context)
|
||||||
{
|
{
|
||||||
@@ -150,7 +144,7 @@ public class DefaultDialogControl: DialogControl
|
|||||||
DialogButton.YesNoCancel => DialogResult.Cancel,
|
DialogButton.YesNoCancel => DialogResult.Cancel,
|
||||||
_ => DialogResult.None
|
_ => DialogResult.None
|
||||||
};
|
};
|
||||||
OnDialogControlClosing(this, result);
|
OnElementClosing(this, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Controls.Metadata;
|
|
||||||
using Avalonia.Controls.Primitives;
|
|
||||||
using Avalonia.Input;
|
|
||||||
using Avalonia.Input.GestureRecognizers;
|
|
||||||
using Avalonia.Interactivity;
|
|
||||||
using Avalonia.Threading;
|
|
||||||
using Ursa.Common;
|
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
|
||||||
|
|
||||||
[TemplatePart(PART_CloseButton, typeof(Button))]
|
|
||||||
[TemplatePart(PART_TitleArea, typeof(Panel))]
|
|
||||||
[PseudoClasses(PC_Modal)]
|
|
||||||
public class DialogControl: ContentControl
|
|
||||||
{
|
|
||||||
public const string PART_CloseButton = "PART_CloseButton";
|
|
||||||
public const string PART_TitleArea = "PART_TitleArea";
|
|
||||||
public const string PC_Modal = ":modal";
|
|
||||||
|
|
||||||
protected internal Button? _closeButton;
|
|
||||||
private Panel? _titleArea;
|
|
||||||
|
|
||||||
internal HorizontalPosition HorizontalAnchor { get; set; } = HorizontalPosition.Center;
|
|
||||||
internal VerticalPosition VerticalAnchor { get; set; } = VerticalPosition.Center;
|
|
||||||
internal HorizontalPosition ActualHorizontalAnchor { get; set; }
|
|
||||||
internal VerticalPosition ActualVerticalAnchor { get; set; }
|
|
||||||
internal double? HorizontalOffset { get; set; }
|
|
||||||
internal double? VerticalOffset { get; set; }
|
|
||||||
internal double? HorizontalOffsetRatio { get; set; }
|
|
||||||
internal double? VerticalOffsetRatio { get; set; }
|
|
||||||
internal bool CanClickOnMaskToClose { get; set; }
|
|
||||||
internal bool IsCloseButtonVisible { get; set; }
|
|
||||||
|
|
||||||
public event EventHandler<DialogLayerChangeEventArgs>? LayerChanged;
|
|
||||||
public event EventHandler<object?>? DialogControlClosing;
|
|
||||||
|
|
||||||
static DialogControl()
|
|
||||||
{
|
|
||||||
DataContextProperty.Changed.AddClassHandler<DialogControl, object?>((o, e) => o.OnDataContextChange(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDataContextChange(AvaloniaPropertyChangedEventArgs<object?> args)
|
|
||||||
{
|
|
||||||
if (args.OldValue.Value is IDialogContext oldContext)
|
|
||||||
{
|
|
||||||
oldContext.RequestClose-= OnContextRequestClose;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.NewValue.Value is IDialogContext newContext)
|
|
||||||
{
|
|
||||||
newContext.RequestClose += OnContextRequestClose;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnApplyTemplate(e);
|
|
||||||
EventHelper.UnregisterClickEvent(OnCloseButtonClick, _closeButton);
|
|
||||||
|
|
||||||
_titleArea?.RemoveHandler(PointerMovedEvent, OnTitlePointerMove);
|
|
||||||
_titleArea?.RemoveHandler(PointerPressedEvent, OnTitlePointerPressed);
|
|
||||||
_titleArea?.RemoveHandler(PointerReleasedEvent, OnTitlePointerRelease);
|
|
||||||
|
|
||||||
_closeButton = e.NameScope.Find<Button>(PART_CloseButton);
|
|
||||||
_titleArea = e.NameScope.Find<Panel>(PART_TitleArea);
|
|
||||||
if (_closeButton is not null)
|
|
||||||
{
|
|
||||||
_closeButton.IsVisible = IsCloseButtonVisible;
|
|
||||||
}
|
|
||||||
_titleArea?.AddHandler(PointerMovedEvent, OnTitlePointerMove, RoutingStrategies.Bubble);
|
|
||||||
_titleArea?.AddHandler(PointerPressedEvent, OnTitlePointerPressed, RoutingStrategies.Bubble);
|
|
||||||
_titleArea?.AddHandler(PointerReleasedEvent, OnTitlePointerRelease, RoutingStrategies.Bubble);
|
|
||||||
EventHelper.RegisterClickEvent(OnCloseButtonClick, _closeButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTitlePointerPressed(object sender, PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
e.Source = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTitlePointerMove(object sender, PointerEventArgs e)
|
|
||||||
{
|
|
||||||
e.Source = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTitlePointerRelease(object sender, PointerReleasedEventArgs e)
|
|
||||||
{
|
|
||||||
e.Source = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Task<T?> ShowAsync<T>(CancellationToken? token = default)
|
|
||||||
{
|
|
||||||
var tcs = new TaskCompletionSource<T?>();
|
|
||||||
token?.Register(() =>
|
|
||||||
{
|
|
||||||
Dispatcher.UIThread.Invoke(CloseDialog);
|
|
||||||
});
|
|
||||||
void OnCloseHandler(object sender, object? args)
|
|
||||||
{
|
|
||||||
if (args is T result)
|
|
||||||
{
|
|
||||||
tcs.SetResult(result);
|
|
||||||
DialogControlClosing-= OnCloseHandler;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tcs.SetResult(default(T));
|
|
||||||
DialogControlClosing-= OnCloseHandler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.DialogControlClosing += OnCloseHandler;
|
|
||||||
return tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCloseButtonClick(object sender, RoutedEventArgs args) => CloseDialog();
|
|
||||||
|
|
||||||
private void OnContextRequestClose(object sender, object? args)
|
|
||||||
{
|
|
||||||
DialogControlClosing?.Invoke(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void UpdateLayer(object? o)
|
|
||||||
{
|
|
||||||
if (o is DialogLayerChangeType t)
|
|
||||||
{
|
|
||||||
LayerChanged?.Invoke(this, new DialogLayerChangeEventArgs(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used for inherited classes to invoke the DialogControlClosing event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="args"></param>
|
|
||||||
protected internal virtual void OnDialogControlClosing(object sender, object? args)
|
|
||||||
{
|
|
||||||
DialogControlClosing?.Invoke(this, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetAsModal(bool modal)
|
|
||||||
{
|
|
||||||
PseudoClasses.Set(PC_Modal, modal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is exposed internally for closing the dialog from neither context nor closing by clicking on the close button.
|
|
||||||
/// It is also exposed to be bound to context flyout.
|
|
||||||
/// It is virtual because inherited classes may return a different result by default.
|
|
||||||
/// </summary>
|
|
||||||
internal virtual void CloseDialog()
|
|
||||||
{
|
|
||||||
if (DataContext is IDialogContext context)
|
|
||||||
{
|
|
||||||
context.Close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DialogControlClosing?.Invoke(this, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
87
src/Ursa/Controls/Dialog/DialogControlBase.cs
Normal file
87
src/Ursa/Controls/Dialog/DialogControlBase.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
[TemplatePart(PART_CloseButton, typeof(Button))]
|
||||||
|
[TemplatePart(PART_TitleArea, typeof(Panel))]
|
||||||
|
[PseudoClasses(PC_Modal)]
|
||||||
|
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; }
|
||||||
|
internal VerticalPosition ActualVerticalAnchor { get; set; }
|
||||||
|
internal double? HorizontalOffset { get; set; }
|
||||||
|
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; }
|
||||||
|
|
||||||
|
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);
|
||||||
|
public event EventHandler<DialogLayerChangeEventArgs> LayerChanged
|
||||||
|
{
|
||||||
|
add => AddHandler(LayerChangedEvent, value);
|
||||||
|
remove => RemoveHandler(LayerChangedEvent, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLayer(object? o)
|
||||||
|
{
|
||||||
|
if (o is DialogLayerChangeType t)
|
||||||
|
{
|
||||||
|
RaiseEvent(new DialogLayerChangeEventArgs(LayerChangedEvent, t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
EventHelper.UnregisterClickEvent(OnCloseButtonClick, _closeButton);
|
||||||
|
_closeButton = e.NameScope.Find<Button>(PART_CloseButton);
|
||||||
|
EventHelper.RegisterClickEvent(OnCloseButtonClick, _closeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTitlePointerPressed(object sender, PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
e.Source = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTitlePointerMove(object sender, PointerEventArgs e)
|
||||||
|
{
|
||||||
|
e.Source = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTitlePointerRelease(object sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
e.Source = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCloseButtonClick(object sender, RoutedEventArgs args) => Close();
|
||||||
|
}
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
public class DialogLayerChangeEventArgs
|
public class DialogLayerChangeEventArgs: RoutedEventArgs
|
||||||
{
|
{
|
||||||
public DialogLayerChangeType ChangeType { get; }
|
public DialogLayerChangeType ChangeType { get; }
|
||||||
|
|
||||||
public DialogLayerChangeEventArgs(DialogLayerChangeType type)
|
public DialogLayerChangeEventArgs(DialogLayerChangeType type)
|
||||||
{
|
{
|
||||||
ChangeType = type;
|
ChangeType = type;
|
||||||
}
|
}
|
||||||
|
public DialogLayerChangeEventArgs(RoutedEvent routedEvent, DialogLayerChangeType type): base(routedEvent)
|
||||||
|
{
|
||||||
|
ChangeType = type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DialogLayerChangeType
|
public enum DialogLayerChangeType
|
||||||
|
|||||||
@@ -26,4 +26,6 @@ public class OverlayDialogOptions
|
|||||||
public DialogButton Buttons { get; set; } = DialogButton.OKCancel;
|
public DialogButton Buttons { get; set; } = DialogButton.OKCancel;
|
||||||
public string? Title { get; set; } = null;
|
public string? Title { get; set; } = null;
|
||||||
public bool IsCloseButtonVisible { get; set; } = true;
|
public bool IsCloseButtonVisible { get; set; } = true;
|
||||||
|
|
||||||
|
public bool CanLightDismiss { get; set; }
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ public static class OverlayDialog
|
|||||||
{
|
{
|
||||||
var host = OverlayDialogManager.GetHost(hostId);
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
if (host is null) return;
|
if (host is null) return;
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = new TView(),
|
Content = new TView(),
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -72,7 +72,7 @@ public static class OverlayDialog
|
|||||||
{
|
{
|
||||||
var host = OverlayDialogManager.GetHost(hostId);
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
if (host is null) return;
|
if (host is null) return;
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = control,
|
Content = control,
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -89,7 +89,7 @@ public static class OverlayDialog
|
|||||||
var view = host.GetDataTemplate(vm)?.Build(vm);
|
var view = host.GetDataTemplate(vm)?.Build(vm);
|
||||||
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
|
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
|
||||||
view.DataContext = vm;
|
view.DataContext = vm;
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = view,
|
Content = view,
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -135,7 +135,7 @@ public static class OverlayDialog
|
|||||||
{
|
{
|
||||||
var host = OverlayDialogManager.GetHost(hostId);
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
if (host is null) return Task.FromResult(default(TResult));
|
if (host is null) return Task.FromResult(default(TResult));
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = new TView(),
|
Content = new TView(),
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -150,7 +150,7 @@ public static class OverlayDialog
|
|||||||
{
|
{
|
||||||
var host = OverlayDialogManager.GetHost(hostId);
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
if (host is null) return Task.FromResult(default(TResult));
|
if (host is null) return Task.FromResult(default(TResult));
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = control,
|
Content = control,
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -168,7 +168,7 @@ public static class OverlayDialog
|
|||||||
var view = host.GetDataTemplate(vm)?.Build(vm);
|
var view = host.GetDataTemplate(vm)?.Build(vm);
|
||||||
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
|
if (view is null) view = new ContentControl() { Padding = new Thickness(24) };
|
||||||
view.DataContext = vm;
|
view.DataContext = vm;
|
||||||
var t = new DialogControl()
|
var t = new CustomDialogControl()
|
||||||
{
|
{
|
||||||
Content = view,
|
Content = view,
|
||||||
DataContext = vm,
|
DataContext = vm,
|
||||||
@@ -178,7 +178,7 @@ public static class OverlayDialog
|
|||||||
return t.ShowAsync<TResult?>(token);
|
return t.ShowAsync<TResult?>(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureDialogControl(DialogControl control, OverlayDialogOptions? options)
|
private static void ConfigureDialogControl(CustomDialogControl control, OverlayDialogOptions? options)
|
||||||
{
|
{
|
||||||
options ??= OverlayDialogOptions.Default;
|
options ??= OverlayDialogOptions.Default;
|
||||||
control.HorizontalAnchor = options.HorizontalAnchor;
|
control.HorizontalAnchor = options.HorizontalAnchor;
|
||||||
@@ -191,6 +191,7 @@ public static class OverlayDialog
|
|||||||
options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset;
|
options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset;
|
||||||
control.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
|
control.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
|
||||||
control.IsCloseButtonVisible = options.IsCloseButtonVisible;
|
control.IsCloseButtonVisible = options.IsCloseButtonVisible;
|
||||||
|
control.CanLightDismiss = options.CanLightDismiss;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options)
|
private static void ConfigureDefaultDialogControl(DefaultDialogControl control, OverlayDialogOptions? options)
|
||||||
@@ -208,6 +209,7 @@ public static class OverlayDialog
|
|||||||
control.Mode = options.Mode;
|
control.Mode = options.Mode;
|
||||||
control.Buttons = options.Buttons;
|
control.Buttons = options.Buttons;
|
||||||
control.Title = options.Title;
|
control.Title = options.Title;
|
||||||
|
control.CanLightDismiss = options.CanLightDismiss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
27
src/Ursa/Controls/Drawer/CustomDrawerControl.cs
Normal file
27
src/Ursa/Controls/Drawer/CustomDrawerControl.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public class CustomDrawerControl: DrawerControlBase
|
||||||
|
{
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
if (_closeButton is not null)
|
||||||
|
{
|
||||||
|
_closeButton.IsVisible = IsCloseButtonVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (DataContext is IDialogContext context)
|
||||||
|
{
|
||||||
|
context.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnElementClosing(this, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
148
src/Ursa/Controls/Drawer/DefaultDrawerControl.cs
Normal file
148
src/Ursa/Controls/Drawer/DefaultDrawerControl.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
[TemplatePart(PART_YesButton, typeof(Button))]
|
||||||
|
[TemplatePart(PART_NoButton, typeof(Button))]
|
||||||
|
[TemplatePart(PART_OKButton, typeof(Button))]
|
||||||
|
[TemplatePart(PART_CancelButton, typeof(Button))]
|
||||||
|
public class DefaultDrawerControl: DrawerControlBase
|
||||||
|
{
|
||||||
|
public const string PART_YesButton = "PART_YesButton";
|
||||||
|
public const string PART_NoButton = "PART_NoButton";
|
||||||
|
public const string PART_OKButton = "PART_OKButton";
|
||||||
|
public const string PART_CancelButton = "PART_CancelButton";
|
||||||
|
|
||||||
|
private Button? _yesButton;
|
||||||
|
private Button? _noButton;
|
||||||
|
private Button? _okButton;
|
||||||
|
private Button? _cancelButton;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<DialogButton> ButtonsProperty = AvaloniaProperty.Register<DefaultDrawerControl, DialogButton>(
|
||||||
|
nameof(Buttons), DialogButton.OKCancel);
|
||||||
|
|
||||||
|
public DialogButton Buttons
|
||||||
|
{
|
||||||
|
get => GetValue(ButtonsProperty);
|
||||||
|
set => SetValue(ButtonsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<DialogMode> ModeProperty = AvaloniaProperty.Register<DefaultDrawerControl, DialogMode>(
|
||||||
|
nameof(Mode), DialogMode.None);
|
||||||
|
|
||||||
|
public DialogMode Mode
|
||||||
|
{
|
||||||
|
get => GetValue(ModeProperty);
|
||||||
|
set => SetValue(ModeProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string?> TitleProperty = AvaloniaProperty.Register<DefaultDrawerControl, string?>(
|
||||||
|
nameof(Title));
|
||||||
|
|
||||||
|
public string? Title
|
||||||
|
{
|
||||||
|
get => GetValue(TitleProperty);
|
||||||
|
set => SetValue(TitleProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
EventHelper.UnregisterClickEvent(OnDefaultButtonClick, _yesButton, _noButton, _okButton, _cancelButton);
|
||||||
|
_yesButton = e.NameScope.Find<Button>(PART_YesButton);
|
||||||
|
_noButton = e.NameScope.Find<Button>(PART_NoButton);
|
||||||
|
_okButton = e.NameScope.Find<Button>(PART_OKButton);
|
||||||
|
_cancelButton = e.NameScope.Find<Button>(PART_CancelButton);
|
||||||
|
EventHelper.RegisterClickEvent(OnDefaultButtonClick, _yesButton, _noButton, _okButton, _cancelButton);
|
||||||
|
SetButtonVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetButtonVisibility()
|
||||||
|
{
|
||||||
|
bool isCloseButtonVisible = DataContext is IDialogContext || Buttons != DialogButton.YesNo;
|
||||||
|
SetVisibility(_closeButton, isCloseButtonVisible);
|
||||||
|
switch (Buttons)
|
||||||
|
{
|
||||||
|
case DialogButton.None:
|
||||||
|
SetVisibility(_okButton, false);
|
||||||
|
SetVisibility(_cancelButton, false);
|
||||||
|
SetVisibility(_yesButton, false);
|
||||||
|
SetVisibility(_noButton, false);
|
||||||
|
break;
|
||||||
|
case DialogButton.OK:
|
||||||
|
SetVisibility(_okButton, true);
|
||||||
|
SetVisibility(_cancelButton, false);
|
||||||
|
SetVisibility(_yesButton, false);
|
||||||
|
SetVisibility(_noButton, false);
|
||||||
|
break;
|
||||||
|
case DialogButton.OKCancel:
|
||||||
|
SetVisibility(_okButton, true);
|
||||||
|
SetVisibility(_cancelButton, true);
|
||||||
|
SetVisibility(_yesButton, false);
|
||||||
|
SetVisibility(_noButton, false);
|
||||||
|
break;
|
||||||
|
case DialogButton.YesNo:
|
||||||
|
SetVisibility(_okButton, false);
|
||||||
|
SetVisibility(_cancelButton, false);
|
||||||
|
SetVisibility(_yesButton, true);
|
||||||
|
SetVisibility(_noButton, true);
|
||||||
|
break;
|
||||||
|
case DialogButton.YesNoCancel:
|
||||||
|
SetVisibility(_okButton, false);
|
||||||
|
SetVisibility(_cancelButton, true);
|
||||||
|
SetVisibility(_yesButton, true);
|
||||||
|
SetVisibility(_noButton, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDefaultButtonClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button button)
|
||||||
|
{
|
||||||
|
if (button == _okButton)
|
||||||
|
{
|
||||||
|
OnElementClosing(this, DialogResult.OK);
|
||||||
|
}
|
||||||
|
else if (button == _cancelButton)
|
||||||
|
{
|
||||||
|
OnElementClosing(this, DialogResult.Cancel);
|
||||||
|
}
|
||||||
|
else if (button == _yesButton)
|
||||||
|
{
|
||||||
|
OnElementClosing(this, DialogResult.Yes);
|
||||||
|
}
|
||||||
|
else if (button == _noButton)
|
||||||
|
{
|
||||||
|
OnElementClosing(this, DialogResult.No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (DataContext is IDialogContext context)
|
||||||
|
{
|
||||||
|
context.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DialogResult result = Buttons switch
|
||||||
|
{
|
||||||
|
DialogButton.None => DialogResult.None,
|
||||||
|
DialogButton.OK => DialogResult.OK,
|
||||||
|
DialogButton.OKCancel => DialogResult.Cancel,
|
||||||
|
DialogButton.YesNo => DialogResult.No,
|
||||||
|
DialogButton.YesNoCancel => DialogResult.Cancel,
|
||||||
|
_ => DialogResult.None
|
||||||
|
};
|
||||||
|
RaiseEvent(new ResultEventArgs(ClosedEvent, result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
145
src/Ursa/Controls/Drawer/Drawer.cs
Normal file
145
src/Ursa/Controls/Drawer/Drawer.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls.Options;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public static class Drawer
|
||||||
|
{
|
||||||
|
public static Task<DialogResult> Show<TView, TViewModel>(TViewModel vm, string? hostId = null, DefaultDrawerOptions? options = null)
|
||||||
|
where TView: Control, new()
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(DialogResult));
|
||||||
|
var drawer = new DefaultDrawerControl()
|
||||||
|
{
|
||||||
|
Content = new TView(),
|
||||||
|
DataContext = vm,
|
||||||
|
};
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(DialogResult));
|
||||||
|
var drawer = new DefaultDrawerControl()
|
||||||
|
{
|
||||||
|
Content = control,
|
||||||
|
DataContext = vm,
|
||||||
|
};
|
||||||
|
ConfigureDefaultDrawer(drawer, options);
|
||||||
|
host.AddDrawer(drawer);
|
||||||
|
return drawer.ShowAsync<DialogResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<DialogResult> Show(object? vm, string? hostId = null, DefaultDrawerOptions? options = null)
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(DialogResult));
|
||||||
|
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.AddDrawer(drawer);
|
||||||
|
return drawer.ShowAsync<DialogResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<TResult?> ShowCustom<TView, TViewModel, TResult>(TViewModel vm, string? hostId = null, CustomDrawerOptions? options = null)
|
||||||
|
where TView: Control, new()
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(TResult));
|
||||||
|
var dialog = new CustomDrawerControl()
|
||||||
|
{
|
||||||
|
Content = new TView(),
|
||||||
|
DataContext = vm,
|
||||||
|
};
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(TResult));
|
||||||
|
var dialog = new CustomDrawerControl()
|
||||||
|
{
|
||||||
|
Content = control,
|
||||||
|
DataContext = vm,
|
||||||
|
};
|
||||||
|
ConfigureCustomDrawer(dialog, options);
|
||||||
|
host.AddDrawer(dialog);
|
||||||
|
return dialog.ShowAsync<TResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<TResult?> ShowCustom<TResult>(object? vm, string? hostId = null, CustomDrawerOptions? options = null)
|
||||||
|
{
|
||||||
|
var host = OverlayDialogManager.GetHost(hostId);
|
||||||
|
if (host is null) return Task.FromResult(default(TResult));
|
||||||
|
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.AddDrawer(dialog);
|
||||||
|
return dialog.ShowAsync<TResult>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void ConfigureCustomDrawer(CustomDrawerControl drawer, CustomDrawerOptions? options)
|
||||||
|
{
|
||||||
|
options ??= CustomDrawerOptions.Default;
|
||||||
|
drawer.Position = options.Position;
|
||||||
|
drawer.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
|
||||||
|
drawer.IsCloseButtonVisible = options.IsCloseButtonVisible;
|
||||||
|
drawer.ShowMask = options.ShowMask;
|
||||||
|
drawer.CanLightDismiss = options.CanLightDismiss;
|
||||||
|
if (options.Position == Position.Left || options.Position == Position.Right)
|
||||||
|
{
|
||||||
|
drawer.MinWidth = options.MinWidth ?? 0.0;
|
||||||
|
drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity;
|
||||||
|
}
|
||||||
|
if (options.Position is Position.Top or Position.Bottom)
|
||||||
|
{
|
||||||
|
drawer.MinHeight = options.MinHeight ?? 0.0;
|
||||||
|
drawer.MaxHeight = options.MaxHeight ?? double.PositiveInfinity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConfigureDefaultDrawer(DefaultDrawerControl drawer, DefaultDrawerOptions? options)
|
||||||
|
{
|
||||||
|
options ??= DefaultDrawerOptions.Default;
|
||||||
|
drawer.Position = options.Position;
|
||||||
|
drawer.CanClickOnMaskToClose = options.CanClickOnMaskToClose;
|
||||||
|
drawer.IsCloseButtonVisible = options.IsCloseButtonVisible;
|
||||||
|
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;
|
||||||
|
drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity;
|
||||||
|
}
|
||||||
|
if (options.Position is Position.Top or Position.Bottom)
|
||||||
|
{
|
||||||
|
drawer.MinHeight = options.MinHeight ?? 0.0;
|
||||||
|
drawer.MaxHeight = options.MaxHeight ?? double.PositiveInfinity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/Ursa/Controls/Drawer/DrawerControlBase.cs
Normal file
97
src/Ursa/Controls/Drawer/DrawerControlBase.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Metadata;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
|
protected internal Button? _closeButton;
|
||||||
|
|
||||||
|
public static readonly StyledProperty<Position> PositionProperty =
|
||||||
|
AvaloniaProperty.Register<DrawerControlBase, Position>(
|
||||||
|
nameof(Position), defaultValue: Position.Right);
|
||||||
|
|
||||||
|
public Position Position
|
||||||
|
{
|
||||||
|
get => GetValue(PositionProperty);
|
||||||
|
set => SetValue(PositionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsOpenProperty = AvaloniaProperty.Register<DrawerControlBase, bool>(
|
||||||
|
nameof(IsOpen));
|
||||||
|
|
||||||
|
public bool IsOpen
|
||||||
|
{
|
||||||
|
get => GetValue(IsOpenProperty);
|
||||||
|
set => SetValue(IsOpenProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsCloseButtonVisibleProperty =
|
||||||
|
AvaloniaProperty.Register<DrawerControlBase, bool>(
|
||||||
|
nameof(IsCloseButtonVisible), defaultValue: true);
|
||||||
|
|
||||||
|
public bool IsCloseButtonVisible
|
||||||
|
{
|
||||||
|
get => GetValue(IsCloseButtonVisibleProperty);
|
||||||
|
set => SetValue(IsCloseButtonVisibleProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal bool ShowMask { get; set; }
|
||||||
|
protected internal bool CanLightDismiss { get; set; }
|
||||||
|
|
||||||
|
static DrawerControlBase()
|
||||||
|
{
|
||||||
|
DataContextProperty.Changed.AddClassHandler<DrawerControlBase, object?>((o, e) => o.OnDataContextChange(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnApplyTemplate(e);
|
||||||
|
EventHelper.UnregisterClickEvent(OnCloseButtonClick, _closeButton);
|
||||||
|
_closeButton = e.NameScope.Find<Button>(PART_CloseButton);
|
||||||
|
EventHelper.RegisterClickEvent(OnCloseButtonClick, _closeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChange(AvaloniaPropertyChangedEventArgs<object?> args)
|
||||||
|
{
|
||||||
|
if(args.OldValue.Value is IDialogContext oldContext)
|
||||||
|
{
|
||||||
|
oldContext.RequestClose -= OnContextRequestClose;
|
||||||
|
}
|
||||||
|
if(args.NewValue.Value is IDialogContext newContext)
|
||||||
|
{
|
||||||
|
newContext.RequestClose += OnContextRequestClose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnContextRequestClose(object sender, object? e)
|
||||||
|
{
|
||||||
|
RaiseEvent(new ResultEventArgs(ClosedEvent, e));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCloseButtonClick(object sender, RoutedEventArgs e) => Close();
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (DataContext is IDialogContext context)
|
||||||
|
{
|
||||||
|
context.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RaiseEvent(new ResultEventArgs(ClosedEvent, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Ursa/Controls/Drawer/Options/CustomDrawerOptions.cs
Normal file
17
src/Ursa/Controls/Drawer/Options/CustomDrawerOptions.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
19
src/Ursa/Controls/Drawer/Options/DefaultDrawerOptions.cs
Normal file
19
src/Ursa/Controls/Drawer/Options/DefaultDrawerOptions.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Ursa.Common;
|
||||||
|
|
||||||
|
namespace Ursa.Controls.Options;
|
||||||
|
|
||||||
|
public class DefaultDrawerOptions
|
||||||
|
{
|
||||||
|
internal static DefaultDrawerOptions 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;
|
||||||
|
public DialogButton Buttons { get; set; } = DialogButton.OKCancel;
|
||||||
|
public string? Title { get; set; }
|
||||||
|
}
|
||||||
@@ -44,10 +44,10 @@ public class IconButton: Button
|
|||||||
set => SetValue(IsLoadingProperty, value);
|
set => SetValue(IsLoadingProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<IconPlacement> IconPlacementProperty = AvaloniaProperty.Register<IconButton, IconPlacement>(
|
public static readonly StyledProperty<Position> IconPlacementProperty = AvaloniaProperty.Register<IconButton, Position>(
|
||||||
nameof(IconPlacement), defaultValue: IconPlacement.Left);
|
nameof(IconPlacement), defaultValue: Position.Left);
|
||||||
|
|
||||||
public IconPlacement IconPlacement
|
public Position IconPlacement
|
||||||
{
|
{
|
||||||
get => GetValue(IconPlacementProperty);
|
get => GetValue(IconPlacementProperty);
|
||||||
set => SetValue(IconPlacementProperty, value);
|
set => SetValue(IconPlacementProperty, value);
|
||||||
@@ -55,7 +55,7 @@ public class IconButton: Button
|
|||||||
|
|
||||||
static IconButton()
|
static IconButton()
|
||||||
{
|
{
|
||||||
IconPlacementProperty.Changed.AddClassHandler<IconButton, IconPlacement>((o, e) =>
|
IconPlacementProperty.Changed.AddClassHandler<IconButton, Position>((o, e) =>
|
||||||
{
|
{
|
||||||
o.SetPlacement(e.NewValue.Value, o.Icon);
|
o.SetPlacement(e.NewValue.Value, o.Icon);
|
||||||
});
|
});
|
||||||
@@ -71,7 +71,7 @@ public class IconButton: Button
|
|||||||
SetPlacement(IconPlacement, Icon);
|
SetPlacement(IconPlacement, Icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetPlacement(IconPlacement placement, object? icon)
|
private void SetPlacement(Position placement, object? icon)
|
||||||
{
|
{
|
||||||
if (icon is null)
|
if (icon is null)
|
||||||
{
|
{
|
||||||
@@ -83,9 +83,9 @@ public class IconButton: Button
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PseudoClasses.Set(PC_Empty, false);
|
PseudoClasses.Set(PC_Empty, false);
|
||||||
PseudoClasses.Set(PC_Left, placement == IconPlacement.Left);
|
PseudoClasses.Set(PC_Left, placement == Position.Left);
|
||||||
PseudoClasses.Set(PC_Right, placement == IconPlacement.Right);
|
PseudoClasses.Set(PC_Right, placement == Position.Right);
|
||||||
PseudoClasses.Set(PC_Top, placement == IconPlacement.Top);
|
PseudoClasses.Set(PC_Top, placement == Position.Top);
|
||||||
PseudoClasses.Set(PC_Bottom, placement == IconPlacement.Bottom);
|
PseudoClasses.Set(PC_Bottom, placement == Position.Bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
src/Ursa/Controls/Layout/DefaultDialogLayout.cs
Normal file
8
src/Ursa/Controls/Layout/DefaultDialogLayout.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Avalonia.Controls.Primitives;
|
||||||
|
|
||||||
|
namespace Ursa.Controls.Layout;
|
||||||
|
|
||||||
|
public class DefaultDialogLayout: TemplatedControl
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
@@ -12,12 +13,11 @@ namespace Ursa.Controls;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The messageBox used to display in OverlayDialogHost.
|
/// The messageBox used to display in OverlayDialogHost.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[TemplatePart(PART_CloseButton, typeof(Button))]
|
|
||||||
[TemplatePart(PART_NoButton, typeof(Button))]
|
[TemplatePart(PART_NoButton, typeof(Button))]
|
||||||
[TemplatePart(PART_OKButton, typeof(Button))]
|
[TemplatePart(PART_OKButton, typeof(Button))]
|
||||||
[TemplatePart(PART_CancelButton, typeof(Button))]
|
[TemplatePart(PART_CancelButton, typeof(Button))]
|
||||||
[TemplatePart(PART_YesButton, typeof(Button))]
|
[TemplatePart(PART_YesButton, typeof(Button))]
|
||||||
public class MessageBoxControl: DialogControl
|
public class MessageBoxControl: DialogControlBase
|
||||||
{
|
{
|
||||||
public const string PART_YesButton = "PART_YesButton";
|
public const string PART_YesButton = "PART_YesButton";
|
||||||
public const string PART_NoButton = "PART_NoButton";
|
public const string PART_NoButton = "PART_NoButton";
|
||||||
@@ -80,19 +80,19 @@ public class MessageBoxControl: DialogControl
|
|||||||
{
|
{
|
||||||
if (button == _okButton)
|
if (button == _okButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, MessageBoxResult.OK);
|
OnElementClosing(this, MessageBoxResult.OK);
|
||||||
}
|
}
|
||||||
else if (button == _cancelButton)
|
else if (button == _cancelButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, MessageBoxResult.Cancel);
|
OnElementClosing(this, MessageBoxResult.Cancel);
|
||||||
}
|
}
|
||||||
else if (button == _yesButton)
|
else if (button == _yesButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, MessageBoxResult.Yes);
|
OnElementClosing(this, MessageBoxResult.Yes);
|
||||||
}
|
}
|
||||||
else if (button == _noButton)
|
else if (button == _noButton)
|
||||||
{
|
{
|
||||||
OnDialogControlClosing(this, MessageBoxResult.No);
|
OnElementClosing(this, MessageBoxResult.No);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,12 +128,7 @@ public class MessageBoxControl: DialogControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetVisibility(Button? button, bool visible)
|
public override void Close()
|
||||||
{
|
|
||||||
if (button is not null) button.IsVisible = visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void CloseDialog()
|
|
||||||
{
|
{
|
||||||
MessageBoxResult result = Buttons switch
|
MessageBoxResult result = Buttons switch
|
||||||
{
|
{
|
||||||
@@ -143,6 +138,6 @@ public class MessageBoxControl: DialogControl
|
|||||||
MessageBoxButton.YesNoCancel => MessageBoxResult.Cancel,
|
MessageBoxButton.YesNoCancel => MessageBoxResult.Cancel,
|
||||||
_ => MessageBoxResult.None
|
_ => MessageBoxResult.None
|
||||||
};
|
};
|
||||||
OnDialogControlClosing(this, result);
|
OnElementClosing(this, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,97 +1,27 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Animation;
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Animation.Easings;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Styling;
|
||||||
using Avalonia.Utilities;
|
using Avalonia.Utilities;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Ursa.Controls.Shapes;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
public class OverlayDialogHost : Canvas
|
public partial class OverlayDialogHost
|
||||||
{
|
{
|
||||||
private readonly List<DialogControl> _dialogs = new();
|
|
||||||
private readonly List<DialogControl> _modalDialogs = new();
|
|
||||||
private readonly List<Border> _masks = new();
|
|
||||||
|
|
||||||
public string? HostId { get; set; }
|
|
||||||
|
|
||||||
private Point _lastPoint;
|
private Point _lastPoint;
|
||||||
|
|
||||||
|
|
||||||
public DataTemplates DialogDataTemplates { get; set; } = new DataTemplates();
|
|
||||||
public Thickness SnapThickness { get; set; } = new Thickness(0);
|
public Thickness SnapThickness { get; set; } = new Thickness(0);
|
||||||
|
|
||||||
public static readonly StyledProperty<IBrush?> OverlayMaskBrushProperty =
|
private static void ResetDialogPosition(DialogControlBase control, Size newSize)
|
||||||
AvaloniaProperty.Register<OverlayDialogHost, IBrush?>(
|
|
||||||
nameof(OverlayMaskBrush));
|
|
||||||
|
|
||||||
public IBrush? OverlayMaskBrush
|
|
||||||
{
|
|
||||||
get => GetValue(OverlayMaskBrushProperty);
|
|
||||||
set => SetValue(OverlayMaskBrushProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Border CreateOverlayMask(bool canCloseOnClick)
|
|
||||||
{
|
|
||||||
Border border = new()
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
|
||||||
VerticalAlignment = VerticalAlignment.Stretch,
|
|
||||||
Width = this.Bounds.Width,
|
|
||||||
Height = this.Bounds.Height,
|
|
||||||
[!BackgroundProperty] = this[!OverlayMaskBrushProperty],
|
|
||||||
IsVisible = true,
|
|
||||||
};
|
|
||||||
if (canCloseOnClick)
|
|
||||||
{
|
|
||||||
border.AddHandler(PointerReleasedEvent, ClickBorderToCloseDialog);
|
|
||||||
}
|
|
||||||
return border;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClickBorderToCloseDialog(object sender, PointerReleasedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is Border border)
|
|
||||||
{
|
|
||||||
int i = _masks.IndexOf(border);
|
|
||||||
DialogControl dialog = _modalDialogs[i];
|
|
||||||
dialog.CloseDialog();
|
|
||||||
border.RemoveHandler(PointerReleasedEvent, ClickBorderToCloseDialog);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnAttachedToVisualTree(e);
|
|
||||||
OverlayDialogManager.RegisterHost(this, HostId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override void OnSizeChanged(SizeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnSizeChanged(e);
|
|
||||||
for (int i = 0; i < _masks.Count; i++)
|
|
||||||
{
|
|
||||||
_masks[i].Width = this.Bounds.Width;
|
|
||||||
_masks[i].Height = this.Bounds.Height;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldSize = e.PreviousSize;
|
|
||||||
var newSize = e.NewSize;
|
|
||||||
foreach (var dialog in _dialogs)
|
|
||||||
{
|
|
||||||
ResetDialogPosition(dialog, oldSize, newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var modalDialog in _modalDialogs)
|
|
||||||
{
|
|
||||||
ResetDialogPosition(modalDialog, oldSize, newSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetDialogPosition(DialogControl control, Size oldSize, Size newSize)
|
|
||||||
{
|
{
|
||||||
var width = newSize.Width - control.Bounds.Width;
|
var width = newSize.Width - control.Bounds.Width;
|
||||||
var height = newSize.Height - control.Bounds.Height;
|
var height = newSize.Height - control.Bounds.Height;
|
||||||
@@ -117,15 +47,9 @@ public class OverlayDialogHost : Canvas
|
|||||||
SetTop(control, Math.Max(0.0, newTop));
|
SetTop(control, Math.Max(0.0, newTop));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
OverlayDialogManager.UnregisterHost(HostId);
|
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPointerMoved(PointerEventArgs e)
|
protected override void OnPointerMoved(PointerEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is DialogControl item)
|
if (e.Source is DialogControlBase item)
|
||||||
{
|
{
|
||||||
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
|
||||||
{
|
{
|
||||||
@@ -142,7 +66,7 @@ public class OverlayDialogHost : Canvas
|
|||||||
|
|
||||||
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is DialogControl item)
|
if (e.Source is DialogControlBase item)
|
||||||
{
|
{
|
||||||
_lastPoint = e.GetPosition(item);
|
_lastPoint = e.GetPosition(item);
|
||||||
}
|
}
|
||||||
@@ -150,49 +74,52 @@ public class OverlayDialogHost : Canvas
|
|||||||
|
|
||||||
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
protected override void OnPointerReleased(PointerReleasedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is DialogControl item)
|
if (e.Source is DialogControlBase item)
|
||||||
{
|
{
|
||||||
AnchorDialog(item);
|
AnchorAndUpdatePositionInfo(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddDialog(DialogControl control)
|
internal void AddDialog(DialogControlBase control)
|
||||||
{
|
{
|
||||||
|
PureRectangle? mask = null;
|
||||||
|
if (control.CanLightDismiss)
|
||||||
|
{
|
||||||
|
CreateOverlayMask(false, control.CanLightDismiss);
|
||||||
|
}
|
||||||
|
if (mask is not null)
|
||||||
|
{
|
||||||
|
Children.Add(mask);
|
||||||
|
}
|
||||||
this.Children.Add(control);
|
this.Children.Add(control);
|
||||||
_dialogs.Add(control);
|
_layers.Add(new DialogPair(mask, control));
|
||||||
control.Measure(this.Bounds.Size);
|
control.Measure(this.Bounds.Size);
|
||||||
control.Arrange(new Rect(control.DesiredSize));
|
control.Arrange(new Rect(control.DesiredSize));
|
||||||
SetToPosition(control);
|
SetToPosition(control);
|
||||||
control.DialogControlClosing += OnDialogControlClosing;
|
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
||||||
control.LayerChanged += OnDialogLayerChanged;
|
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
||||||
ResetZIndices();
|
ResetZIndices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDialogControlClosing(object sender, object? e)
|
private async void OnDialogControlClosing(object sender, object? e)
|
||||||
{
|
{
|
||||||
if (sender is DialogControl control)
|
if (sender is DialogControlBase control)
|
||||||
{
|
{
|
||||||
|
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||||
|
if (layer is null) return;
|
||||||
|
_layers.Remove(layer);
|
||||||
|
|
||||||
|
control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
||||||
|
control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
||||||
|
|
||||||
Children.Remove(control);
|
Children.Remove(control);
|
||||||
control.DialogControlClosing -= OnDialogControlClosing;
|
|
||||||
control.LayerChanged -= OnDialogLayerChanged;
|
if (layer.Mask is not null)
|
||||||
if (_dialogs.Contains(control))
|
|
||||||
{
|
{
|
||||||
_dialogs.Remove(control);
|
await _maskDisappearAnimation.RunAsync(layer.Mask);
|
||||||
}
|
Children.Remove(layer.Mask);
|
||||||
else if (_modalDialogs.Contains(control))
|
|
||||||
{
|
|
||||||
_modalDialogs.Remove(control);
|
|
||||||
if (_masks.Count > 0)
|
|
||||||
{
|
|
||||||
var last = _masks.Last();
|
|
||||||
this.Children.Remove(last);
|
|
||||||
_masks.Remove(last);
|
|
||||||
if (_masks.Count > 0)
|
|
||||||
{
|
|
||||||
_masks.Last().IsVisible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetZIndices();
|
ResetZIndices();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,94 +128,64 @@ public class OverlayDialogHost : Canvas
|
|||||||
/// Add a dialog as a modal dialog to the host
|
/// Add a dialog as a modal dialog to the host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="control"></param>
|
/// <param name="control"></param>
|
||||||
internal void AddModalDialog(DialogControl control)
|
internal void AddModalDialog(DialogControlBase control)
|
||||||
{
|
{
|
||||||
var mask = CreateOverlayMask(control.CanClickOnMaskToClose);
|
var mask = CreateOverlayMask(true, control.CanClickOnMaskToClose);
|
||||||
_masks.Add(mask);
|
_layers.Add(new DialogPair(mask, control));
|
||||||
_modalDialogs.Add(control);
|
|
||||||
control.SetAsModal(true);
|
control.SetAsModal(true);
|
||||||
for (int i = 0; i < _masks.Count-1; i++)
|
|
||||||
{
|
|
||||||
_masks[i].Opacity = 0.5;
|
|
||||||
}
|
|
||||||
ResetZIndices();
|
ResetZIndices();
|
||||||
this.Children.Add(mask);
|
this.Children.Add(mask);
|
||||||
this.Children.Add(control);
|
this.Children.Add(control);
|
||||||
control.Measure(this.Bounds.Size);
|
control.Measure(this.Bounds.Size);
|
||||||
control.Arrange(new Rect(control.DesiredSize));
|
control.Arrange(new Rect(control.DesiredSize));
|
||||||
SetToPosition(control);
|
SetToPosition(control);
|
||||||
control.DialogControlClosing += OnDialogControlClosing;
|
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
||||||
control.LayerChanged += OnDialogLayerChanged;
|
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
||||||
|
_maskAppearAnimation.RunAsync(mask);
|
||||||
|
control.IsClosed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle dialog layer change event
|
// Handle dialog layer change event
|
||||||
private void OnDialogLayerChanged(object sender, DialogLayerChangeEventArgs e)
|
private void OnDialogLayerChanged(object sender, DialogLayerChangeEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is not DialogControl control)
|
if (sender is not DialogControlBase control)
|
||||||
return;
|
return;
|
||||||
if (!_dialogs.Contains(control))
|
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||||
return;
|
if (layer is null) return;
|
||||||
int index = _dialogs.IndexOf(control);
|
int index = _layers.IndexOf(layer);
|
||||||
_dialogs.Remove(control);
|
_layers.Remove(layer);
|
||||||
int newIndex = index;
|
int newIndex = index;
|
||||||
switch (e.ChangeType)
|
switch (e.ChangeType)
|
||||||
{
|
{
|
||||||
case DialogLayerChangeType.BringForward:
|
case DialogLayerChangeType.BringForward:
|
||||||
newIndex = MathUtilities.Clamp(index + 1, 0, _dialogs.Count);
|
newIndex = MathUtilities.Clamp(index + 1, 0, _layers.Count);
|
||||||
break;
|
break;
|
||||||
case DialogLayerChangeType.SendBackward:
|
case DialogLayerChangeType.SendBackward:
|
||||||
newIndex = MathUtilities.Clamp(index - 1, 0, _dialogs.Count);
|
newIndex = MathUtilities.Clamp(index - 1, 0, _layers.Count);
|
||||||
break;
|
break;
|
||||||
case DialogLayerChangeType.BringToFront:
|
case DialogLayerChangeType.BringToFront:
|
||||||
newIndex = _dialogs.Count;
|
newIndex = _layers.Count;
|
||||||
break;
|
break;
|
||||||
case DialogLayerChangeType.SendToBack:
|
case DialogLayerChangeType.SendToBack:
|
||||||
newIndex = 0;
|
newIndex = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dialogs.Insert(newIndex, control);
|
_layers.Insert(newIndex, layer);
|
||||||
for (int i = 0; i < _dialogs.Count; i++)
|
ResetZIndices();
|
||||||
{
|
|
||||||
_dialogs[i].ZIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _masks.Count * 2; i += 2)
|
|
||||||
{
|
|
||||||
_masks[i].ZIndex = _dialogs.Count + i;
|
|
||||||
_modalDialogs[i].ZIndex = _dialogs.Count + i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetZIndices()
|
private void SetToPosition(DialogControlBase? control)
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
for ( int i = 0; i< _dialogs.Count; i++)
|
|
||||||
{
|
|
||||||
_dialogs[i].ZIndex = index;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
for(int i = 0; i< _masks.Count; i++)
|
|
||||||
{
|
|
||||||
_masks[i].ZIndex = index;
|
|
||||||
index++;
|
|
||||||
_modalDialogs[i].ZIndex = index;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetToPosition(DialogControl? control)
|
|
||||||
{
|
{
|
||||||
if (control is null) return;
|
if (control is null) return;
|
||||||
double left = GetLeftPosition(control);
|
double left = GetLeftPosition(control);
|
||||||
double top = GetTopPosition(control);
|
double top = GetTopPosition(control);
|
||||||
SetLeft(control, left);
|
SetLeft(control, left);
|
||||||
SetTop(control, top);
|
SetTop(control, top);
|
||||||
AnchorDialog(control);
|
AnchorAndUpdatePositionInfo(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AnchorDialog(DialogControl control)
|
private void AnchorAndUpdatePositionInfo(DialogControlBase control)
|
||||||
{
|
{
|
||||||
control.ActualHorizontalAnchor = HorizontalPosition.Center;
|
control.ActualHorizontalAnchor = HorizontalPosition.Center;
|
||||||
control.ActualVerticalAnchor = VerticalPosition.Center;
|
control.ActualVerticalAnchor = VerticalPosition.Center;
|
||||||
@@ -329,7 +226,7 @@ public class OverlayDialogHost : Canvas
|
|||||||
control.VerticalOffsetRatio = (top + bottom) == 0 ? 0 : top / (top + bottom);
|
control.VerticalOffsetRatio = (top + bottom) == 0 ? 0 : top / (top + bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GetLeftPosition(DialogControl control)
|
private double GetLeftPosition(DialogControlBase control)
|
||||||
{
|
{
|
||||||
double left = 0;
|
double left = 0;
|
||||||
double offset = Math.Max(0, control.HorizontalOffset ?? 0);
|
double offset = Math.Max(0, control.HorizontalOffset ?? 0);
|
||||||
@@ -357,7 +254,7 @@ public class OverlayDialogHost : Canvas
|
|||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double GetTopPosition(DialogControl control)
|
private double GetTopPosition(DialogControlBase control)
|
||||||
{
|
{
|
||||||
double top = 0;
|
double top = 0;
|
||||||
double offset = Math.Max(0, control.VerticalOffset ?? 0);
|
double offset = Math.Max(0, control.VerticalOffset ?? 0);
|
||||||
@@ -380,22 +277,5 @@ public class OverlayDialogHost : Canvas
|
|||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDataTemplate? GetDataTemplate(object? o)
|
|
||||||
{
|
|
||||||
if (o is null) return null;
|
|
||||||
IDataTemplate? result = null;
|
|
||||||
var templates = this.DialogDataTemplates;
|
|
||||||
result = templates.FirstOrDefault(a => a.Match(o));
|
|
||||||
if (result != null) return result;
|
|
||||||
var keys = this.Resources.Keys;
|
|
||||||
foreach (var key in keys)
|
|
||||||
{
|
|
||||||
if (Resources.TryGetValue(key, out var value) && value is IDataTemplate t)
|
|
||||||
{
|
|
||||||
result = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
152
src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs
Normal file
152
src/Ursa/Controls/OverlayShared/OverlayDialogHost.Drawer.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Animation.Easings;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using Ursa.Common;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Ursa.Controls.Shapes;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public partial class OverlayDialogHost
|
||||||
|
{
|
||||||
|
internal async void AddDrawer(DrawerControlBase control)
|
||||||
|
{
|
||||||
|
PureRectangle? mask = null;
|
||||||
|
if (control.ShowMask == false && 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);
|
||||||
|
this.Children.Add(control);
|
||||||
|
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, true);
|
||||||
|
if (mask is null)
|
||||||
|
{
|
||||||
|
await animation.RunAsync(control);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Task.WhenAll(animation.RunAsync(control), _maskAppearAnimation.RunAsync(mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetDrawerPosition(DrawerControlBase control)
|
||||||
|
{
|
||||||
|
if(control.Position is Position.Left or Position.Right)
|
||||||
|
{
|
||||||
|
control.Height = this.Bounds.Height;
|
||||||
|
}
|
||||||
|
if(control.Position is Position.Top or Position.Bottom)
|
||||||
|
{
|
||||||
|
control.Width = this.Bounds.Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ResetDrawerPosition(DrawerControlBase control, Size newSize)
|
||||||
|
{
|
||||||
|
if (control.Position == Position.Right)
|
||||||
|
{
|
||||||
|
control.Height = newSize.Height;
|
||||||
|
SetLeft(control, newSize.Width - control.Bounds.Width);
|
||||||
|
}
|
||||||
|
else if (control.Position == Position.Left)
|
||||||
|
{
|
||||||
|
control.Height = newSize.Height;
|
||||||
|
SetLeft(control, 0);
|
||||||
|
}
|
||||||
|
else if (control.Position == Position.Top)
|
||||||
|
{
|
||||||
|
control.Width = newSize.Width;
|
||||||
|
SetTop(control, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
control.Width = newSize.Width;
|
||||||
|
SetTop(control, newSize.Height-control.Bounds.Height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Animation CreateAnimation(Size elementBounds, Position position, bool appear = true)
|
||||||
|
{
|
||||||
|
// left or top.
|
||||||
|
double source = 0;
|
||||||
|
double target = 0;
|
||||||
|
if (position == Position.Left)
|
||||||
|
{
|
||||||
|
source = appear ? -elementBounds.Width : 0;
|
||||||
|
target = appear ? 0 : -elementBounds.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position == Position.Right)
|
||||||
|
{
|
||||||
|
source = appear ? Bounds.Width : Bounds.Width - elementBounds.Width;
|
||||||
|
target = appear ? Bounds.Width - elementBounds.Width : Bounds.Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position == Position.Top)
|
||||||
|
{
|
||||||
|
source = appear ? -elementBounds.Height : 0;
|
||||||
|
target = appear ? 0 : -elementBounds.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position == Position.Bottom)
|
||||||
|
{
|
||||||
|
source = appear ? Bounds.Height : Bounds.Height - elementBounds.Height;
|
||||||
|
target = appear ? Bounds.Height - elementBounds.Height : Bounds.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetProperty = position==Position.Left || position==Position.Right ? Canvas.LeftProperty : Canvas.TopProperty;
|
||||||
|
var animation = new Animation();
|
||||||
|
animation.Easing = new CubicEaseOut();
|
||||||
|
animation.FillMode = FillMode.Forward;
|
||||||
|
var keyFrame1 = new KeyFrame(){ Cue = new Cue(0.0) };
|
||||||
|
keyFrame1.Setters.Add(new Setter()
|
||||||
|
{ Property = targetProperty, Value = source });
|
||||||
|
var keyFrame2 = new KeyFrame() { Cue = new Cue(1.0) };
|
||||||
|
keyFrame2.Setters.Add(new Setter()
|
||||||
|
{ Property = targetProperty, Value = target });
|
||||||
|
animation.Children.Add(keyFrame1);
|
||||||
|
animation.Children.Add(keyFrame2);
|
||||||
|
animation.Duration = TimeSpan.FromSeconds(0.3);
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnDrawerControlClosing(object sender, ResultEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is DrawerControlBase control)
|
||||||
|
{
|
||||||
|
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||||
|
if(layer is null) return;
|
||||||
|
_layers.Remove(layer);
|
||||||
|
control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
||||||
|
control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
||||||
|
if (layer.Mask is not null)
|
||||||
|
{
|
||||||
|
layer.Mask.RemoveHandler(PointerPressedEvent, ClickMaskToCloseDialog);
|
||||||
|
var disappearAnimation = CreateAnimation(control.Bounds.Size, control.Position, false);
|
||||||
|
await Task.WhenAll(disappearAnimation.RunAsync(control), _maskDisappearAnimation.RunAsync(layer.Mask));
|
||||||
|
Children.Remove(layer.Mask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var disappearAnimation = CreateAnimation(control.Bounds.Size, control.Position, false);
|
||||||
|
await disappearAnimation.RunAsync(control);
|
||||||
|
}
|
||||||
|
Children.Remove(control);
|
||||||
|
ResetZIndices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs
Normal file
176
src/Ursa/Controls/OverlayShared/OverlayDialogHost.Shared.cs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Animation;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Ursa.Controls.OverlayShared;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Media.Immutable;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using Ursa.Controls.Shapes;
|
||||||
|
|
||||||
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
|
public partial class OverlayDialogHost: Canvas
|
||||||
|
{
|
||||||
|
private static readonly Animation _maskAppearAnimation;
|
||||||
|
private static readonly Animation _maskDisappearAnimation;
|
||||||
|
|
||||||
|
private readonly List<DialogPair> _layers = new List<DialogPair>();
|
||||||
|
|
||||||
|
private class DialogPair
|
||||||
|
{
|
||||||
|
internal PureRectangle? Mask;
|
||||||
|
internal OverlayFeedbackElement Element;
|
||||||
|
|
||||||
|
public DialogPair(PureRectangle? mask, OverlayFeedbackElement element)
|
||||||
|
{
|
||||||
|
Mask = mask;
|
||||||
|
Element = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static OverlayDialogHost()
|
||||||
|
{
|
||||||
|
ClipToBoundsProperty.OverrideDefaultValue<OverlayDialogHost>(true);
|
||||||
|
_maskAppearAnimation = CreateOpacityAnimation(true);
|
||||||
|
_maskDisappearAnimation = CreateOpacityAnimation(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Animation CreateOpacityAnimation(bool appear)
|
||||||
|
{
|
||||||
|
var animation = new Animation();
|
||||||
|
animation.FillMode = FillMode.Forward;
|
||||||
|
var keyFrame1 = new KeyFrame{ Cue = new Cue(0.0) };
|
||||||
|
keyFrame1.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 0.0 : 1.0 });
|
||||||
|
var keyFrame2 = new KeyFrame{ Cue = new Cue(1.0) };
|
||||||
|
keyFrame2.Setters.Add(new Setter() { Property = OpacityProperty, Value = appear ? 1.0 : 0.0 });
|
||||||
|
animation.Children.Add(keyFrame1);
|
||||||
|
animation.Children.Add(keyFrame2);
|
||||||
|
animation.Duration = TimeSpan.FromSeconds(0.2);
|
||||||
|
return animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? HostId { get; set; }
|
||||||
|
|
||||||
|
public DataTemplates DialogDataTemplates { get; set; } = new DataTemplates();
|
||||||
|
|
||||||
|
public static readonly StyledProperty<IBrush?> OverlayMaskBrushProperty =
|
||||||
|
AvaloniaProperty.Register<OverlayDialogHost, IBrush?>(
|
||||||
|
nameof(OverlayMaskBrush));
|
||||||
|
|
||||||
|
public IBrush? OverlayMaskBrush
|
||||||
|
{
|
||||||
|
get => GetValue(OverlayMaskBrushProperty);
|
||||||
|
set => SetValue(OverlayMaskBrushProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PureRectangle CreateOverlayMask(bool modal, bool canCloseOnClick)
|
||||||
|
{
|
||||||
|
PureRectangle rec = new()
|
||||||
|
{
|
||||||
|
Width = this.Bounds.Width,
|
||||||
|
Height = this.Bounds.Height,
|
||||||
|
IsVisible = true,
|
||||||
|
};
|
||||||
|
if (modal)
|
||||||
|
{
|
||||||
|
rec[!PureRectangle.BackgroundProperty] = this[!OverlayMaskBrushProperty];
|
||||||
|
}
|
||||||
|
else if(canCloseOnClick)
|
||||||
|
{
|
||||||
|
rec.SetCurrentValue(Shape.FillProperty, Brushes.Transparent);
|
||||||
|
}
|
||||||
|
if (canCloseOnClick)
|
||||||
|
{
|
||||||
|
rec.AddHandler(PointerReleasedEvent, ClickMaskToCloseDialog);
|
||||||
|
}
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClickMaskToCloseDialog(object sender, PointerReleasedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is PureRectangle border)
|
||||||
|
{
|
||||||
|
var layer = _layers.FirstOrDefault(a => a.Mask == border);
|
||||||
|
if (layer is not null)
|
||||||
|
{
|
||||||
|
layer.Element.Close();
|
||||||
|
border.RemoveHandler(PointerReleasedEvent, ClickMaskToCloseDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnAttachedToVisualTree(e);
|
||||||
|
OverlayDialogManager.RegisterHost(this, HostId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
OverlayDialogManager.UnregisterHost(HostId);
|
||||||
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected sealed override void OnSizeChanged(SizeChangedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnSizeChanged(e);
|
||||||
|
for (int i = 0; i < _layers.Count; i++)
|
||||||
|
{
|
||||||
|
if (_layers[i].Mask is { } rect)
|
||||||
|
{
|
||||||
|
rect.Width = this.Bounds.Width;
|
||||||
|
rect.Height = this.Bounds.Height;
|
||||||
|
}
|
||||||
|
if (_layers[i].Element is DialogControlBase d)
|
||||||
|
{
|
||||||
|
ResetDialogPosition(d, e.NewSize);
|
||||||
|
}
|
||||||
|
else if (_layers[i].Element is DrawerControlBase drawer)
|
||||||
|
{
|
||||||
|
ResetDrawerPosition(drawer, e.NewSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetZIndices()
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for (int i = 0; i < _layers.Count; i++)
|
||||||
|
{
|
||||||
|
if(_layers[i].Mask is { } mask)
|
||||||
|
{
|
||||||
|
mask.ZIndex = index;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if(_layers[i].Element is { } dialog)
|
||||||
|
{
|
||||||
|
dialog.ZIndex = index;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IDataTemplate? GetDataTemplate(object? o)
|
||||||
|
{
|
||||||
|
if (o is null) return null;
|
||||||
|
IDataTemplate? result = null;
|
||||||
|
var templates = this.DialogDataTemplates;
|
||||||
|
result = templates.FirstOrDefault(a => a.Match(o));
|
||||||
|
if (result != null) return result;
|
||||||
|
var keys = this.Resources.Keys;
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
if (Resources.TryGetValue(key, out var value) && value is IDataTemplate t)
|
||||||
|
{
|
||||||
|
result = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/Ursa/Controls/OverlayShared/OverlayFeedbackElement.cs
Normal file
93
src/Ursa/Controls/OverlayShared/OverlayFeedbackElement.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Ursa.EventArgs;
|
||||||
|
|
||||||
|
namespace Ursa.Controls.OverlayShared;
|
||||||
|
|
||||||
|
public abstract class OverlayFeedbackElement: ContentControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<bool> IsClosedProperty =
|
||||||
|
AvaloniaProperty.Register<OverlayFeedbackElement, bool>(nameof(IsClosed), defaultValue: true);
|
||||||
|
|
||||||
|
public bool IsClosed
|
||||||
|
{
|
||||||
|
get => GetValue(IsClosedProperty);
|
||||||
|
set => SetValue(IsClosedProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OverlayFeedbackElement()
|
||||||
|
{
|
||||||
|
DataContextProperty.Changed.AddClassHandler<CustomDialogControl, object?>((o, e) => o.OnDataContextChange(e));
|
||||||
|
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o,e)=>o.OnClosed(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClosed(ResultEventArgs arg2)
|
||||||
|
{
|
||||||
|
SetCurrentValue(IsClosedProperty,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly RoutedEvent<ResultEventArgs> ClosedEvent = RoutedEvent.Register<DrawerControlBase, ResultEventArgs>(
|
||||||
|
nameof(Closed), RoutingStrategies.Bubble);
|
||||||
|
|
||||||
|
public event EventHandler<ResultEventArgs> Closed
|
||||||
|
{
|
||||||
|
add => AddHandler(ClosedEvent, value);
|
||||||
|
remove => RemoveHandler(ClosedEvent, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChange(AvaloniaPropertyChangedEventArgs<object?> args)
|
||||||
|
{
|
||||||
|
if (args.OldValue.Value is IDialogContext oldContext)
|
||||||
|
{
|
||||||
|
oldContext.RequestClose -= OnContextRequestClose;
|
||||||
|
}
|
||||||
|
if (args.NewValue.Value is IDialogContext newContext)
|
||||||
|
{
|
||||||
|
newContext.RequestClose += OnContextRequestClose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnElementClosing(object sender, object? args)
|
||||||
|
{
|
||||||
|
RaiseEvent(new ResultEventArgs(ClosedEvent, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnContextRequestClose(object sender, object? args)
|
||||||
|
{
|
||||||
|
RaiseEvent(new ResultEventArgs(ClosedEvent, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<T?> ShowAsync<T>(CancellationToken? token = default)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<T?>();
|
||||||
|
token?.Register(() =>
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Invoke(Close);
|
||||||
|
});
|
||||||
|
|
||||||
|
void OnCloseHandler(object sender, ResultEventArgs? args)
|
||||||
|
{
|
||||||
|
if (args?.Result is T result)
|
||||||
|
{
|
||||||
|
tcs.SetResult(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tcs.SetResult(default);
|
||||||
|
}
|
||||||
|
RemoveHandler(ClosedEvent, OnCloseHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHandler(ClosedEvent, OnCloseHandler);
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void SetVisibility(Button? button, bool visible)
|
||||||
|
{
|
||||||
|
if (button is not null) button.IsVisible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Close();
|
||||||
|
}
|
||||||
30
src/Ursa/Controls/Shapes/PureRectangle.cs
Normal file
30
src/Ursa/Controls/Shapes/PureRectangle.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Shapes;
|
||||||
|
using Avalonia.Media;
|
||||||
|
|
||||||
|
namespace Ursa.Controls.Shapes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A rectangle, with no corner radius.
|
||||||
|
/// </summary>
|
||||||
|
public class PureRectangle: Control
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<IBrush?> BackgroundProperty = AvaloniaProperty.Register<PureRectangle, IBrush?>(
|
||||||
|
nameof(Background));
|
||||||
|
|
||||||
|
public IBrush? Background
|
||||||
|
{
|
||||||
|
get => GetValue(BackgroundProperty);
|
||||||
|
set => SetValue(BackgroundProperty, value);
|
||||||
|
}
|
||||||
|
static PureRectangle()
|
||||||
|
{
|
||||||
|
FocusableProperty.OverrideDefaultValue<PureRectangle>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render(DrawingContext context)
|
||||||
|
{
|
||||||
|
context.DrawRectangle(Background, null, new Rect(Bounds.Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Ursa/EventArgs/ResultEventArgs.cs
Normal file
18
src/Ursa/EventArgs/ResultEventArgs.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Avalonia.Interactivity;
|
||||||
|
|
||||||
|
namespace Ursa.EventArgs;
|
||||||
|
|
||||||
|
public class ResultEventArgs: RoutedEventArgs
|
||||||
|
{
|
||||||
|
public object? Result { get; set; }
|
||||||
|
|
||||||
|
public ResultEventArgs(object? result)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultEventArgs(RoutedEvent routedEvent, object? result): base(routedEvent)
|
||||||
|
{
|
||||||
|
Result = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user