Merge pull request #412 from irihitech/dialogresizer

Introduce Resize feature to Dialog and Drawer.
This commit is contained in:
Zhang Dian
2024-09-18 18:24:12 +08:00
committed by GitHub
27 changed files with 1000 additions and 558 deletions

View File

@@ -50,6 +50,7 @@
IsChecked="{Binding IsCloseButtonVisible}"
IsThreeState="True" />
<CheckBox u:FormItem.Label="CanDragMove" IsChecked="{Binding CanDragMove}" />
<CheckBox u:FormItem.Label="CanResize" IsChecked="{Binding CanResize}" />
<Button
HorizontalAlignment="Left"
u:FormItem.NoLabel="True"
@@ -85,6 +86,7 @@
IsThreeState="True" />
<CheckBox u:FormItem.Label="Modal" IsChecked="{Binding IsModal}" />
<CheckBox u:FormItem.Label="CanDragMove" IsChecked="{Binding CanDragMove}" />
<CheckBox u:FormItem.Label="CanResize" IsChecked="{Binding CanResize}" />
<Button
HorizontalAlignment="Left"
u:FormItem.NoLabel="True"
@@ -120,6 +122,7 @@
<CheckBox u:FormItem.Label="Can DragMove" IsChecked="{Binding CanDragMove}" />
<CheckBox u:FormItem.Label="Can LightDismiss" IsChecked="{Binding CanLightDismiss}" />
<CheckBox u:FormItem.Label="FullScreen" IsChecked="{Binding FullScreen}" />
<CheckBox u:FormItem.Label="CanResize" IsChecked="{Binding CanResize}" />
<ToggleSwitch
u:FormItem.Label="Global/Local OverlayHost"
IsChecked="{Binding IsLocal}"
@@ -150,6 +153,7 @@
<CheckBox u:FormItem.Label="Can DragMove" IsChecked="{Binding CanDragMove}" />
<CheckBox u:FormItem.Label="Can LightDismiss" IsChecked="{Binding CanLightDismiss}" />
<CheckBox u:FormItem.Label="FullScreen" IsChecked="{Binding FullScreen}" />
<CheckBox u:FormItem.Label="CanResize" IsChecked="{Binding CanResize}" />
<ToggleSwitch
u:FormItem.Label="Global/Local OverlayHost"
IsChecked="{Binding IsLocal}"

View File

@@ -27,6 +27,7 @@
<CheckBox u:FormItem.Label="Is Modal" IsChecked="{Binding IsModal}"/>
<CheckBox u:FormItem.Label="Is Close Button Visible" IsChecked="{Binding IsCloseButtonVisible}" IsThreeState="True"/>
<CheckBox u:FormItem.Label="Custom Dialog" IsChecked="{Binding Custom}"/>
<CheckBox u:FormItem.Label="CanResize" IsChecked="{Binding CanResize}" />
<ToggleSwitch
u:FormItem.Label="Global/Local OverlayHost"
IsChecked="{Binding IsLocal}"

View File

@@ -32,6 +32,7 @@ public partial class DefaultWindowDialogDemoViewModel: ObservableObject
[ObservableProperty] private bool _showInTaskBar;
[ObservableProperty] private bool? _isCloseButtonVisible;
[ObservableProperty] private bool _canDragMove;
[ObservableProperty] private bool _canResize;
public ICommand ShowDialogCommand { get; }
@@ -55,6 +56,7 @@ public partial class DefaultWindowDialogDemoViewModel: ObservableObject
IsCloseButtonVisible = IsCloseButtonVisible,
StartupLocation = Location,
CanDragMove = CanDragMove,
CanResize = CanResize,
};
if (X.HasValue && Y.HasValue)
{
@@ -74,6 +76,7 @@ public partial class CustomWindowDialogDemoViewModel: ObservableObject
[ObservableProperty] private bool? _isCloseButtonVisible;
[ObservableProperty] private bool _isModal;
[ObservableProperty] private bool _canDragMove;
[ObservableProperty] private bool _canResize;
public ICommand ShowDialogCommand { get; }
@@ -94,6 +97,7 @@ public partial class CustomWindowDialogDemoViewModel: ObservableObject
IsCloseButtonVisible = IsCloseButtonVisible,
StartupLocation = Location,
CanDragMove = CanDragMove,
CanResize = CanResize,
};
if (X.HasValue && Y.HasValue)
{
@@ -128,6 +132,7 @@ public partial class DefaultOverlayDialogDemoViewModel : ObservableObject
[ObservableProperty] private bool? _isCloseButtonVisible;
[ObservableProperty] private bool _isModal;
[ObservableProperty] private bool _isLocal;
[ObservableProperty] private bool _canResize;
public ICommand ShowDialogCommand { get; }
@@ -157,6 +162,7 @@ public partial class DefaultOverlayDialogDemoViewModel : ObservableObject
CanLightDismiss = CanLightDismiss,
CanDragMove = CanDragMove,
IsCloseButtonVisible = IsCloseButtonVisible,
CanResize = CanResize,
};
string? dialogHostId = IsLocal ? DialogDemoViewModel.LocalHost : null;
if (IsModal)
@@ -183,6 +189,7 @@ public partial class CustomOverlayDialogDemoViewModel: ObservableObject
[ObservableProperty] private bool? _isCloseButtonVisible;
[ObservableProperty] private bool _isModal;
[ObservableProperty] private bool _isLocal;
[ObservableProperty] private bool _canResize;
public ICommand ShowDialogCommand { get; }
@@ -208,6 +215,7 @@ public partial class CustomOverlayDialogDemoViewModel: ObservableObject
CanLightDismiss = CanLightDismiss,
CanDragMove = CanDragMove,
IsCloseButtonVisible = IsCloseButtonVisible,
CanResize = CanResize,
};
var dialogHostId = IsLocal ? DialogDemoViewModel.LocalHost : null;
if (IsModal)

View File

@@ -24,6 +24,7 @@ public partial class DrawerDemoViewModel : ObservableObject
[ObservableProperty] private bool _custom;
[ObservableProperty] private bool _isLocal;
[ObservableProperty] private bool _canResize;
public DrawerDemoViewModel()
{
@@ -42,6 +43,7 @@ public partial class DrawerDemoViewModel : ObservableObject
CanLightDismiss = CanLightDismiss,
IsCloseButtonVisible = IsCloseButtonVisible,
Title = Title,
CanResize = CanResize,
};
var hostId = IsLocal ? "LocalHost" : null;
if (Custom)

View File

@@ -11,7 +11,8 @@
d:DesignHeight="450"
d:DesignWidth="800"
x:CompileBindings="True"
IsFullScreenButtonVisible="{OnPlatform True,macOS=False}"
IsFullScreenButtonVisible="{OnPlatform True, macOS=False}"
IsManagedResizerVisible="{OnPlatform False, Linux=True}"
x:DataType="viewModels:MainWindowViewModel"
Icon="/Assets/Ursa.ico"
mc:Ignorable="d">

View File

