feat: add demo, sync theme from scope.
This commit is contained in:
@@ -25,5 +25,6 @@ public static class MenuKeys
|
|||||||
public const string MenuKeyTagInput = "TagInput";
|
public const string MenuKeyTagInput = "TagInput";
|
||||||
public const string MenuKeyTimeline = "Timeline";
|
public const string MenuKeyTimeline = "Timeline";
|
||||||
public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
|
public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
|
||||||
|
public const string MenuKeyThemeToggler = "ThemeToggler";
|
||||||
|
|
||||||
}
|
}
|
||||||
26
demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml
Normal file
26
demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml
Normal 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>
|
||||||
13
demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs
Normal file
13
demo/Ursa.Demo/Pages/ThemeTogglerDemo.axaml.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@ public class MainViewViewModel : ViewModelBase
|
|||||||
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
|
MenuKeys.MenuKeyTagInput => new TagInputDemoViewModel(),
|
||||||
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
|
MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(),
|
||||||
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
|
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
|
||||||
|
MenuKeys.MenuKeyThemeToggler => new ThemeTogglerDemoViewModel(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@ public class MenuViewModel: ViewModelBase
|
|||||||
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
|
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination },
|
||||||
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"},
|
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider, Status = "New"},
|
||||||
new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput },
|
new() { MenuHeader = "TagInput", Key = MenuKeys.MenuKeyTagInput },
|
||||||
|
new() { MenuHeader = "Theme Toggler", Key = MenuKeys.MenuKeyThemeToggler },
|
||||||
new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" },
|
new() { MenuHeader = "Timeline", Key = MenuKeys.MenuKeyTimeline, Status = "Updated" },
|
||||||
new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon, Status = "New"},
|
new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon, Status = "New"},
|
||||||
};
|
};
|
||||||
|
|||||||
6
demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs
Normal file
6
demo/Ursa.Demo/ViewModels/ThemeTogglerDemoViewModel.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ursa.Demo.ViewModels;
|
||||||
|
|
||||||
|
public class ThemeTogglerDemoViewModel
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,11 +3,13 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
|
using Ursa.Common;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
public abstract class ThemeSelectorBase: TemplatedControl
|
public abstract class ThemeSelectorBase: TemplatedControl
|
||||||
{
|
{
|
||||||
|
private bool _syncFromScope;
|
||||||
private Application? _application;
|
private Application? _application;
|
||||||
private ThemeVariantScope? _scope;
|
private ThemeVariantScope? _scope;
|
||||||
|
|
||||||
@@ -33,17 +35,81 @@ public abstract class ThemeSelectorBase: TemplatedControl
|
|||||||
static ThemeSelectorBase()
|
static ThemeSelectorBase()
|
||||||
{
|
{
|
||||||
SelectedThemeProperty.Changed.AddClassHandler<ThemeSelectorBase, ThemeVariant?>((s, e) => s.OnSelectedThemeChanged(e));
|
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)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnAttachedToVisualTree(e);
|
base.OnAttachedToVisualTree(e);
|
||||||
_application = Application.Current;
|
_application = Application.Current;
|
||||||
|
if (_application is not null)
|
||||||
|
{
|
||||||
|
_application.ActualThemeVariantChanged += OnScopeThemeChanged;
|
||||||
|
SyncThemeFromScope(_application.ActualThemeVariant);
|
||||||
|
}
|
||||||
_scope = this.GetLogicalAncestors().FirstOrDefault(a => a is ThemeVariantScope) as ThemeVariantScope;
|
_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)
|
protected virtual void OnSelectedThemeChanged(AvaloniaPropertyChangedEventArgs<ThemeVariant?> args)
|
||||||
{
|
{
|
||||||
|
if (_syncFromScope) return;
|
||||||
ThemeVariant? newTheme = args.NewValue.Value;
|
ThemeVariant? newTheme = args.NewValue.Value;
|
||||||
if (newTheme is null) return;
|
if (newTheme is null) return;
|
||||||
if (TargetScope is not null)
|
if (TargetScope is not null)
|
||||||
|
|||||||
@@ -28,18 +28,22 @@ public class ThemeToggleButton: ThemeSelectorBase
|
|||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
EventHelper.UnregisterEvent(ToggleButton.IsCheckedChangedEvent, OnButtonCheckedChanged, _button);
|
EventHelper.UnregisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button);
|
||||||
_button = e.NameScope.Get<ToggleButton>(PART_ThemeToggleButton);
|
_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);
|
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;
|
var newTheme = (sender as ToggleButton)!.IsChecked;
|
||||||
if (newTheme is null) return;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user