feat: WIP dialog window.

This commit is contained in:
rabbitism
2024-01-21 23:40:06 +08:00
parent a0598b6a47
commit 2c361f9f3c
9 changed files with 193 additions and 19 deletions

View File

@@ -13,6 +13,7 @@
mc:Ignorable="d">
<Grid ColumnDefinitions="Auto, *">
<StackPanel Grid.Column="0">
<Button Command="{Binding ShowGlobalDialogCommand}">Show Dialog</Button>
<Button Command="{Binding ShowLocalOverlayDialogCommand}">Show Local Overlay Dialog</Button>
<Button Command="{Binding ShowGlobalOverlayDialogCommand}"> Show Global Overlay Dialog </Button>
</StackPanel>

View File

@@ -1,10 +1,12 @@
using System;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Ursa.Controls;
using Ursa.Demo.Pages;
namespace Ursa.Demo.ViewModels;
@@ -12,19 +14,26 @@ public class DialogDemoViewModel: ObservableObject
{
public ICommand ShowLocalOverlayDialogCommand { get; }
public ICommand ShowGlobalOverlayDialogCommand { get; }
public ICommand ShowGlobalDialogCommand { get; }
public DialogDemoViewModel()
{
ShowLocalOverlayDialogCommand = new RelayCommand(ShowLocalOverlayDialog);
ShowGlobalOverlayDialogCommand = new RelayCommand(ShowGlobalOverlayDialog);
ShowLocalOverlayDialogCommand = new AsyncRelayCommand(ShowLocalOverlayDialog);
ShowGlobalOverlayDialogCommand = new AsyncRelayCommand(ShowGlobalOverlayDialog);
ShowGlobalDialogCommand = new AsyncRelayCommand(ShowGlobalDialog);
}
private async void ShowGlobalOverlayDialog()
private async Task ShowGlobalDialog()
{
await DialogBox.ShowAsync<BadgeDemo, BadgeDemoViewModel, string>(new BadgeDemoViewModel());
}
private async Task ShowGlobalOverlayDialog()
{
await DialogBox.ShowOverlayAsync<Banner, DateTime>(DateTime.Now, "GlobalHost");
}
private async void ShowLocalOverlayDialog()
private async Task ShowLocalOverlayDialog()
{
await DialogBox.ShowOverlayAsync<Banner, DateTime>(DateTime.Now, "LocalHost");
}

View File

@@ -0,0 +1,52 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:u="https://irihi.tech/ursa">
<!-- Add Resources Here -->
<ControlTheme x:Key="{x:Type u:DialogControl}" TargetType="u:DialogControl">
<Setter Property="Template">
<ControlTemplate TargetType="u:DialogControl">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Theme="{DynamicResource CardBorder}" Width="100" Height="100" IsHitTestVisible="True">
<Grid RowDefinitions="Auto, *, Auto">
<ContentPresenter Content="{TemplateBinding Content}"></ContentPresenter>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type u:DialogWindow}" TargetType="u:DialogWindow">
<Setter Property="Title" Value="{x:Null}" />
<Setter Property="Background" Value="{DynamicResource WindowDefaultBackground}" />
<Setter Property="TransparencyBackgroundFallback" Value="{DynamicResource WindowDefaultBackground}" />
<Setter Property="Foreground" Value="{DynamicResource WindowDefaultForeground}" />
<Setter Property="FontSize" Value="{DynamicResource DefaultFontSize}" />
<Setter Property="FontFamily" Value="{DynamicResource DefaultFontFamily}" />
<Setter Property="Padding" Value="48 24" />
<Setter Property="SizeToContent" Value="WidthAndHeight" />
<Setter Property="WindowStartupLocation" Value="CenterOwner" />
<Setter Property="ExtendClientAreaTitleBarHeightHint" Value="1" />
<Setter Property="ExtendClientAreaToDecorationsHint" Value="True" />
<Setter Property="ExtendClientAreaChromeHints" Value="SystemChrome"/>
<Setter Property="SystemDecorations">
<OnPlatform >
<OnPlatform.Windows><SystemDecorations>Full</SystemDecorations></OnPlatform.Windows>
<OnPlatform.Default><SystemDecorations>BorderOnly</SystemDecorations></OnPlatform.Default>
</OnPlatform>
</Setter>
<Setter Property="CanResize" Value="False" />
<Setter Property="Template">
<ControlTemplate TargetType="u:DialogWindow">
<Panel>
<Border Name="PART_TransparencyFallback" IsHitTestVisible="False" />
<Border Background="{TemplateBinding Background}" IsHitTestVisible="False" />
<Panel Margin="{TemplateBinding WindowDecorationMargin}" Background="Transparent" />
<ChromeOverlayLayer></ChromeOverlayLayer>
<Grid RowDefinitions="Auto, *, Auto">
<Button Name="{x:Static u:DialogWindow.PART_CloseButton}" VerticalAlignment="Top">Close</Button>
<ContentPresenter Content="{TemplateBinding Content}"></ContentPresenter>
</Grid>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@@ -4,6 +4,7 @@
<ResourceInclude Source="Badge.axaml" />
<ResourceInclude Source="Banner.axaml" />
<ResourceInclude Source="ButtonGroup.axaml" />
<ResourceInclude Source="Dialog.axaml" />
<ResourceInclude Source="Divider.axaml" />
<ResourceInclude Source="DualBadge.axaml" />
<ResourceInclude Source="IconButton.axaml" />

