From b297b3f5aa25cd305246d7e98e63f0bac2f8b062 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 6 Feb 2024 22:06:52 +0800 Subject: [PATCH] feat: add demo, sync theme from scope. --- demo/Ursa.Demo/Models/MenuKeys.cs | 1 + demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml | 26 ++++++++ .../Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs | 13 ++++ .../Ursa.Demo/ViewModels/MainViewViewModel.cs | 1 + demo/Ursa.Demo/ViewModels/MenuViewModel.cs | 1 + .../ViewModels/ThemeTogglerDemoViewModel.cs | 6 ++ .../ThemeSelector/ThemeSelectorBase.cs | 66 +++++++++++++++++++ .../ThemeSelector/ThemeToggleButton.cs | 16 +++-- 8 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml create mode 100644 demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs create mode 100644 demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs diff --git a/demo/Ursa.Demo/Models/MenuKeys.cs b/demo/Ursa.Demo/Models/MenuKeys.cs index 2283b6a..60d6ef2 100644 --- a/demo/Ursa.Demo/Models/MenuKeys.cs +++ b/demo/Ursa.Demo/Models/MenuKeys.cs @@ -25,5 +25,6 @@ public static class MenuKeys public const string MenuKeyTagInput = "TagInput"; public const string MenuKeyTimeline = "Timeline"; public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon"; + public const string MenuKeyThemeToggler = "ThemeToggler"; } \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml new file mode 100644 index 0000000..021c289 --- /dev/null +++ b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs new file mode 100644 index 0000000..a8e48c6 --- /dev/null +++ b/demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ursa.Demo.Pages; + +public partial class ThemeTogglerDemo : UserControl +{ + public ThemeTogglerDemo() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index 0206b1d..6797172 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -47,6 +47,7 @@ public class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(), MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(), MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(), + MenuKeys.MenuKeyThemeToggler => new ThemeTogglerDemoViewModel(), }; } } \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index 74b5ff7..cf807af 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -32,6 +32,7 @@ public class MenuViewModel: ViewModelBase new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination }, new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"}, new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput }, + new() { MenuHeader = "Theme Toggler", Key = MenuKeys.MenuKeyThemeToggler }, new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" }, new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon, Status = "New"}, }; diff --git a/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs new file mode 100644 index 0000000..66a65da --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs @@ -0,0 +1,6 @@ +namespace Ursa.Demo.ViewModels; + +public class ThemeTogglerDemoViewModel +{ + +} \ No newline at end of file diff --git a/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs b/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs index 20f240a..033acc3 100644 --- a/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs +++ b/src/Ursa/Controls/ThemeSelector/ThemeSelectorBase.cs @@ -3,11 +3,13 @@ using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; using Avalonia.Styling; +using Ursa.Common; namespace Ursa.Controls; public abstract class ThemeSelectorBase: TemplatedControl { + private bool _syncFromScope; private Application? _application; private ThemeVariantScope? _scope; @@ -33,17 +35,81 @@ public abstract class ThemeSelectorBase: TemplatedControl static ThemeSelectorBase() { SelectedThemeProperty.Changed.AddClassHandler((s, e) => s.OnSelectedThemeChanged(e)); + TargetScopeProperty.Changed.AddClassHandler((s, e) => s.OnTargetScopeChanged(e)); + } + + private void OnTargetScopeChanged(AvaloniaPropertyChangedEventArgs args) + { + var target = args.NewValue.Value; + if (target is not null) + { + SyncThemeFromScope(target.ActualThemeVariant); + target.ActualThemeVariantChanged += OnScopeThemeChanged; + } + } + + private void OnScopeThemeChanged(object sender, EventArgs e) + { + _syncFromScope = true; + if (this.TargetScope is { } target) + { + SyncThemeFromScope(target.ActualThemeVariant); + } + else if (this._scope is { } scope) + { + SyncThemeFromScope(scope.ActualThemeVariant); + } + else if (_application is { } app) + { + SyncThemeFromScope(app.ActualThemeVariant); + } + _syncFromScope = false; + } + + protected internal virtual void SyncThemeFromScope(ThemeVariant? theme) + { + this.SelectedTheme = theme; } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); _application = Application.Current; + if (_application is not null) + { + _application.ActualThemeVariantChanged += OnScopeThemeChanged; + SyncThemeFromScope(_application.ActualThemeVariant); + } _scope = this.GetLogicalAncestors().FirstOrDefault(a => a is ThemeVariantScope) as ThemeVariantScope; + if (_scope is not null) + { + _scope.ActualThemeVariantChanged += OnScopeThemeChanged; + SyncThemeFromScope(_scope.ActualThemeVariant); + } + + if (TargetScope is not null) + { + SyncThemeFromScope(TargetScope.ActualThemeVariant); + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + if (_application is not null) + { + _application.ActualThemeVariantChanged -= OnScopeThemeChanged; + } + + if (_scope is not null) + { + _scope.ActualThemeVariantChanged -= OnScopeThemeChanged; + } } protected virtual void OnSelectedThemeChanged(AvaloniaPropertyChangedEventArgs args) { + if (_syncFromScope) return; ThemeVariant? newTheme = args.NewValue.Value; if (newTheme is null) return; if (TargetScope is not null) diff --git a/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs b/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs index 80cead7..db1188e 100644 --- a/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs +++ b/src/Ursa/Controls/ThemeSelector/ThemeToggleButton.cs @@ -28,18 +28,22 @@ public class ThemeToggleButton: ThemeSelectorBase protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - EventHelper.UnregisterEvent(ToggleButton.IsCheckedChangedEvent, OnButtonCheckedChanged, _button); + EventHelper.UnregisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button); _button = e.NameScope.Get(PART_ThemeToggleButton); - EventHelper.RegisterEvent(ToggleButton.IsCheckedChangedEvent, OnButtonCheckedChanged, _button); + EventHelper.RegisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button); PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, _currentTheme == ThemeVariant.Light, _button); } - private void OnButtonCheckedChanged(object sender, RoutedEventArgs e) + private void OnButtonClickedChanged(object sender, RoutedEventArgs e) { var newTheme = (sender as ToggleButton)!.IsChecked; if (newTheme is null) return; - SelectedTheme = newTheme.Value ? ThemeVariant.Light : ThemeVariant.Dark; + SetCurrentValue(SelectedThemeProperty, newTheme.Value ? ThemeVariant.Light : ThemeVariant.Dark); + } + + protected internal override void SyncThemeFromScope(ThemeVariant? theme) + { + base.SyncThemeFromScope(theme); + PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, theme == ThemeVariant.Light, _button); } - - } \ No newline at end of file