From 92011c79ca686ff7f8b19a9aa77f663a1777d51c Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Sun, 23 Feb 2025 22:13:02 +0800 Subject: [PATCH 1/6] feat: add splash window. --- demo/Sandbox/App.axaml.cs | 6 +- demo/Sandbox/Views/MainSplashWindow.axaml | 12 ++++ demo/Sandbox/Views/MainSplashWindow.axaml.cs | 27 +++++++++ demo/Ursa.Demo/App.axaml.cs | 5 +- demo/Ursa.Demo/Views/MainSplashWindow.axaml | 54 ++++++++++++++++++ .../Ursa.Demo/Views/MainSplashWindow.axaml.cs | 24 ++++++++ demo/Ursa.Demo/Views/MainWindow.axaml | 1 + src/Ursa/Windows/SplashWindow.cs | 56 +++++++++++++++++++ 8 files changed, 177 insertions(+), 8 deletions(-) create mode 100644 demo/Sandbox/Views/MainSplashWindow.axaml create mode 100644 demo/Sandbox/Views/MainSplashWindow.axaml.cs create mode 100644 demo/Ursa.Demo/Views/MainSplashWindow.axaml create mode 100644 demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs create mode 100644 src/Ursa/Windows/SplashWindow.cs diff --git a/demo/Sandbox/App.axaml.cs b/demo/Sandbox/App.axaml.cs index 3ead0df..e086f87 100644 --- a/demo/Sandbox/App.axaml.cs +++ b/demo/Sandbox/App.axaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; @@ -21,10 +22,7 @@ public partial class App : Application // Line below is needed to remove Avalonia data validation. // Without this line you will get duplicate validations from both Avalonia and CT BindingPlugins.DataValidators.RemoveAt(0); - desktop.MainWindow = new MainWindow - { - DataContext = new MainWindowViewModel(), - }; + desktop.MainWindow = new MainSplashWindow(); } base.OnFrameworkInitializationCompleted(); diff --git a/demo/Sandbox/Views/MainSplashWindow.axaml b/demo/Sandbox/Views/MainSplashWindow.axaml new file mode 100644 index 0000000..e5e6b73 --- /dev/null +++ b/demo/Sandbox/Views/MainSplashWindow.axaml @@ -0,0 +1,12 @@ + + Welcome to Avalonia Splash Window! + diff --git a/demo/Sandbox/Views/MainSplashWindow.axaml.cs b/demo/Sandbox/Views/MainSplashWindow.axaml.cs new file mode 100644 index 0000000..58546f1 --- /dev/null +++ b/demo/Sandbox/Views/MainSplashWindow.axaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using System.Timers; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using Sandbox.ViewModels; +using Ursa.Controls; + +namespace Sandbox.Views; + +public partial class MainSplashWindow : SplashWindow +{ + public MainSplashWindow() + { + InitializeComponent(); + } + + protected override async Task CreateNextWindow() + { + return new MainWindow() + { + DataContext = new MainWindowViewModel() + }; + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/App.axaml.cs b/demo/Ursa.Demo/App.axaml.cs index 980608c..01d6f16 100644 --- a/demo/Ursa.Demo/App.axaml.cs +++ b/demo/Ursa.Demo/App.axaml.cs @@ -18,10 +18,7 @@ public partial class App : Application { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow() - { - DataContext = new MainViewViewModel(), - }; + desktop.MainWindow = new MainSplashWindow(); } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) { diff --git a/demo/Ursa.Demo/Views/MainSplashWindow.axaml b/demo/Ursa.Demo/Views/MainSplashWindow.axaml new file mode 100644 index 0000000..b4942ed --- /dev/null +++ b/demo/Ursa.Demo/Views/MainSplashWindow.axaml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs b/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs new file mode 100644 index 0000000..b5974ea --- /dev/null +++ b/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Ursa.Controls; +using Ursa.Demo.ViewModels; + +namespace Ursa.Demo.Views; + +public partial class MainSplashWindow : SplashWindow +{ + public MainSplashWindow() + { + InitializeComponent(); + } + + protected override async Task CreateNextWindow() + { + return new MainWindow() + { + DataContext = new MainViewViewModel() + }; + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/Views/MainWindow.axaml b/demo/Ursa.Demo/Views/MainWindow.axaml index ed3ec2b..6136fae 100644 --- a/demo/Ursa.Demo/Views/MainWindow.axaml +++ b/demo/Ursa.Demo/Views/MainWindow.axaml @@ -13,6 +13,7 @@ d:DesignWidth="800" x:CompileBindings="True" x:DataType="viewModels:MainWindowViewModel" + WindowStartupLocation="CenterScreen" Icon="/Assets/Ursa.ico" IsFullScreenButtonVisible="{OnPlatform True, macOS=False}" IsManagedResizerVisible="{OnPlatform False, Linux=True}" diff --git a/src/Ursa/Windows/SplashWindow.cs b/src/Ursa/Windows/SplashWindow.cs new file mode 100644 index 0000000..c63196a --- /dev/null +++ b/src/Ursa/Windows/SplashWindow.cs @@ -0,0 +1,56 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Interactivity; +using Avalonia.Threading; + +namespace Ursa.Controls; + +public abstract class SplashWindow: Window +{ + public static readonly StyledProperty CountDownProperty = AvaloniaProperty.Register( + nameof(CountDown)); + + public TimeSpan? CountDown + { + get => GetValue(CountDownProperty); + set => SetValue(CountDownProperty, value); + } + + protected override void OnLoaded(RoutedEventArgs e) + { + base.OnLoaded(e); + if (CountDown != null && CountDown != TimeSpan.Zero) + { + DispatcherTimer.RunOnce(Close, CountDown.Value); + } + } + + protected virtual Task CanClose() => Task.FromResult(true); + protected abstract Task CreateNextWindow(); + + private bool _canClose; + + protected override sealed async void OnClosing(WindowClosingEventArgs e) + { + VerifyAccess(); + if (!_canClose) + { + e.Cancel = true; + _canClose = await CanClose(); + if (_canClose) + { + var nextWindow = await CreateNextWindow(); + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + { + lifetime.MainWindow = nextWindow; + } + nextWindow.Show(); + Close(); + return; + } + } + base.OnClosing(e); + + } +} \ No newline at end of file From b20137e8aacf861fd1bf628635fafac7c164b0b3 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:44:37 +0800 Subject: [PATCH 2/6] feat: add default Theme to SplashWindow. --- demo/Ursa.Demo/Views/MainSplashWindow.axaml | 15 ++--------- .../Controls/SplashWindow.axaml | 27 +++++++++++++++++++ src/Ursa.Themes.Semi/Controls/_index.axaml | 1 + src/Ursa/Windows/SplashWindow.cs | 2 ++ 4 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 src/Ursa.Themes.Semi/Controls/SplashWindow.axaml diff --git a/demo/Ursa.Demo/Views/MainSplashWindow.axaml b/demo/Ursa.Demo/Views/MainSplashWindow.axaml index b4942ed..6bb95af 100644 --- a/demo/Ursa.Demo/Views/MainSplashWindow.axaml +++ b/demo/Ursa.Demo/Views/MainSplashWindow.axaml @@ -2,22 +2,11 @@ x:Class="Ursa.Demo.Views.MainSplashWindow" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:iri="https://irihi.tech/shared" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:u="https://irihi.tech/ursa" Title="MainSplashWindow" Width="400" - Height="400" - d:DesignHeight="450" - d:DesignWidth="800" - CountDown="0:0:3" - ExtendClientAreaChromeHints="NoChrome" - ExtendClientAreaTitleBarHeightHint="0" - ExtendClientAreaToDecorationsHint="True" - SystemDecorations="None" - WindowStartupLocation="CenterScreen" - mc:Ignorable="d"> + Height="400"> - + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml b/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml new file mode 100644 index 0000000..6c03cb8 --- /dev/null +++ b/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + Full + + + None + + + + + \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml index 541005b..ce51210 100644 --- a/src/Ursa.Themes.Semi/Controls/_index.axaml +++ b/src/Ursa.Themes.Semi/Controls/_index.axaml @@ -56,5 +56,6 @@ + diff --git a/src/Ursa/Windows/SplashWindow.cs b/src/Ursa/Windows/SplashWindow.cs index c63196a..064d906 100644 --- a/src/Ursa/Windows/SplashWindow.cs +++ b/src/Ursa/Windows/SplashWindow.cs @@ -8,6 +8,8 @@ namespace Ursa.Controls; public abstract class SplashWindow: Window { + protected override Type StyleKeyOverride => typeof(SplashWindow); + public static readonly StyledProperty CountDownProperty = AvaloniaProperty.Register( nameof(CountDown)); From aa4507c9c8158ab1e5c88253fbd94969d2b36f64 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Mon, 24 Feb 2025 19:15:55 +0800 Subject: [PATCH 3/6] feat: add mvvm demo. --- demo/Ursa.Demo/App.axaml.cs | 5 +- demo/Ursa.Demo/ViewModels/SplashViewModel.cs | 38 +++++++++++++++ demo/Ursa.Demo/Views/MvvmSplashWindow.axaml | 48 +++++++++++++++++++ .../Ursa.Demo/Views/MvvmSplashWindow.axaml.cs | 24 ++++++++++ src/Ursa/Windows/SplashWindow.cs | 24 ++++++++++ 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 demo/Ursa.Demo/ViewModels/SplashViewModel.cs create mode 100644 demo/Ursa.Demo/Views/MvvmSplashWindow.axaml create mode 100644 demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs diff --git a/demo/Ursa.Demo/App.axaml.cs b/demo/Ursa.Demo/App.axaml.cs index 01d6f16..92e928f 100644 --- a/demo/Ursa.Demo/App.axaml.cs +++ b/demo/Ursa.Demo/App.axaml.cs @@ -18,7 +18,10 @@ public partial class App : Application { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainSplashWindow(); + desktop.MainWindow = new MvvmSplashWindow() + { + DataContext = new SplashViewModel() + }; } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) { diff --git a/demo/Ursa.Demo/ViewModels/SplashViewModel.cs b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs new file mode 100644 index 0000000..c8a7178 --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs @@ -0,0 +1,38 @@ +using System; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.ComponentModel; +using Irihi.Avalonia.Shared.Contracts; + +namespace Ursa.Demo.ViewModels; + +public partial class SplashViewModel: ObservableObject, IDialogContext +{ + [ObservableProperty] private double _progress; + private Random _r = new(); + + public SplashViewModel() + { + DispatcherTimer.Run(OnUpdate, TimeSpan.FromMilliseconds(200), DispatcherPriority.Default); + } + + private bool OnUpdate() + { + Progress += 10 * _r.NextDouble(); + if (Progress <= 100) + { + return true; + } + else + { + RequestClose?.Invoke(this, null); + return false; + } + } + + public void Close() + { + RequestClose?.Invoke(this, null); + } + + public event EventHandler? RequestClose; +} \ No newline at end of file diff --git a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml new file mode 100644 index 0000000..b7868e2 --- /dev/null +++ b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs new file mode 100644 index 0000000..c25f646 --- /dev/null +++ b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Ursa.Controls; +using Ursa.Demo.ViewModels; + +namespace Ursa.Demo.Views; + +public partial class MvvmSplashWindow : SplashWindow +{ + public MvvmSplashWindow() + { + InitializeComponent(); + } + + protected override async Task CreateNextWindow() + { + return new MainWindow() + { + DataContext = new MainViewViewModel() + }; + } +} \ No newline at end of file diff --git a/src/Ursa/Windows/SplashWindow.cs b/src/Ursa/Windows/SplashWindow.cs index 064d906..b82cc7d 100644 --- a/src/Ursa/Windows/SplashWindow.cs +++ b/src/Ursa/Windows/SplashWindow.cs @@ -3,6 +3,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; using Avalonia.Threading; +using Irihi.Avalonia.Shared.Contracts; namespace Ursa.Controls; @@ -18,6 +19,24 @@ public abstract class SplashWindow: Window get => GetValue(CountDownProperty); set => SetValue(CountDownProperty, value); } + + static SplashWindow() + { + DataContextProperty.Changed.AddClassHandler((window, e) => + window.OnDataContextChange(e)); + } + + private void OnDataContextChange(AvaloniaPropertyChangedEventArgs args) + { + if (args.OldValue.Value is IDialogContext oldContext) oldContext.RequestClose -= OnContextRequestClose; + + if (args.NewValue.Value is IDialogContext newContext) newContext.RequestClose += OnContextRequestClose; + } + + private void OnContextRequestClose(object? sender, object? args) + { + Close(args); + } protected override void OnLoaded(RoutedEventArgs e) { @@ -49,6 +68,11 @@ public abstract class SplashWindow: Window } nextWindow.Show(); Close(); + if (DataContext is IDialogContext idc) + { + idc.Close(); + idc.RequestClose -= OnContextRequestClose; + } return; } } From dc2cf01b4347a7709cf8e975a81d385e0e0a9d03 Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Mon, 24 Feb 2025 19:50:02 +0800 Subject: [PATCH 4/6] feat: close directly if next window is null. --- demo/Ursa.Demo/ViewModels/SplashViewModel.cs | 4 +- .../Ursa.Demo/Views/MainSplashWindow.axaml.cs | 2 +- demo/Ursa.Demo/Views/MvvmSplashWindow.axaml | 98 ++++++++++--------- .../Ursa.Demo/Views/MvvmSplashWindow.axaml.cs | 12 ++- src/Ursa/Windows/SplashWindow.cs | 14 ++- 5 files changed, 72 insertions(+), 58 deletions(-) diff --git a/demo/Ursa.Demo/ViewModels/SplashViewModel.cs b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs index c8a7178..76d894c 100644 --- a/demo/Ursa.Demo/ViewModels/SplashViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs @@ -24,14 +24,14 @@ public partial class SplashViewModel: ObservableObject, IDialogContext } else { - RequestClose?.Invoke(this, null); + RequestClose?.Invoke(this, true); return false; } } public void Close() { - RequestClose?.Invoke(this, null); + RequestClose?.Invoke(this, false); } public event EventHandler? RequestClose; diff --git a/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs b/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs index b5974ea..662a15e 100644 --- a/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs +++ b/demo/Ursa.Demo/Views/MainSplashWindow.axaml.cs @@ -14,7 +14,7 @@ public partial class MainSplashWindow : SplashWindow InitializeComponent(); } - protected override async Task CreateNextWindow() + protected override async Task CreateNextWindow() { return new MainWindow() { diff --git a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml index b7868e2..1bd5f41 100644 --- a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml +++ b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml @@ -1,48 +1,54 @@ - - + + + + + + + + + - - - - - - - - - - - + FontSize="14" + Text="聚焦生产力的美学进化" /> + + + diff --git a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs index c25f646..a1bf93b 100644 --- a/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs +++ b/demo/Ursa.Demo/Views/MvvmSplashWindow.axaml.cs @@ -14,11 +14,15 @@ public partial class MvvmSplashWindow : SplashWindow InitializeComponent(); } - protected override async Task CreateNextWindow() + protected override async Task CreateNextWindow() { - return new MainWindow() + if (this.DialogResult is true) { - DataContext = new MainViewViewModel() - }; + return new MainWindow() + { + DataContext = new MainViewViewModel() + }; + } + return null; } } \ No newline at end of file diff --git a/src/Ursa/Windows/SplashWindow.cs b/src/Ursa/Windows/SplashWindow.cs index b82cc7d..ee8feb7 100644 --- a/src/Ursa/Windows/SplashWindow.cs +++ b/src/Ursa/Windows/SplashWindow.cs @@ -35,7 +35,8 @@ public abstract class SplashWindow: Window private void OnContextRequestClose(object? sender, object? args) { - Close(args); + DialogResult = args; + Close(); } protected override void OnLoaded(RoutedEventArgs e) @@ -46,9 +47,11 @@ public abstract class SplashWindow: Window DispatcherTimer.RunOnce(Close, CountDown.Value); } } + + protected object? DialogResult { get; private set; } protected virtual Task CanClose() => Task.FromResult(true); - protected abstract Task CreateNextWindow(); + protected abstract Task CreateNextWindow(); private bool _canClose; @@ -62,16 +65,17 @@ public abstract class SplashWindow: Window if (_canClose) { var nextWindow = await CreateNextWindow(); - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime && nextWindow is not null) { lifetime.MainWindow = nextWindow; } - nextWindow.Show(); + nextWindow?.Show(); Close(); if (DataContext is IDialogContext idc) { - idc.Close(); + // unregister in advance in case developer try to raise event again. idc.RequestClose -= OnContextRequestClose; + idc.Close(); } return; } From 186eed1870dd1b10ddd5fab89e9df3ae23653a2f Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Mon, 24 Feb 2025 19:50:52 +0800 Subject: [PATCH 5/6] feat: remove default count-down. --- src/Ursa.Themes.Semi/Controls/SplashWindow.axaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml b/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml index 6c03cb8..4127e94 100644 --- a/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml +++ b/src/Ursa.Themes.Semi/Controls/SplashWindow.axaml @@ -3,7 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:u="https://irihi.tech/ursa"> - From 381e28ed6be1f44bc26a3cd32cbaec256052f513 Mon Sep 17 00:00:00 2001 From: Zhang Dian <54255897+zdpcdt@users.noreply.github.com> Date: Tue, 25 Feb 2025 00:01:39 +0800 Subject: [PATCH 6/6] feat: reduce TimeSpan. --- demo/Ursa.Demo/ViewModels/SplashViewModel.cs | 2 +- demo/Ursa.Demo/Views/MainSplashWindow.axaml | 2 +- demo/Ursa.Demo/Views/MvvmSplashWindow.axaml | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/demo/Ursa.Demo/ViewModels/SplashViewModel.cs b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs index 76d894c..c2e2d14 100644 --- a/demo/Ursa.Demo/ViewModels/SplashViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/SplashViewModel.cs @@ -12,7 +12,7 @@ public partial class SplashViewModel: ObservableObject, IDialogContext public SplashViewModel() { - DispatcherTimer.Run(OnUpdate, TimeSpan.FromMilliseconds(200), DispatcherPriority.Default); + DispatcherTimer.Run(OnUpdate, TimeSpan.FromMilliseconds(20), DispatcherPriority.Default); } private bool OnUpdate() diff --git a/demo/Ursa.Demo/Views/MainSplashWindow.axaml b/demo/Ursa.Demo/Views/MainSplashWindow.axaml index 6bb95af..8f40138 100644 --- a/demo/Ursa.Demo/Views/MainSplashWindow.axaml +++ b/demo/Ursa.Demo/Views/MainSplashWindow.axaml @@ -17,7 +17,7 @@ Grid.Column="0" Width="64" Margin="0,0,16,0" - Fill="{DynamicResource SemiGrey5}" /> + Fill="{DynamicResource SemiColorText2}" /> + Fill="{DynamicResource SemiColorText2}" /> @@ -51,4 +52,4 @@ Text="Aesthetic Evolution of Productivity" /> - + \ No newline at end of file