@@ -7,68 +7,72 @@
<ControlTheme x:Key="{x:Type u:OverlayDialogHost}" TargetType="u:OverlayDialogHost">
<Setter Property="OverlayMaskBrush" Value="{DynamicResource OverlayDialogMaskBrush}" />
</ControlTheme>
<ControlTheme x:Key="{x:Type u:CustomDialogControl}" TargetType="u:CustomDialogControl">
<Setter Property="MinWidth" Value="{DynamicResource DialogMinWidth}"/>
<Setter Property="MinHeight" Value="{DynamicResource DialogMinHeight}"/>
<Setter Property="CornerRadius" Value="{DynamicResource DialogCornerRadius}" />
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Duration="0.2" Property="RenderTransform"/>
<TransformOperationsTransition Property="RenderTransform" Duration="0.2" />
</Transitions>
</Setter>
<Setter Property="RenderTransform" Value="scale(1.0)"></Setter>
<Setter Property="RenderTransform" Value="scale(1.0)" />
<Setter Property="Template">
<ControlTemplate TargetType="u:CustomDialogControl">
<Border
Name="PART_Border"
Focusable="True"
Padding="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius}"
IsHitTestVisible="True"
Theme="{DynamicResource CardBorder}">
<Border ClipToBounds="True" CornerRadius="{TemplateBinding 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 OverlayCloseButton}" />
<Panel>
<Border
Name="PART_Border"
Padding="0"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius}"
Focusable="True"
IsHitTestVisible="True"
Theme="{DynamicResource CardBorder}">
<Border ClipToBounds="True" CornerRadius="{TemplateBinding 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 OverlayCloseButton}" />
</Grid>
</Grid>
</Grid>
</Border>
</Border>
</Border>
<u:DialogResizer IsVisible="{TemplateBinding CanResize}" Margin="{Binding #PART_Border.Margin}" />
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:full-screen">
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="CornerRadius" Value="0" />
<Style Selector="^ /template/ Border#PART_Border">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Theme" Value="{x:Null}"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Background" Value="{DynamicResource BorderCardBackground}"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Theme" Value="{x:Null}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="{DynamicResource BorderCardBackground}" />
</Style>
</Style>
<Style Selector="^ /template/ Panel#PART_TitleArea">
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem Command="{Binding $parent[u:DialogControlBase].Close}" Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
Width="12"
@@ -126,9 +130,7 @@
Data="{DynamicResource DialogArrangeSendToBackGlyph}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem Command="{Binding $parent[u:DialogControlBase].Close}" Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
Width="12"
@@ -142,111 +144,115 @@
</ControlTheme>
<ControlTheme x:Key="{x:Type u:DefaultDialogControl}" TargetType="u:DefaultDialogControl">
<Setter Property="MinWidth" Value="{DynamicResource DialogMinWidth}"/>
<Setter Property="MinHeight" Value="{DynamicResource DialogMinHeight}"/>
<Setter Property="CornerRadius" Value="{DynamicResource DialogCornerRadius}" />
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Duration="0.2" Property="RenderTransform"/>
<TransformOperationsTransition Property="RenderTransform" Duration="0.2" />
</Transitions>
</Setter>
<Setter Property="Template">
<ControlTemplate TargetType="u:DefaultDialogControl">
<Border
Name="PART_Border"
Padding="0"
Focusable="True"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
BoxShadow="0 0 8 0 #1A000000"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius}"
IsHitTestVisible="True"
Theme="{DynamicResource CardBorder}">
<Border ClipToBounds="True" CornerRadius="{TemplateBinding CornerRadius}">
<Grid RowDefinitions="Auto, *, Auto">
<ScrollViewer Grid.Row="1">
<ContentPresenter
Name="PART_ContentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="24,8"
Content="{TemplateBinding Content}" />
</ScrollViewer>
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
<Panel
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
Grid.Column="0"
Grid.ColumnSpan="3"
Background="Transparent" />
<PathIcon
Name="PART_Icon"
Grid.Column="0"
Width="16"
Height="16"
Margin="24,24,8,0"
VerticalAlignment="Center" />
<TextBlock
Name="PART_Title"
Grid.Column="1"
Margin="0,24,0,0"
VerticalAlignment="Center"
FontSize="16"
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
IsHitTestVisible="False"
IsVisible="{TemplateBinding Title,
Converter={x:Static ObjectConverters.IsNotNull}}"
Text="{TemplateBinding Title}"
TextWrapping="Wrap" />
<Button
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
Grid.Column="2"
Margin="0,24,24,0"
DockPanel.Dock="Right"
Theme="{DynamicResource OverlayCloseButton}" />
<Panel>
<Border
Name="PART_Border"
Padding="0"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
BoxShadow="0 0 8 0 #1A000000"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius}"
Focusable="True"
IsHitTestVisible="True"
Theme="{DynamicResource CardBorder}">
<Border ClipToBounds="True" CornerRadius="{TemplateBinding CornerRadius}">
<Grid RowDefinitions="Auto, *, Auto">
<ScrollViewer Grid.Row="1">
<ContentPresenter
Name="PART_ContentPresenter"
Margin="24,8"
Content="{TemplateBinding Content}" />
</ScrollViewer>
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
<Panel
Name="{x:Static u:DialogControlBase.PART_TitleArea}"
Grid.Column="0"
Grid.ColumnSpan="3"
Background="Transparent" />
<PathIcon
Name="PART_Icon"
Grid.Column="0"
Width="16"
Height="16"
Margin="24,24,8,0"
VerticalAlignment="Center" />
<TextBlock
Name="PART_Title"
Grid.Column="1"
Margin="0,24,0,0"
VerticalAlignment="Center"
FontSize="16"
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
IsHitTestVisible="False"
IsVisible="{TemplateBinding Title,
Converter={x:Static ObjectConverters.IsNotNull}}"
Text="{TemplateBinding Title}"
TextWrapping="Wrap" />
<Button
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
Grid.Column="2"
Margin="0,24,24,0"
DockPanel.Dock="Right"
Theme="{DynamicResource OverlayCloseButton}" />
</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>
<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>
</Border>
<u:DialogResizer IsVisible="{TemplateBinding CanResize}" Margin="{Binding #PART_Border.Margin}"/>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:full-screen">
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="CornerRadius" Value="0" />
</Style>
<Style Selector="^:full-screen /template/ Border#PART_Border">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Theme" Value="{x:Null}"/>
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="Background" Value="{DynamicResource BorderCardBackground}"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Theme" Value="{x:Null}" />
<Setter Property="Margin" Value="0" />
<Setter Property="Background" Value="{DynamicResource BorderCardBackground}" />
</Style>
<Style Selector="^[Mode=None]">
<Style Selector="^ /template/ PathIcon#PART_Icon">
@@ -369,9 +375,7 @@
<Style Selector="^ /template/ Panel#PART_TitleArea">
<Setter Property="ContextFlyout">
<MenuFlyout>
<MenuItem
Command="{Binding $parent[u:DialogControlBase].Close}"
Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem Command="{Binding $parent[u:DialogControlBase].Close}" Header="{DynamicResource STRING_MENU_DIALOG_CLOSE}">
<MenuItem.Icon>
<PathIcon
Width="12"
@@ -458,6 +462,8 @@
<Setter Property="ExtendClientAreaTitleBarHeightHint" Value="1" />
<Setter Property="ExtendClientAreaToDecorationsHint" Value="True" />
<Setter Property="ExtendClientAreaChromeHints" Value="SystemChrome" />
<Setter Property="MinWidth" Value="{DynamicResource DialogMinWidth}"/>
<Setter Property="MinHeight" Value="{DynamicResource DialogMinHeight}"/>
<Setter Property="SystemDecorations">
<OnPlatform>
<OnPlatform.Windows>
@@ -473,11 +479,12 @@
<ControlTemplate TargetType="u:DialogWindow">
<Panel>
<Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
<Border Background="{TemplateBinding Background}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsHitTestVisible="False" />
<Border
Background="{TemplateBinding Background}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsHitTestVisible="False" />
<Panel Margin="{TemplateBinding WindowDecorationMargin}" Background="Transparent" />
<VisualLayerManager>
<Grid RowDefinitions="Auto, *">
@@ -505,8 +512,10 @@
Margin="0,24,24,0"
Theme="{DynamicResource CloseButton}" />
</Grid>
</Grid>
</VisualLayerManager>
<u:WindowResizer IsVisible="{TemplateBinding IsManagedResizerVisible}" />
</Panel>
</ControlTemplate>
</Setter>
@@ -525,6 +534,8 @@
<Setter Property="ExtendClientAreaTitleBarHeightHint" Value="1" />
<Setter Property="ExtendClientAreaToDecorationsHint" Value="True" />
<Setter Property="ExtendClientAreaChromeHints" Value="SystemChrome" />
<Setter Property="MinWidth" Value="{DynamicResource DialogMinWidth}"/>
<Setter Property="MinHeight" Value="{DynamicResource DialogMinHeight}"/>
<Setter Property="SystemDecorations">
<OnPlatform>
<OnPlatform.Windows>
@@ -540,11 +551,12 @@
<ControlTemplate TargetType="u:DefaultDialogWindow">
<Panel>
<Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
<Border Background="{TemplateBinding Background}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsHitTestVisible="False" />
<Border
Background="{TemplateBinding Background}"
BackgroundSizing="InnerBorderEdge"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
IsHitTestVisible="False" />
<Panel Margin="{TemplateBinding WindowDecorationMargin}" Background="Transparent" />
<VisualLayerManager>
<Grid RowDefinitions="Auto, *, Auto">
@@ -614,8 +626,10 @@
Content="{DynamicResource STRING_MENU_DIALOG_OK}"
Theme="{DynamicResource SolidButton}" />
</StackPanel>
</Grid>
</VisualLayerManager>
<u:WindowResizer IsVisible="{TemplateBinding IsManagedResizerVisible}" />
</Panel>
</ControlTemplate>
</Setter>
@@ -738,4 +752,4 @@
</Style>
</Style>
</ControlTheme>
</ResourceDictionary>
</ResourceDictionary>