View File

@@ -1,5 +1,6 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
@@ -7,19 +8,47 @@ namespace Ursa.Controls;
public static class DialogBox
{
public static async Task ShowAsync()
public static async Task<TResult?> ShowAsync<TView, TViewModel, TResult>(TViewModel vm) where TView : Control, new()
{
return;
var window = new DialogWindow()
{
Content = new TView()
{
DataContext = vm,
},
DataContext = vm,
};
var lifetime = Application.Current?.ApplicationLifetime;
if (lifetime is IClassicDesktopStyleApplicationLifetime classLifetime)
{
var main = classLifetime.MainWindow;
if (main is null)
{
window.Show();
return default(TResult);
}
else
{
var result = await window.ShowDialog<TResult>(main);
return result;
}
}
else
{
return default(TResult);
}
}
public static async Task ShowAsync<TView, TViewModel>(TViewModel vm)
where TView: Control, new()
where TViewModel: new()
public static async Task<TResult> ShowAsync<TView, TViewModel, TResult>(Window owner, TViewModel vm) where
TView: Control, new()
{
TView t = new TView();
t.DataContext = vm;
var window = new DialogWindow();
window.Content = new TView();
window.DataContext = vm;
return await window.ShowDialog<TResult>(owner);
}
public static async Task<object?> ShowOverlayAsync<TView, TViewModel>(TViewModel vm, string hostId)
where TView : Control, new()
where TViewModel: new()

View File

@@ -1,8 +1,27 @@
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
namespace Ursa.Controls;
[TemplatePart(PART_CloseButton, typeof(Button))]
public class DialogControl: ContentControl
{
public const string PART_CloseButton = "PART_CloseButton";
private Button? _closeButton;
public event EventHandler OnClose;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
}
public void Show()
{
}
}

View File

@@ -0,0 +1,30 @@
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
namespace Ursa.Controls;
[TemplatePart(PART_CloseButton, typeof(Button))]
public class DialogWindow: Window
{
public const string PART_CloseButton = "PART_CloseButton";
protected override Type StyleKeyOverride { get; } = typeof(DialogWindow);
private Button? _closeButton;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_closeButton = e.NameScope.Find<Button>(PART_CloseButton);
if (_closeButton != null)
{
_closeButton.Click += (sender, args) =>
{
Close();
};
}
}
}

View File

@@ -2,5 +2,6 @@ namespace Ursa.Controls;
public interface IDialogContext
{
object Close();
T? Close<T>();
}

View File

@@ -1,14 +1,26 @@
using System.Collections.Specialized;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Ursa.Controls;
public class OverlayDialogHost: Canvas
{
private readonly List<DialogControl> _dialogs = new();
private readonly List<DialogControl> _modalDialogs = new();
private Rectangle _overlayMask = new()
{
Fill = new SolidColorBrush(new Color(1, 0, 0, 0)),
[Rectangle.ZIndexProperty] = 0,
};
public static readonly StyledProperty<string> HostIdProperty = AvaloniaProperty.Register<OverlayDialogHost, string>(
nameof(HostId));
@@ -39,13 +51,18 @@ public class OverlayDialogHost: Canvas
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
var parent = item.FindAncestorOfType<DialogControl>();
if (parent is null)
{
return;
}
var p = e.GetPosition(this);
var left= p.X - _lastPoint.X;
var top = p.Y - _lastPoint.Y;
left = MathUtilities.Clamp(left, 0, Bounds.Width - item.Bounds.Width);
top = MathUtilities.Clamp(top, 0, Bounds.Height - item.Bounds.Height);
Canvas.SetLeft(item, left);
Canvas.SetTop(item, top);
left = MathUtilities.Clamp(left, 0, Bounds.Width - parent.Bounds.Width);
top = MathUtilities.Clamp(top, 0, Bounds.Height - parent.Bounds.Height);
Canvas.SetLeft(parent, left);
Canvas.SetTop(parent, top);
}
}
}
@@ -55,7 +72,22 @@ public class OverlayDialogHost: Canvas
base.OnPointerPressed(e);
if (e.Source is Control item)
{
_lastPoint = e.GetPosition(item);
var parent = item.FindAncestorOfType<DialogControl>();
if (parent is null)
{
return;
}
_lastPoint = e.GetPosition(parent);
}
}
public void AddDialog(DialogControl control)
{
this.Children.Add(control);
}
public void AddModalDialog(DialogControl control)
{
this.Children.Add(control);
}
}