feat: implement overlay messagebox, unify messagebox and dialog layout.
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
<ComboBox ItemsSource="{Binding Icons}" SelectedItem="{Binding SelectedIcon}" />
|
||||
<ToggleSwitch Content="Try Long Message" IsChecked="{Binding UseLong}"></ToggleSwitch>
|
||||
<ToggleSwitch Content="Show Title" IsChecked="{Binding UseTitle}"></ToggleSwitch>
|
||||
<ToggleSwitch Content="Overlay" IsChecked="{Binding UseOverlay}"></ToggleSwitch>
|
||||
<Button Command="{Binding DefaultMessageBoxCommand}" Content="Default" />
|
||||
<Button Command="{Binding OkCommand}" Content="OK" />
|
||||
<Button Command="{Binding OkCancelCommand}" Content="OKCancel" />
|
||||
|
||||
@@ -61,7 +61,14 @@ public class MessageBoxDemoViewModel: ObservableObject
|
||||
_title = value ? "Ursa MessageBox" : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool _useOverlay;
|
||||
|
||||
public bool UseOverlay
|
||||
{
|
||||
get => _useOverlay;
|
||||
set => SetProperty(ref _useOverlay, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -80,26 +87,46 @@ public class MessageBoxDemoViewModel: ObservableObject
|
||||
|
||||
private async Task OnDefaultMessageAsync()
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon);
|
||||
if (UseOverlay)
|
||||
{
|
||||
Result = await MessageBox.ShowOverlayAsync(_message, _title, icon: SelectedIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async Task OnOkAsync()
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon, button:MessageBoxButton.OK);
|
||||
await Show(MessageBoxButton.OK);
|
||||
}
|
||||
|
||||
private async Task OnYesNoAsync()
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon, button: MessageBoxButton.YesNo);
|
||||
await Show(MessageBoxButton.YesNo);
|
||||
}
|
||||
|
||||
private async Task OnYesNoCancelAsync()
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon, button: MessageBoxButton.YesNoCancel);
|
||||
await Show(MessageBoxButton.YesNoCancel);
|
||||
}
|
||||
|
||||
private async Task OnOkCancelAsync()
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon, button:MessageBoxButton.OKCancel);
|
||||
await Show(MessageBoxButton.OK);
|
||||
}
|
||||
|
||||
private async Task Show(MessageBoxButton button)
|
||||
{
|
||||
if (UseOverlay)
|
||||
{
|
||||
Result = await MessageBox.ShowOverlayAsync(_message, _title, icon: SelectedIcon, button:button);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = await MessageBox.ShowAsync(_message, _title, icon: SelectedIcon, button:button);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,15 @@
|
||||
<Setter Property="WindowStartupLocation" Value="CenterOwner" />
|
||||
<Setter Property="ExtendClientAreaTitleBarHeightHint" Value="1" />
|
||||
<Setter Property="ExtendClientAreaToDecorationsHint" Value="True" />
|
||||
<Setter Property="ExtendClientAreaChromeHints" Value="SystemChrome"/>
|
||||
<Setter Property="ExtendClientAreaChromeHints" Value="SystemChrome" />
|
||||
<Setter Property="SystemDecorations">
|
||||
<OnPlatform >
|
||||
<OnPlatform.Windows><SystemDecorations>Full</SystemDecorations></OnPlatform.Windows>
|
||||
<OnPlatform.Default><SystemDecorations>BorderOnly</SystemDecorations></OnPlatform.Default>
|
||||
<OnPlatform>
|
||||
<OnPlatform.Windows>
|
||||
<SystemDecorations>Full</SystemDecorations>
|
||||
</OnPlatform.Windows>
|
||||
<OnPlatform.Default>
|
||||
<SystemDecorations>BorderOnly</SystemDecorations>
|
||||
</OnPlatform.Default>
|
||||
</OnPlatform>
|
||||
</Setter>
|
||||
<Setter Property="CanResize" Value="False" />
|
||||
@@ -29,35 +33,34 @@
|
||||
<Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
|
||||
<Border Background="{TemplateBinding Background}" IsHitTestVisible="False" />
|
||||
<Panel Margin="{TemplateBinding WindowDecorationMargin}" Background="Transparent" />
|
||||
<ChromeOverlayLayer></ChromeOverlayLayer>
|
||||
<ChromeOverlayLayer />
|
||||
<Grid RowDefinitions="Auto, *, Auto">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*, Auto">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="8,8,0,0"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap"
|
||||
Text="{TemplateBinding Title}" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,4,0"
|
||||
Theme="{DynamicResource CloseButton}"/>
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
MaxWidth="{DynamicResource MessageBoxWindowContentMaxWidth}"
|
||||
ColumnDefinitions="Auto, *">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto" Margin="24 24 24 0">
|
||||
<PathIcon
|
||||
Name="PART_Icon"
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Margin="0,0,12,0"
|
||||
IsHitTestVisible="False"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
IsHitTestVisible="False"
|
||||
Text="{TemplateBinding Title}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
|
||||
Grid.Column="2"
|
||||
Theme="{DynamicResource CloseButton}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
MaxWidth="{DynamicResource MessageBoxWindowContentMaxWidth}"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
ColumnDefinitions="Auto, *">
|
||||
<ScrollViewer
|
||||
Grid.Column="1"
|
||||
MaxHeight="300"
|
||||
@@ -73,7 +76,7 @@
|
||||
</Grid>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="0,0,8,8"
|
||||
Margin="0,0,24,24"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
@@ -85,17 +88,20 @@
|
||||
Name="{x:Static u:MessageBoxWindow.PART_NoButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Danger"
|
||||
Content="No" />
|
||||
Content="No"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxWindow.PART_YesButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Primary"
|
||||
Content="Yes" />
|
||||
Content="Yes"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxWindow.PART_OKButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Primary"
|
||||
Content="OK" />
|
||||
Content="OK"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Panel>
|
||||
@@ -134,6 +140,138 @@
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiGreen6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogSuccessIconGlyph}" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:MessageBoxControl}" TargetType="u:MessageBoxControl">
|
||||
<Setter Property="CornerRadius" Value="12" />
|
||||
<Setter Property="Padding" Value="48 24"></Setter>
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:MessageBoxControl">
|
||||
<Border
|
||||
Padding="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="Shadow"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="{TemplateBinding CornerRadius}"
|
||||
IsHitTestVisible="True"
|
||||
Theme="{DynamicResource CardBorder}">
|
||||
<Grid RowDefinitions="Auto, *, Auto">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto, *, Auto">
|
||||
<Panel
|
||||
Name="{x:Static u:DialogControl.PART_TitleArea}"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Background="Transparent" />
|
||||
<PathIcon
|
||||
Name="PART_Icon"
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24"
|
||||
Margin="24,24,8,0"
|
||||
IsHitTestVisible="False"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
Name="PART_Title"
|
||||
Grid.Column="1"
|
||||
Margin="0,24,0,0"
|
||||
FontSize="16"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="Bold"
|
||||
Text="{TemplateBinding Title}"
|
||||
IsHitTestVisible="False"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxWindow.PART_CloseButton}"
|
||||
Grid.Column="2"
|
||||
Margin="0,24,24,0"
|
||||
Theme="{DynamicResource CloseButton}" />
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
MaxWidth="{DynamicResource MessageBoxWindowContentMaxWidth}"
|
||||
Margin="{TemplateBinding Padding}">
|
||||
<ScrollViewer
|
||||
MaxHeight="300"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock
|
||||
Name="PART_ContentPresenter"
|
||||
VerticalAlignment="Center"
|
||||
Text="{TemplateBinding Content}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="24,0,24,24"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxControl.PART_CancelButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Tertiary"
|
||||
Content="Cancel" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxControl.PART_NoButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Danger"
|
||||
Content="No"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxControl.PART_YesButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Primary"
|
||||
Content="Yes"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
<Button
|
||||
Name="{x:Static u:MessageBoxControl.PART_OKButton}"
|
||||
Margin="8,0,0,0"
|
||||
Classes="Primary"
|
||||
Content="OK"
|
||||
Theme="{DynamicResource SolidButton}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^[MessageIcon=None] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="False" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=None] /template/ TextBlock#PART_Title">
|
||||
<Setter Property="Margin" Value="24 24 0 0" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Asterisk] /template/ PathIcon#PART_Icon, ^[MessageIcon=Information] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiBlue6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogInformationIconGlyph}" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Error] /template/ PathIcon#PART_Icon, ^[MessageIcon=Hand] /template/ PathIcon#PART_Icon, ^[MessageIcon=Stop] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiRed6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogErrorIconGlyph}" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Exclamation] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiYellow6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogWarningIconGlyph}" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Question] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiBlue6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogQuestionIconGlyph}" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Warning] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiOrange6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogWarningIconGlyph}" />
|
||||
</Style>
|
||||
<Style Selector="^[MessageIcon=Success] /template/ PathIcon#PART_Icon">
|
||||
<Setter Property="IsVisible" Value="True" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SemiGreen6}" />
|
||||
<Setter Property="Data" Value="{DynamicResource DialogSuccessIconGlyph}" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DialogControl: ContentControl
|
||||
public const string PART_TitleArea = "PART_TitleArea";
|
||||
public const string PC_Modal = ":modal";
|
||||
|
||||
private Button? _closeButton;
|
||||
protected internal Button? _closeButton;
|
||||
private Panel? _titleArea;
|
||||
public event EventHandler<DialogLayerChangeEventArgs>? LayerChanged;
|
||||
public event EventHandler<object?>? DialogControlClosing;
|
||||
|
||||
@@ -2,6 +2,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Styling;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
@@ -56,4 +57,25 @@ public static class MessageBox
|
||||
var result = await messageWindow.ShowDialog<MessageBoxResult>(owner);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<MessageBoxResult> ShowOverlayAsync(
|
||||
string message,
|
||||
string? title = null,
|
||||
string? hostId = null,
|
||||
MessageBoxIcon icon = MessageBoxIcon.None,
|
||||
MessageBoxButton button = MessageBoxButton.OKCancel)
|
||||
{
|
||||
var host = OverlayDialogManager.GetHost(hostId);
|
||||
if (host is null) return MessageBoxResult.None;
|
||||
var messageControl = new MessageBoxControl()
|
||||
{
|
||||
Content = message,
|
||||
Title = title,
|
||||
Buttons = button,
|
||||
MessageIcon = icon,
|
||||
};
|
||||
host.AddModalDialog(messageControl);
|
||||
var result = await messageControl.ShowAsync<MessageBoxResult>();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
135
src/Ursa/Controls/MessageBox/MessageBoxControl.cs
Normal file
135
src/Ursa/Controls/MessageBox/MessageBoxControl.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Metadata;
|
||||
using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Styling;
|
||||
using Ursa.Common;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// The messageBox used to display in OverlayDialogHost.
|
||||
/// </summary>
|
||||
[TemplatePart(PART_CloseButton, typeof(Button))]
|
||||
[TemplatePart(PART_NoButton, typeof(Button))]
|
||||
[TemplatePart(PART_OKButton, typeof(Button))]
|
||||
[TemplatePart(PART_CancelButton, typeof(Button))]
|
||||
[TemplatePart(PART_YesButton, typeof(Button))]
|
||||
public class MessageBoxControl: DialogControl
|
||||
{
|
||||
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<MessageBoxIcon> MessageIconProperty =
|
||||
AvaloniaProperty.Register<MessageBoxWindow, MessageBoxIcon>(
|
||||
nameof(MessageIcon));
|
||||
|
||||
public MessageBoxIcon MessageIcon
|
||||
{
|
||||
get => GetValue(MessageIconProperty);
|
||||
set => SetValue(MessageIconProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<MessageBoxButton> ButtonsProperty = AvaloniaProperty.Register<MessageBoxControl, MessageBoxButton>(
|
||||
nameof(Buttons), MessageBoxButton.OK);
|
||||
|
||||
public MessageBoxButton Buttons
|
||||
{
|
||||
get => GetValue(ButtonsProperty);
|
||||
set => SetValue(ButtonsProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<string?> TitleProperty = AvaloniaProperty.Register<MessageBoxControl, string?>(
|
||||
nameof(Title));
|
||||
|
||||
public string? Title
|
||||
{
|
||||
get => GetValue(TitleProperty);
|
||||
set => SetValue(TitleProperty, value);
|
||||
}
|
||||
|
||||
static MessageBoxControl()
|
||||
{
|
||||
ButtonsProperty.Changed.AddClassHandler<MessageBoxControl>((o, e) => { o.SetButtonVisibility(); });
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
EventHelper.UnregisterClickEvent(DefaultButtonsClose, _okButton, _cancelButton, _yesButton, _noButton);
|
||||
_okButton = e.NameScope.Find<Button>(PART_OKButton);
|
||||
_cancelButton = e.NameScope.Find<Button>(PART_CancelButton);
|
||||
_yesButton = e.NameScope.Find<Button>(PART_YesButton);
|
||||
_noButton = e.NameScope.Find<Button>(PART_NoButton);
|
||||
EventHelper.RegisterClickEvent(DefaultButtonsClose, _okButton, _cancelButton, _yesButton, _noButton);
|
||||
SetButtonVisibility();
|
||||
}
|
||||
|
||||
private void DefaultButtonsClose(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button)
|
||||
{
|
||||
if (button == _okButton)
|
||||
{
|
||||
OnDialogControlClosing(this, MessageBoxResult.OK);
|
||||
}
|
||||
else if (button == _cancelButton)
|
||||
{
|
||||
OnDialogControlClosing(this, MessageBoxResult.Cancel);
|
||||
}
|
||||
else if (button == _yesButton)
|
||||
{
|
||||
OnDialogControlClosing(this, MessageBoxResult.Yes);
|
||||
}
|
||||
else if (button == _noButton)
|
||||
{
|
||||
OnDialogControlClosing(this, MessageBoxResult.No);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetButtonVisibility()
|
||||
{
|
||||
switch (Buttons)
|
||||
{
|
||||
case MessageBoxButton.OK:
|
||||
SetVisibility(_okButton, true);
|
||||
SetVisibility(_cancelButton, false);
|
||||
SetVisibility(_yesButton, false);
|
||||
SetVisibility(_noButton, false);
|
||||
break;
|
||||
case MessageBoxButton.OKCancel:
|
||||
SetVisibility(_okButton, true);
|
||||
SetVisibility(_cancelButton, true);
|
||||
SetVisibility(_yesButton, false);
|
||||
SetVisibility(_noButton, false);
|
||||
break;
|
||||
case MessageBoxButton.YesNo:
|
||||
SetVisibility(_okButton, false);
|
||||
SetVisibility(_cancelButton, false);
|
||||
SetVisibility(_yesButton, true);
|
||||
SetVisibility(_noButton, true);
|
||||
break;
|
||||
case MessageBoxButton.YesNoCancel:
|
||||
SetVisibility(_okButton, false);
|
||||
SetVisibility(_cancelButton, true);
|
||||
SetVisibility(_yesButton, true);
|
||||
SetVisibility(_noButton, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetVisibility(Button? button, bool visible)
|
||||
{
|
||||
if (button is not null) button.IsVisible = visible;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using Avalonia.Controls.Primitives;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform;
|
||||
using Ursa.Common;
|
||||
|
||||
namespace Ursa.Controls;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user