View File

@@ -1,154 +1,60 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="https://irihi.tech/ursa"
xmlns:c="clr-namespace:Ursa.Themes.Semi.Converters">
<ControlTheme TargetType="u:CustomDrawerControl" x:Key="{x:Type u:CustomDrawerControl}">
<Setter Property="VerticalAlignment" Value="Stretch"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
<Setter Property="Padding" Value="{DynamicResource DrawerMargin}"/>
<Setter Property="BorderThickness" Value="{DynamicResource DrawerBorderThickness}"/>
<Setter Property="CornerRadius" Value="{DynamicResource DrawerCornerRadius}"/>
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Ursa.Themes.Semi.Converters"
xmlns:u="https://irihi.tech/ursa">
<ControlTheme x:Key="{x:Type u:CustomDrawerControl}" TargetType="u:CustomDrawerControl">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="MinWidth" Value="96" />
<Setter Property="MinHeight" Value="96" />
<Setter Property="Padding" Value="{DynamicResource DrawerMargin}" />
<Setter Property="BorderThickness" Value="{DynamicResource DrawerBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource DrawerCornerRadius}" />
<Setter Property="Template">
<ControlTemplate TargetType="u:CustomDrawerControl">
<Border Name="PART_Root"
Focusable="True"
Margin="{TemplateBinding Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}"
<Panel>
<Border
Name="PART_Root"
Margin="{TemplateBinding Padding,
Converter={x:Static c:ThicknessTakeConverter.Left}}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="{TemplateBinding BorderThickness,
Converter={x:Static c:ThicknessTakeConverter.Left}}"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Left}}"
BorderThickness="{TemplateBinding BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Left}}"
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 OverlayCloseButton}" />
</Grid>
</Grid>
</Border>
</Border>
</ControlTemplate>
</Setter>
<Style Selector="^[Position=Right] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Left}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
</Style>
<Style Selector="^[Position=Left] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Right}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
</Style>
<Style Selector="^[Position=Top] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Bottom}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
</Style>
<Style Selector="^[Position=Bottom] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Top}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
</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="Padding" Value="{DynamicResource DrawerMargin}"/>
<Setter Property="BorderThickness" Value="{DynamicResource DrawerBorderThickness}"/>
<Setter Property="CornerRadius" Value="{DynamicResource DrawerCornerRadius}"/>
<Setter Property="Template">
<ControlTemplate TargetType="u:DefaultDrawerControl">
<Border Name="PART_Root"
CornerRadius="{TemplateBinding CornerRadius,
Converter={x:Static c:CornerRadiusTakeConverter.Left}}"
Focusable="True"
Margin="{TemplateBinding Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Left}}"
BorderThickness="{TemplateBinding BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Left}}"
IsHitTestVisible="True"
Theme="{DynamicResource CardBorder}">
<Border ClipToBounds="True" CornerRadius="{Binding #PART_Root.CornerRadius}">
<Grid RowDefinitions="Auto, *, Auto">
<ScrollViewer Grid.Row="1">
<Border ClipToBounds="True" CornerRadius="{Binding #PART_Root.CornerRadius}">
<Grid RowDefinitions="Auto, *">
<ContentPresenter
Name="PART_ContentPresenter"
Margin="24,8"
Grid.Row="0"
Grid.RowSpan="2"
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="{DynamicResource TextBlockTitleFontWeight}"
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 OverlayCloseButton}" />
<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 OverlayCloseButton}" />
</Grid>
</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>
</Border>
<u:DialogResizer IsVisible="{TemplateBinding CanResize}" />
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^[Position=Right] /template/ Border#PART_Root">
@@ -171,5 +77,150 @@
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Top}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
</Style>
<Style Selector="^[Position=Top] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
<Setter Property="ResizeDirection" Value="Bottom"/>
</Style>
<Style Selector="^[Position=Bottom] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
<Setter Property="ResizeDirection" Value="Top"/>
</Style>
<Style Selector="^[Position=Left] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
<Setter Property="ResizeDirection" Value="Right"/>
</Style>
<Style Selector="^[Position=Right] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
<Setter Property="ResizeDirection" Value="Left"/>
</Style>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:DefaultDrawerControl}" TargetType="u:DefaultDrawerControl">
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="MinWidth" Value="96" />
<Setter Property="MinHeight" Value="96" />
<Setter Property="Padding" Value="{DynamicResource DrawerMargin}" />
<Setter Property="BorderThickness" Value="{DynamicResource DrawerBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource DrawerCornerRadius}" />
<Setter Property="Template">
<ControlTemplate TargetType="u:DefaultDrawerControl">
<Panel>
<Border
Name="PART_Root"
Margin="{TemplateBinding Padding,
Converter={x:Static c:ThicknessTakeConverter.Left}}"
Padding="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="{TemplateBinding BorderThickness,
Converter={x:Static c:ThicknessTakeConverter.Left}}"
Classes="Shadow"
ClipToBounds="False"
CornerRadius="{TemplateBinding CornerRadius,
Converter={x:Static c:CornerRadiusTakeConverter.Left}}"
Focusable="True"
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"
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="{DynamicResource TextBlockTitleFontWeight}"
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 OverlayCloseButton}" />
</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>
<u:DialogResizer IsVisible="{TemplateBinding CanResize}" />
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^[Position=Right] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Left}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
</Style>
<Style Selector="^[Position=Left] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Right}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
</Style>
<Style Selector="^[Position=Top] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Bottom}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
</Style>
<Style Selector="^[Position=Bottom] /template/ Border#PART_Root">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
<Setter Property="CornerRadius" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CornerRadius, Converter={x:Static c:CornerRadiusTakeConverter.Top}}" />
<Setter Property="BorderThickness" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
</Style>
<Style Selector="^[Position=Top] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Bottom}}" />
<Setter Property="ResizeDirection" Value="Bottom"/>
</Style>
<Style Selector="^[Position=Bottom] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Top}}" />
<Setter Property="ResizeDirection" Value="Top"/>
</Style>
<Style Selector="^[Position=Left] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Right}}" />
<Setter Property="ResizeDirection" Value="Right"/>
</Style>
<Style Selector="^[Position=Right] /template/ u|DialogResizer">
<Setter Property="Margin" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Padding, Converter={x:Static c:ThicknessTakeConverter.Left}}" />
<Setter Property="ResizeDirection" Value="Left"/>
</Style>
</ControlTheme>
</ResourceDictionary>

View File

