feat: add demo, sync theme from scope.

This commit is contained in:
rabbitism
2024-02-06 22:06:52 +08:00
parent 5f35f574a9
commit b297b3f5aa
8 changed files with 124 additions and 6 deletions

View File

@@ -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";
}

View File

@@ -0,0 +1,26 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:u="https://irihi.tech/ursa"
mc:Ignorable="d" d:DesignWidth="800"
d:DesignHeight="450"
x:Class="Ursa.Demo.Pages.ThemeTogglerDemo">
<Grid ColumnDefinitions="Auto, *">
<StackPanel Grid.Column="0">
<TextBlock Text="Global"></TextBlock>
<u:ThemeToggleButton/>
<TextBlock Text="Target To Scope"></TextBlock>
<u:ThemeToggleButton TargetScope="{Binding #scope}"></u:ThemeToggleButton>
</StackPanel>
<ThemeVariantScope Grid.Column="1" Name="scope" RequestedThemeVariant="Dark">
<Border Theme="{DynamicResource CardBorder}">
<StackPanel>
<Button Content="Hello World"></Button>
<Calendar></Calendar>
<u:ThemeToggleButton></u:ThemeToggleButton>
</StackPanel>
</Border>
</ThemeVariantScope>
</Grid>
</UserControl>

View File

@@ -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();
}
}

View File

@@ -47,6 +47,7 @@ public class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
MenuKeys.MenuKeyThemeToggler => new ThemeTogglerDemoViewModel(),
};
}
}

View File

@@ -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"},
};

View File

@@ -0,0 +1,6 @@
namespace Ursa.Demo.ViewModels;
public class ThemeTogglerDemoViewModel
{
}

View File

@@ -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<ThemeSelectorBase, ThemeVariant?>((s, e) => s.OnSelectedThemeChanged(e));
TargetScopeProperty.Changed.AddClassHandler<ThemeSelectorBase, ThemeVariantScope?>((s, e) => s.OnTargetScopeChanged(e));
}
private void OnTargetScopeChanged(AvaloniaPropertyChangedEventArgs<ThemeVariantScope?> 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<ThemeVariant?> args)
{
if (_syncFromScope) return;
ThemeVariant? newTheme = args.NewValue.Value;
if (newTheme is null) return;
if (TargetScope is not null)

View File

@@ -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<ToggleButton>(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);
}
}