@@ -1,29 +1,144 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="https://irihi.tech/ursa"
xmlns:iri="https://irihi.tech/shared">
<!-- Add Resources Here -->
<ControlTheme TargetType="u:WindowResizerThumb" x:Key="{x:Type u:WindowResizerThumb}">
<ResourceDictionary
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:iri="https://irihi.tech/shared"
xmlns:u="https://irihi.tech/ursa">
<!-- Add Resources Here -->
<ControlTheme x:Key="{x:Type u:WindowResizerThumb}" TargetType="u:WindowResizerThumb">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<ControlTemplate TargetType="u:WindowResizerThumb">
<iri:PureRectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{TemplateBinding Background}"/>
<iri:PureRectangle
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme TargetType="u:WindowResizer" x:Key="{x:Type u:WindowResizer}">
<ControlTheme x:Key="{x:Type u:DialogResizerThumb}" TargetType="u:DialogResizerThumb">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<ControlTemplate TargetType="u:DialogResizerThumb">
<Panel>
<iri:PureRectangle
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}" />
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:DialogResizer}" TargetType="u:DialogResizer">
<Setter Property="Template">
<ControlTemplate TargetType="u:WindowResizer">
<Grid RowDefinitions="Auto, *, Auto" ColumnDefinitions="Auto, *, Auto" >
<u:WindowResizerThumb Grid.Row="0" Grid.Column="0" ResizeDirection="TopLeft" Cursor="TopLeftCorner" />
<u:WindowResizerThumb Grid.Row="0" Grid.Column="1" ResizeDirection="Top" Cursor="TopSide" Height="{DynamicResource ResizerThumbHeight}" />
<u:WindowResizerThumb Grid.Row="0" Grid.Column="2" ResizeDirection="TopRight" Cursor="TopRightCorner" />
<u:WindowResizerThumb Grid.Row="1" Grid.Column="0" ResizeDirection="Left" Cursor="LeftSide" Width="{DynamicResource ResizerThumbWidth}" />
<u:WindowResizerThumb Grid.Row="1" Grid.Column="2" ResizeDirection="Right" Cursor="RightSide" Width="{DynamicResource ResizerThumbWidth}" />
<u:WindowResizerThumb Grid.Row="2" Grid.Column="0" ResizeDirection="BottomLeft" Cursor="BottomLeftCorner" />
<u:WindowResizerThumb Grid.Row="2" Grid.Column="1" ResizeDirection="Bottom" Cursor="BottomSide" Height="{DynamicResource ResizerThumbHeight}" />
<u:WindowResizerThumb Grid.Row="2" Grid.Column="2" ResizeDirection="BottomRight" Cursor="BottomRightCorner" />
<Grid ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, *, Auto">
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_TopLeft}"
Grid.Row="0"
Grid.Column="0"
Cursor="TopLeftCorner"
ResizeDirection="TopLeft" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_Top}"
Grid.Row="0"
Grid.Column="1"
Height="{DynamicResource ResizerThumbHeight}"
Cursor="TopSide"
ResizeDirection="Top" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_TopRight}"
Grid.Row="0"
Grid.Column="2"
Cursor="TopRightCorner"
ResizeDirection="TopRight" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_Left}"
Grid.Row="1"
Grid.Column="0"
Width="{DynamicResource ResizerThumbWidth}"
Cursor="LeftSide"
ResizeDirection="Left" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_Right}"
Grid.Row="1"
Grid.Column="2"
Width="{DynamicResource ResizerThumbWidth}"
Cursor="RightSide"
ResizeDirection="Right" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_BottomLeft}"
Grid.Row="2"
Grid.Column="0"
Cursor="BottomLeftCorner"
ResizeDirection="BottomLeft" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_Bottom}"
Grid.Row="2"
Grid.Column="1"
Height="{DynamicResource ResizerThumbHeight}"
Cursor="BottomSide"
ResizeDirection="Bottom" />
<u:DialogResizerThumb
Name="{x:Static u:DialogResizer.PART_BottomRight}"
Grid.Row="2"
Grid.Column="2"
Cursor="BottomRightCorner"
ResizeDirection="BottomRight" />
</Grid>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:WindowResizer}" TargetType="u:WindowResizer">
<Setter Property="Template">
<ControlTemplate TargetType="u:WindowResizer">
<Grid ColumnDefinitions="Auto, *, Auto" RowDefinitions="Auto, *, Auto">
<u:WindowResizerThumb
Grid.Row="0"
Grid.Column="0"
Cursor="TopLeftCorner"
ResizeDirection="TopLeft" />
<u:WindowResizerThumb
Grid.Row="0"
Grid.Column="1"
Height="{DynamicResource ResizerThumbHeight}"
Cursor="TopSide"
ResizeDirection="Top" />
<u:WindowResizerThumb
Grid.Row="0"
Grid.Column="2"
Cursor="TopRightCorner"
ResizeDirection="TopRight" />
<u:WindowResizerThumb
Grid.Row="1"
Grid.Column="0"
Width="{DynamicResource ResizerThumbWidth}"
Cursor="LeftSide"
ResizeDirection="Left" />
<u:WindowResizerThumb
Grid.Row="1"
Grid.Column="2"
Width="{DynamicResource ResizerThumbWidth}"
Cursor="RightSide"
ResizeDirection="Right" />
<u:WindowResizerThumb
Grid.Row="2"
Grid.Column="0"
Cursor="BottomLeftCorner"
ResizeDirection="BottomLeft" />
<u:WindowResizerThumb
Grid.Row="2"
Grid.Column="1"
Height="{DynamicResource ResizerThumbHeight}"
Cursor="BottomSide"
ResizeDirection="Bottom" />
<u:WindowResizerThumb
Grid.Row="2"
Grid.Column="2"
Cursor="BottomRightCorner"
ResizeDirection="BottomRight" />
</Grid>
</ControlTemplate>
</Setter>

View File

@@ -6,4 +6,6 @@
<StreamGeometry x:Key="DialogArrangeSendBackwardGlyph">M2,2H16V16H2V2M22,8V22H8V18H18V8H22M4,4V14H14V4H4Z</StreamGeometry>
<StreamGeometry x:Key="DialogArrangeSendToBackGlyph">M2,2H11V11H2V2M9,4H4V9H9V4M22,13V22H13V13H22M15,20H20V15H15V20M16,8V11H13V8H16M11,16H8V13H11V16Z</StreamGeometry>
<CornerRadius x:Key="DialogCornerRadius">12</CornerRadius>
<x:Double x:Key="DialogMinWidth">48</x:Double>
<x:Double x:Key="DialogMinHeight">48</x:Double>
</ResourceDictionary>

View File

@@ -205,6 +205,8 @@ public static class Dialog
window.IsCloseButtonVisible = options.IsCloseButtonVisible;
window.ShowInTaskbar = options.ShowInTaskBar;
window.CanDragMove = options.CanDragMove;
window.CanResize = options.CanResize;
window.IsManagedResizerVisible = options.CanResize;
if (options.StartupLocation == WindowStartupLocation.Manual)
{
if (options.Position is not null)
@@ -229,6 +231,8 @@ public static class Dialog
window.ShowInTaskbar = options.ShowInTaskBar;
window.IsCloseButtonVisible = options.IsCloseButtonVisible;
window.CanDragMove = options.CanDragMove;
window.IsManagedResizerVisible = options.CanResize;
window.CanResize = options.CanResize;
if (options.StartupLocation == WindowStartupLocation.Manual)
{
if (options.Position is not null)

View File

@@ -24,10 +24,16 @@ public abstract class DialogControlBase : OverlayFeedbackElement
AvaloniaProperty.RegisterDirect<DialogControlBase, bool>(
nameof(IsFullScreen), o => o.IsFullScreen, (o, v) => o.IsFullScreen = v);
public static readonly StyledProperty<bool> CanResizeProperty = AvaloniaProperty.Register<DialogControlBase, bool>(
nameof(CanResize));
protected internal Button? _closeButton;
private bool _isFullScreen;
private Panel? _titleArea;
private bool _moveDragging;
private Point _moveDragStartPoint;
static DialogControlBase()
{
@@ -36,6 +42,12 @@ public abstract class DialogControlBase : OverlayFeedbackElement
IsFullScreenProperty.AffectsPseudoClass<DialogControlBase>(PC_FullScreen);
}
public bool CanResize
{
get => GetValue(CanResizeProperty);
set => SetValue(CanResizeProperty, value);
}
internal HorizontalPosition HorizontalAnchor { get; set; } = HorizontalPosition.Center;
internal VerticalPosition VerticalAnchor { get; set; } = VerticalPosition.Center;
internal HorizontalPosition ActualHorizontalAnchor { get; set; }
@@ -79,17 +91,44 @@ public abstract class DialogControlBase : OverlayFeedbackElement
private void OnTitlePointerPressed(InputElement sender, PointerPressedEventArgs e)
{
e.Source = this;
//e.Source = this;
if (ContainerPanel is OverlayDialogHost h)
{
if (h.IsTopLevel && this.IsFullScreen)
{
var top = TopLevel.GetTopLevel(this);
if (top is Window w)
{
w.BeginMoveDrag(e);
return;
}
}
}
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
if (IsFullScreen) return;
_moveDragging = true;
_moveDragStartPoint = e.GetPosition(this);
}
private void OnTitlePointerMove(InputElement sender, PointerEventArgs e)
{
e.Source = this;
//e.Source = this;
if (!_moveDragging) return;
if (ContainerPanel is null) return;
var p = e.GetPosition(this);
var left = Canvas.GetLeft(this) + p.X - _moveDragStartPoint.X;
var top = Canvas.GetTop(this) + p.Y - _moveDragStartPoint.Y;
left = MathHelpers.SafeClamp(left, 0, ContainerPanel.Bounds.Width - Bounds.Width);
top = MathHelpers.SafeClamp(top, 0, ContainerPanel.Bounds.Height - Bounds.Height);
Canvas.SetLeft(this, left);
Canvas.SetTop(this, top);
}
private void OnTitlePointerRelease(InputElement sender, PointerReleasedEventArgs e)
{
e.Source = this;
// e.Source = this;
_moveDragging = false;
AnchorAndUpdatePositionInfo();
}
private void OnCloseButtonClick(object? sender, RoutedEventArgs args)
@@ -195,4 +234,50 @@ public abstract class DialogControlBase : OverlayFeedbackElement
}
#endregion
protected internal override void AnchorAndUpdatePositionInfo()
{
if (ContainerPanel is null) return;
ActualHorizontalAnchor = HorizontalPosition.Center;
ActualVerticalAnchor = VerticalPosition.Center;
double left = Canvas.GetLeft(this);
double top = Canvas.GetTop(this);
double right = ContainerPanel.Bounds.Width - left - Bounds.Width;
double bottom = ContainerPanel.Bounds.Height - top - Bounds.Height;
if (ContainerPanel is OverlayDialogHost h)
{
var snapThickness = h.SnapThickness;
if(top < snapThickness.Top)
{
Canvas.SetTop(this, 0);
ActualVerticalAnchor = VerticalPosition.Top;
VerticalOffsetRatio = 0;
}
if(bottom < snapThickness.Bottom)
{
Canvas.SetTop(this, ContainerPanel.Bounds.Height - Bounds.Height);
ActualVerticalAnchor = VerticalPosition.Bottom;
VerticalOffsetRatio = 1;
}
if(left < snapThickness.Left)
{
Canvas.SetLeft(this, 0);
ActualHorizontalAnchor = HorizontalPosition.Left;
HorizontalOffsetRatio = 0;
}
if(right < snapThickness.Right)
{
Canvas.SetLeft(this, ContainerPanel.Bounds.Width - this.Bounds.Width);
ActualHorizontalAnchor = HorizontalPosition.Right;
HorizontalOffsetRatio = 1;
}
}
left = Canvas.GetLeft(this);
top = Canvas.GetTop(this);
right = ContainerPanel.Bounds.Width - left - Bounds.Width;
bottom = ContainerPanel.Bounds.Height - top - Bounds.Height;
HorizontalOffsetRatio = (left + right) == 0 ? 0 : left / (left + right);
VerticalOffsetRatio = (top + bottom) == 0 ? 0 : top / (top + bottom);
}
}

View File

@@ -17,8 +17,17 @@ public class DialogWindow : Window
public const string PART_TitleArea = "PART_TitleArea";
protected internal Button? _closeButton;
private Panel? _titleArea;
public bool CanDragMove { get; set; } = true;
public static readonly StyledProperty<bool> IsManagedResizerVisibleProperty = AvaloniaProperty.Register<DialogWindow, bool>(
nameof(IsManagedResizerVisible));
public bool IsManagedResizerVisible
{
get => GetValue(IsManagedResizerVisibleProperty);
set => SetValue(IsManagedResizerVisibleProperty, value);
}
static DialogWindow()
{
@@ -26,9 +35,11 @@ public class DialogWindow : Window
window.OnDataContextChange(e));
}
public bool CanDragMove { get; set; } = true;
internal bool? IsCloseButtonVisible { get; set; }
protected override Type StyleKeyOverride { get; } = typeof(DialogWindow);
internal bool? IsCloseButtonVisible { get; set; }
private void OnDataContextChange(AvaloniaPropertyChangedEventArgs<object?> args)
{

View File

@@ -30,4 +30,6 @@ public class DialogOptions
public bool ShowInTaskBar { get; set; } = true;
public bool CanDragMove { get; set; } = true;
public bool CanResize { get; set; }
}

View File

@@ -62,4 +62,6 @@ public class OverlayDialogOptions
/// id.
/// </summary>
public int? TopLevelHashCode { get; set; }
public bool CanResize { get; set; }
}

View File

@@ -204,6 +204,7 @@ public static class OverlayDialog
options.VerticalAnchor == VerticalPosition.Center ? null : options.VerticalOffset;
control.IsCloseButtonVisible = options.IsCloseButtonVisible;
control.CanLightDismiss = options.CanLightDismiss;
control.CanResize = options.CanResize;
DialogControlBase.SetCanDragMove(control, options.CanDragMove);
}
@@ -229,6 +230,7 @@ public static class OverlayDialog
control.Title = options.Title;
control.CanLightDismiss = options.CanLightDismiss;
control.IsCloseButtonVisible = options.IsCloseButtonVisible;
control.CanResize = options.CanResize;
DialogControlBase.SetCanDragMove(control, options.CanDragMove);
}

View File

@@ -25,4 +25,9 @@ public class CustomDrawerControl: DrawerControlBase
OnElementClosing(this, null);
}
}
protected internal override void AnchorAndUpdatePositionInfo()
{
// throw new NotImplementedException();
}
}

View File

@@ -135,4 +135,9 @@ public class DefaultDrawerControl : DrawerControlBase
RaiseEvent(new ResultEventArgs(ClosedEvent, result));
}
}
protected internal override void AnchorAndUpdatePositionInfo()
{
// throw new NotImplementedException();
}
}

View File

@@ -205,16 +205,17 @@ public static class Drawer
drawer.Position = options.Position;
drawer.IsCloseButtonVisible = options.IsCloseButtonVisible;
drawer.CanLightDismiss = options.CanLightDismiss;
drawer.CanResize = options.CanResize;
if (options.Position == Position.Left || options.Position == Position.Right)
{
drawer.MinWidth = options.MinWidth ?? 0.0;
drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity;
if(options.MinWidth is not null) drawer.MinWidth = options.MinWidth.Value;
if(options.MaxWidth is not null) drawer.MaxWidth = options.MaxWidth.Value;
}
if (options.Position is Position.Top or Position.Bottom)
{
drawer.MinHeight = options.MinHeight ?? 0.0;
drawer.MaxHeight = options.MaxHeight ?? double.PositiveInfinity;
if(options.MinHeight is not null) drawer.MinHeight = options.MinHeight.Value;
if(options.MaxHeight is not null) drawer.MaxHeight = options.MaxHeight.Value;
}
}
@@ -226,16 +227,17 @@ public static class Drawer
drawer.CanLightDismiss = options.CanLightDismiss;
drawer.Buttons = options.Buttons;
drawer.Title = options.Title;
drawer.CanResize = options.CanResize;
if (options.Position == Position.Left || options.Position == Position.Right)
{
drawer.MinWidth = options.MinWidth ?? 0.0;
drawer.MaxWidth = options.MaxWidth ?? double.PositiveInfinity;
if(options.MinWidth is not null) drawer.MinWidth = options.MinWidth.Value;
if(options.MaxWidth is not null) drawer.MaxWidth = options.MaxWidth.Value;
}
if (options.Position is Position.Top or Position.Bottom)
{
drawer.MinHeight = options.MinHeight ?? 0.0;
drawer.MaxHeight = options.MaxHeight ?? double.PositiveInfinity;
if(options.MinHeight is not null) drawer.MinHeight = options.MinHeight.Value;
if(options.MaxHeight is not null) drawer.MaxHeight = options.MaxHeight.Value;
}
}
}

View File

@@ -22,6 +22,15 @@ public abstract class DrawerControlBase: OverlayFeedbackElement
AvaloniaProperty.Register<DrawerControlBase, Position>(
nameof(Position), defaultValue: Position.Right);
public static readonly StyledProperty<bool> CanResizeProperty = AvaloniaProperty.Register<DrawerControlBase, bool>(
nameof(CanResize));
public bool CanResize
{
get => GetValue(CanResizeProperty);
set => SetValue(CanResizeProperty, value);
}
public Position Position
{
get => GetValue(PositionProperty);

View File

@@ -20,4 +20,6 @@ public class DrawerOptions
/// The hash code of the top level dialog host. This is used to identify the dialog host if there are multiple dialog hosts with the same id. If this is not provided, the dialog will be added to the first dialog host with the same id.
/// </summary>
public int? TopLevelHashCode { get; set; }
public bool CanResize { get; set; }
}

View File

@@ -1,5 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.VisualTree;
using Irihi.Avalonia.Shared.Helpers;
@@ -10,10 +9,8 @@ namespace Ursa.Controls;
public partial class OverlayDialogHost
{
private Point _lastPoint;
public Thickness SnapThickness { get; set; } = new Thickness(0);
public Thickness SnapThickness { get; set; } = new(0);
private static void ResetDialogPosition(DialogControlBase control, Size newSize)
{
control.MaxWidth = newSize.Width;
@@ -26,138 +23,71 @@ public partial class OverlayDialogHost
SetTop(control, 0);
return;
}
var width = newSize.Width - control.Bounds.Width;
var height = newSize.Height - control.Bounds.Height;
var newLeft = width * control.HorizontalOffsetRatio??0;
var newTop = height * control.VerticalOffsetRatio??0;
if(control.ActualHorizontalAnchor == HorizontalPosition.Left)
{
newLeft = 0;
}
if (control.ActualHorizontalAnchor == HorizontalPosition.Right)
{
newLeft = newSize.Width - control.Bounds.Width;
}
if (control.ActualVerticalAnchor == VerticalPosition.Top)
{
newTop = 0;
}
if (control.ActualVerticalAnchor == VerticalPosition.Bottom)
{
newTop = newSize.Height - control.Bounds.Height;
}
var newLeft = width * control.HorizontalOffsetRatio ?? 0;
var newTop = height * control.VerticalOffsetRatio ?? 0;
if (control.ActualHorizontalAnchor == HorizontalPosition.Left) newLeft = 0;
if (control.ActualHorizontalAnchor == HorizontalPosition.Right) newLeft = newSize.Width - control.Bounds.Width;
if (control.ActualVerticalAnchor == VerticalPosition.Top) newTop = 0;
if (control.ActualVerticalAnchor == VerticalPosition.Bottom) newTop = newSize.Height - control.Bounds.Height;
SetLeft(control, Math.Max(0.0, newLeft));
SetTop(control, Math.Max(0.0, newTop));
}
protected override void OnPointerMoved(PointerEventArgs e)
{
if (e.Source is DialogControlBase item)
{
if (item.IsFullScreen) return;
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
var p = e.GetPosition(this);
var left = p.X - _lastPoint.X;
var top = p.Y - _lastPoint.Y;
left = MathHelpers.SafeClamp(left, 0, Bounds.Width - item.Bounds.Width);
top = MathHelpers.SafeClamp(top, 0, Bounds.Height - item.Bounds.Height);
SetLeft(item, left);
SetTop(item, top);
}
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
if (e.Source is DialogControlBase item)
{
if (IsTopLevel && item.IsFullScreen)
{
var top = TopLevel.GetTopLevel(item);
if (top is Window w)
{
w.BeginMoveDrag(e);
}
}
else
{
_lastPoint = e.GetPosition(item);
}
}
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (e.Source is DialogControlBase item)
{
AnchorAndUpdatePositionInfo(item);
}
}
internal void AddDialog(DialogControlBase control)
{
PureRectangle? mask = null;
if (control.CanLightDismiss)
{
mask = CreateOverlayMask(false, control.CanLightDismiss);
}
if (mask is not null)
{
Children.Add(mask);
}
this.Children.Add(control);
if (control.CanLightDismiss) mask = CreateOverlayMask(false, control.CanLightDismiss);
if (mask is not null) Children.Add(mask);
Children.Add(control);
_layers.Add(new DialogPair(mask, control, false));
if (control.IsFullScreen)
{
control.Width = Bounds.Width;
control.Height = Bounds.Height;
}
control.MaxWidth = Bounds.Width;
control.MaxHeight = Bounds.Height;
control.Measure(this.Bounds.Size);
control.Measure(Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetToPosition(control);
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
ResetZIndices();
}
private async void OnDialogControlClosing(object? sender, object? e)
{
if (sender is DialogControlBase control)
if (sender is not DialogControlBase control) return;
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);
layer.Mask?.RemoveHandler(PointerPressedEvent, DragMaskToMoveWindow);
Children.Remove(control);
if (layer.Mask is not null)
{
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);
control.RemoveHandler(PointerPressedEvent, DragMaskToMoveWindow);
Children.Remove(control);
if (layer.Mask is not null)
Children.Remove(layer.Mask);
if (layer.Modal)
{
Children.Remove(layer.Mask);
if (layer.Modal)
{
_modalCount--;
IsInModalStatus = _modalCount > 0;
if (!IsAnimationDisabled)
{
await _maskDisappearAnimation.RunAsync(layer.Mask);
}
}
_modalCount--;
IsInModalStatus = _modalCount > 0;
if (!IsAnimationDisabled) await MaskDisappearAnimation.RunAsync(layer.Mask);
}
ResetZIndices();
}
ResetZIndices();
}
/// <summary>
/// Add a dialog as a modal dialog to the host
/// Add a dialog as a modal dialog to the host
/// </summary>
/// <param name="control"></param>
internal void AddModalDialog(DialogControlBase control)
@@ -166,24 +96,22 @@ public partial class OverlayDialogHost
_layers.Add(new DialogPair(mask, control));
control.SetAsModal(true);
ResetZIndices();
this.Children.Add(mask);
this.Children.Add(control);
Children.Add(mask);
Children.Add(control);
if (control.IsFullScreen)
{
control.Width = Bounds.Width;
control.Height = Bounds.Height;
}
control.MaxWidth = Bounds.Width;
control.MaxHeight = Bounds.Height;
control.Measure(this.Bounds.Size);
control.Measure(Bounds.Size);
control.Arrange(new Rect(control.DesiredSize));
SetToPosition(control);
control.AddHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
control.AddHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
if (!IsAnimationDisabled)
{
_maskAppearAnimation.RunAsync(mask);
}
if (!IsAnimationDisabled) MaskAppearAnimation.RunAsync(mask);
var element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
element?.Focus();
@@ -200,9 +128,9 @@ public partial class OverlayDialogHost
return;
var layer = _layers.FirstOrDefault(a => a.Element == control);
if (layer is null) return;
int index = _layers.IndexOf(layer);
var index = _layers.IndexOf(layer);
_layers.Remove(layer);
int newIndex = index;
var newIndex = index;
switch (e.ChangeType)
{
case DialogLayerChangeType.BringForward:
@@ -222,63 +150,21 @@ public partial class OverlayDialogHost
_layers.Insert(newIndex, layer);
ResetZIndices();
}
private void SetToPosition(DialogControlBase? control)
{
if (control is null) return;
double left = GetLeftPosition(control);
double top = GetTopPosition(control);
var left = GetLeftPosition(control);
var top = GetTopPosition(control);
SetLeft(control, left);
SetTop(control, top);
AnchorAndUpdatePositionInfo(control);
}
private void AnchorAndUpdatePositionInfo(DialogControlBase control)
{
control.ActualHorizontalAnchor = HorizontalPosition.Center;
control.ActualVerticalAnchor = VerticalPosition.Center;
double left = GetLeft(control);
double top = GetTop(control);
double right = Bounds.Width - left - control.Bounds.Width;
double bottom = Bounds.Height - top - control.Bounds.Height;
if(top < SnapThickness.Top)
{
SetTop(control, 0);
control.ActualVerticalAnchor = VerticalPosition.Top;
control.VerticalOffsetRatio = 0;
}
if(bottom < SnapThickness.Bottom)
{
SetTop(control, Bounds.Height - control.Bounds.Height);
control.ActualVerticalAnchor = VerticalPosition.Bottom;
control.VerticalOffsetRatio = 1;
}
if(left < SnapThickness.Left)
{
SetLeft(control, 0);
control.ActualHorizontalAnchor = HorizontalPosition.Left;
control.HorizontalOffsetRatio = 0;
}
if(right < SnapThickness.Right)
{
SetLeft(control, Bounds.Width - control.Bounds.Width);
control.ActualHorizontalAnchor = HorizontalPosition.Right;
control.HorizontalOffsetRatio = 1;
}
left = GetLeft(control);
top = GetTop(control);
right = Bounds.Width - left - control.Bounds.Width;
bottom = Bounds.Height - top - control.Bounds.Height;
control.HorizontalOffsetRatio = (left + right) == 0 ? 0 : left / (left + right);
control.VerticalOffsetRatio = (top + bottom) == 0 ? 0 : top / (top + bottom);
control.AnchorAndUpdatePositionInfo();
}
private double GetLeftPosition(DialogControlBase control)
{
double left;
double offset = Math.Max(0, control.HorizontalOffset ?? 0);
left = this.Bounds.Width - control.Bounds.Width;
var offset = Math.Max(0, control.HorizontalOffset ?? 0);
var left = Bounds.Width - control.Bounds.Width;
if (control.HorizontalAnchor == HorizontalPosition.Center)
{
left *= 0.5;
@@ -290,20 +176,18 @@ public partial class OverlayDialogHost
}
else if (control.HorizontalAnchor == HorizontalPosition.Right)
{
double leftOffset = Bounds.Width - control.Bounds.Width - offset;
var leftOffset = Bounds.Width - control.Bounds.Width - offset;
leftOffset = Math.Max(0, leftOffset);
if(control.HorizontalOffset.HasValue)
{
left = MathHelpers.SafeClamp(left, 0, leftOffset);
}
if (control.HorizontalOffset.HasValue) left = MathHelpers.SafeClamp(left, 0, leftOffset);
}
return left;
}
}
private double GetTopPosition(DialogControlBase control)
{
double offset = Math.Max(0, control.VerticalOffset ?? 0);
var top = this.Bounds.Height - control.Bounds.Height;
var offset = Math.Max(0, control.VerticalOffset ?? 0);
var top = Bounds.Height - control.Bounds.Height;
if (control.VerticalAnchor == VerticalPosition.Center)
{
top *= 0.5;
@@ -318,8 +202,7 @@ public partial class OverlayDialogHost
var topOffset = Math.Max(0, Bounds.Height - control.Bounds.Height - offset);
top = MathHelpers.SafeClamp(top, 0, topOffset);
}
return top;
}
}

View File

@@ -42,7 +42,7 @@ public partial class OverlayDialogHost
}
else
{
await Task.WhenAll(animation.RunAsync(control), _maskAppearAnimation.RunAsync(mask));
await Task.WhenAll(animation.RunAsync(control), MaskAppearAnimation.RunAsync(mask));
}
}
}
@@ -67,7 +67,7 @@ public partial class OverlayDialogHost
}
else
{
await Task.WhenAll(animation.RunAsync(control), _maskAppearAnimation.RunAsync(mask));
await Task.WhenAll(animation.RunAsync(control), MaskAppearAnimation.RunAsync(mask));
}
var element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
element?.Focus();
@@ -174,7 +174,7 @@ public partial class OverlayDialogHost
if (!IsAnimationDisabled)
{
var disappearAnimation = CreateAnimation(control.Bounds.Size, control.Position, false);
await Task.WhenAll(disappearAnimation.RunAsync(control), _maskDisappearAnimation.RunAsync(layer.Mask));
await Task.WhenAll(disappearAnimation.RunAsync(control), MaskDisappearAnimation.RunAsync(layer.Mask));
}
Children.Remove(layer.Mask);
}

View File

@@ -14,8 +14,8 @@ namespace Ursa.Controls;
public partial class OverlayDialogHost: Canvas
{
private static readonly Animation _maskAppearAnimation;
private static readonly Animation _maskDisappearAnimation;
private static readonly Animation MaskAppearAnimation;
private static readonly Animation MaskDisappearAnimation;
private readonly List<DialogPair> _layers = new List<DialogPair>(10);
@@ -61,8 +61,8 @@ public partial class OverlayDialogHost: Canvas
static OverlayDialogHost()
{
ClipToBoundsProperty.OverrideDefaultValue<OverlayDialogHost>(true);
_maskAppearAnimation = CreateOpacityAnimation(true);
_maskDisappearAnimation = CreateOpacityAnimation(false);
MaskAppearAnimation = CreateOpacityAnimation(true);
MaskDisappearAnimation = CreateOpacityAnimation(false);
}
private static Animation CreateOpacityAnimation(bool appear)
@@ -128,28 +128,21 @@ public partial class OverlayDialogHost: Canvas
{
return;
}
if (sender is PureRectangle mask)
if (sender is not PureRectangle mask) return;
if(TopLevel.GetTopLevel(mask) is Window window)
{
var window = this.GetVisualAncestors().OfType<Window>().FirstOrDefault();
if(window is not null)
{
window.BeginMoveDrag(e);
}
window.BeginMoveDrag(e);
}
}
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);
border.RemoveHandler(PointerPressedEvent, DragMaskToMoveWindow);
}
}
if (sender is not PureRectangle border) return;
var layer = _layers.FirstOrDefault(a => a.Mask == border);
if (layer is null) return;
border.RemoveHandler(PointerReleasedEvent, ClickMaskToCloseDialog);
border.RemoveHandler(PointerPressedEvent, DragMaskToMoveWindow);
layer.Element.Close();
}
private IDisposable? _modalStatusSubscription;
private int? _toplevelHash;

View File

@@ -1,90 +1,201 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Irihi.Avalonia.Shared.Contracts;
using Irihi.Avalonia.Shared.Helpers;
using Ursa.EventArgs;
namespace Ursa.Controls.OverlayShared;
public abstract class OverlayFeedbackElement: ContentControl
public abstract class OverlayFeedbackElement : ContentControl
{
public static readonly StyledProperty<bool> IsClosedProperty =
AvaloniaProperty.Register<OverlayFeedbackElement, bool>(nameof(IsClosed), defaultValue: true);
AvaloniaProperty.Register<OverlayFeedbackElement, bool>(nameof(IsClosed), true);
public static readonly RoutedEvent<ResultEventArgs> ClosedEvent =
RoutedEvent.Register<DrawerControlBase, ResultEventArgs>(
nameof(Closed), RoutingStrategies.Bubble);
private bool _resizeDragging;
protected Panel? ContainerPanel;
private Rect _resizeDragStartBounds;
private Point _resizeDragStartPoint;
private WindowEdge? _windowEdge;
static OverlayFeedbackElement()
{
FocusableProperty.OverrideDefaultValue<OverlayFeedbackElement>(false);
DataContextProperty.Changed.AddClassHandler<OverlayFeedbackElement, object?>((o, e) =>
o.OnDataContextChange(e));
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o, e) => o.OnClosed(e));
}
public bool IsClosed
{
get => GetValue(IsClosedProperty);
set => SetValue(IsClosedProperty, value);
}
static OverlayFeedbackElement()
{
FocusableProperty.OverrideDefaultValue<OverlayFeedbackElement>(false);
DataContextProperty.Changed.AddClassHandler<OverlayFeedbackElement, object?>((o, e) => o.OnDataContextChange(e));
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o,e)=>o.OnClosed(e));
}
private void OnClosed(ResultEventArgs _)
{
SetCurrentValue(IsClosedProperty,true);
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;
}
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);
});
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;
}
public abstract void Close();
internal void BeginResizeDrag(WindowEdge windowEdge, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
_resizeDragging = true;
_resizeDragStartPoint = e.GetPosition(this);
_resizeDragStartBounds = Bounds;
_windowEdge = windowEdge;
}
internal void BeginMoveDrag(PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
_resizeDragging = true;
_resizeDragStartPoint = e.GetPosition(this);
_resizeDragStartBounds = Bounds;
_windowEdge = null;
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
ContainerPanel = this.FindAncestorOfType<Panel>();
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);
_resizeDragging = false;
}
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
base.OnPointerCaptureLost(e);
_resizeDragging = false;
}
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
if (!_resizeDragging || _windowEdge is null) return;
var point = e.GetPosition(this);
Vector diff = point - _resizeDragStartPoint;
var left = Canvas.GetLeft(this);
var top = Canvas.GetTop(this);
var width = _windowEdge is WindowEdge.West or WindowEdge.NorthWest or WindowEdge.SouthWest
? Bounds.Width : _resizeDragStartBounds.Width;
var height = _windowEdge is WindowEdge.North or WindowEdge.NorthEast or WindowEdge.NorthWest
? Bounds.Height : _resizeDragStartBounds.Height;
var newBounds = CalculateNewBounds(left, top, width, height, diff, ContainerPanel?.Bounds, _windowEdge.Value);
Canvas.SetLeft(this, newBounds.Left);
Canvas.SetTop(this, newBounds.Top);
SetCurrentValue(WidthProperty, newBounds.Width);
SetCurrentValue(HeightProperty, newBounds.Height);
AnchorAndUpdatePositionInfo();
}
private Rect CalculateNewBounds(double left, double top, double width, double height, Vector diff, Rect? containerBounds,
WindowEdge windowEdge)
{
diff = CoerceDelta(left, top, width, height, diff, containerBounds, windowEdge);
switch (windowEdge)
{
case WindowEdge.North:
top += diff.Y; height -= diff.Y;
break;
case WindowEdge.NorthEast:
top += diff.Y; width += diff.X; height -= diff.Y;
break;
case WindowEdge.East:
width += diff.X;
break;
case WindowEdge.SouthEast:
width += diff.X; height += diff.Y;
break;
case WindowEdge.South:
height += diff.Y;
break;
case WindowEdge.SouthWest:
left += diff.X; width -= diff.X; height += diff.Y;
break;
case WindowEdge.West:
left += diff.X; width -= diff.X;
break;
case WindowEdge.NorthWest:
left += diff.X; top += diff.Y; width -= diff.X; height -= diff.Y;
break;
}
return new Rect(left, top, width, height);
}
private Vector CoerceDelta(double left, double top, double width, double height, Vector diff, Rect? containerBounds,
WindowEdge windowEdge)
{
if (containerBounds is null) return diff;
var minX = windowEdge is WindowEdge.West or WindowEdge.NorthWest or WindowEdge.SouthWest
? -left
: -width;
var minY = windowEdge is WindowEdge.North or WindowEdge.NorthEast or WindowEdge.NorthWest
? -top
: -height;
var maxX = windowEdge is WindowEdge.West or WindowEdge.NorthWest or WindowEdge.SouthWest
? width-MinWidth
: containerBounds.Value.Width - left - width;
var maxY = windowEdge is WindowEdge.North or WindowEdge.NorthEast or WindowEdge.NorthWest
? height-MinWidth
: containerBounds.Value.Height - top - height;
return new Vector(MathHelpers.SafeClamp(diff.X, minX, maxX), MathHelpers.SafeClamp(diff.Y, minY, maxY));
}
protected internal abstract void AnchorAndUpdatePositionInfo();
}

View File

@@ -0,0 +1,75 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls;
public class DialogResizer: TemplatedControl
{
public const string PART_Top = "PART_Top";
public const string PART_Bottom = "PART_Bottom";
public const string PART_Left = "PART_Left";
public const string PART_Right = "PART_Right";
public const string PART_TopLeft = "PART_TopLeft";
public const string PART_TopRight = "PART_TopRight";
public const string PART_BottomLeft = "PART_BottomLeft";
public const string PART_BottomRight = "PART_BottomRight";
private Thumb? _top;
private Thumb? _bottom;
private Thumb? _left;
private Thumb? _right;
private Thumb? _topLeft;
private Thumb? _topRight;
private Thumb? _bottomLeft;
private Thumb? _bottomRight;
public static readonly StyledProperty<ResizeDirection> ResizeDirectionProperty = AvaloniaProperty.Register<DialogResizer, ResizeDirection>(
nameof(ResizeDirection), ResizeDirection.All);
/// <summary>
/// Defines what direction the dialog is allowed to be resized.
/// </summary>
public ResizeDirection ResizeDirection
{
get => GetValue(ResizeDirectionProperty);
set => SetValue(ResizeDirectionProperty, value);
}
static DialogResizer()
{
ResizeDirectionProperty.Changed.AddClassHandler<DialogResizer, ResizeDirection>((resizer, e) => resizer.OnResizeDirectionChanged(e));
}
private void OnResizeDirectionChanged(AvaloniaPropertyChangedEventArgs<ResizeDirection> args)
{
UpdateThumbVisibility(args.NewValue.Value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_top = e.NameScope.Find<Thumb>(PART_Top);
_bottom = e.NameScope.Find<Thumb>(PART_Bottom);
_left = e.NameScope.Find<Thumb>(PART_Left);
_right = e.NameScope.Find<Thumb>(PART_Right);
_topLeft = e.NameScope.Find<Thumb>(PART_TopLeft);
_topRight = e.NameScope.Find<Thumb>(PART_TopRight);
_bottomLeft = e.NameScope.Find<Thumb>(PART_BottomLeft);
_bottomRight = e.NameScope.Find<Thumb>(PART_BottomRight);
UpdateThumbVisibility(ResizeDirection);
}
private void UpdateThumbVisibility(ResizeDirection direction)
{
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.Top), _top);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.Bottom), _bottom);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.Left), _left);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.Right), _right);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.TopLeft), _topLeft);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.TopRight), _topRight);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.BottomLeft), _bottomLeft);
IsVisibleProperty.SetValue(direction.HasFlag(ResizeDirection.BottomRight), _bottomRight);
}
}

View File

@@ -0,0 +1,48 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Ursa.Controls.OverlayShared;
namespace Ursa.Controls;
public class DialogResizerThumb: Thumb
{
private OverlayFeedbackElement? _dialog;
public static readonly StyledProperty<ResizeDirection> ResizeDirectionProperty = AvaloniaProperty.Register<DialogResizerThumb, ResizeDirection>(
nameof(ResizeDirection));
public ResizeDirection ResizeDirection
{
get => GetValue(ResizeDirectionProperty);
set => SetValue(ResizeDirectionProperty, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_dialog = this.FindLogicalAncestorOfType<OverlayFeedbackElement>();
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (_dialog is null) return;
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
var windowEdge = ResizeDirection switch
{
ResizeDirection.Top => WindowEdge.North,
ResizeDirection.TopRight => WindowEdge.NorthEast,
ResizeDirection.Right => WindowEdge.East,
ResizeDirection.BottomRight => WindowEdge.SouthEast,
ResizeDirection.Bottom => WindowEdge.South,
ResizeDirection.BottomLeft => WindowEdge.SouthWest,
ResizeDirection.Left => WindowEdge.West,
ResizeDirection.TopLeft => WindowEdge.NorthWest,
_ => throw new ArgumentOutOfRangeException()
};
_dialog.BeginResizeDrag(windowEdge, e);
}
}

View File

@@ -1,13 +1,18 @@
namespace Ursa.Controls;
[Flags]
public enum ResizeDirection
{
Top,
Bottom,
Left,
Right,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Top = 1,
Bottom = 2,
Left = 4,
Right = 8,
TopLeft = 16,
TopRight = 32,
BottomLeft = 64,
BottomRight = 128,
Sides = Top | Bottom | Left | Right,
Corners = TopLeft | TopRight | BottomLeft | BottomRight,
All = Sides | Corners,
}