forked from irihitech/Semi.Avalonia
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request irihitech#95 from irihitech/theme
feat: Theme toggler
- Loading branch information
Showing
17 changed files
with
302 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace Ursa.Demo.ViewModels; | ||
|
||
public class ThemeTogglerDemoViewModel | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<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 TargetType="u:ThemeToggleButton" x:Key="{x:Type u:ThemeToggleButton}"> | ||
<Setter Property="Template"> | ||
<ControlTemplate TargetType="u:ThemeToggleButton"> | ||
<ToggleSwitch | ||
Padding="4" | ||
Name="{x:Static u:ThemeToggleButton.PART_ThemeToggleButton}" | ||
Theme="{DynamicResource ButtonToggleSwitch}"> | ||
<ToggleSwitch.OnContent> | ||
<PathIcon | ||
Width="16" | ||
Height="16" | ||
Data="{DynamicResource ThemeSelectorButtonLightGlyph}" | ||
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" /> | ||
</ToggleSwitch.OnContent> | ||
<ToggleSwitch.OffContent> | ||
<PathIcon | ||
Width="16" | ||
Height="16" | ||
Data="{DynamicResource ThemeSelectorButtonDarkGlyph}" | ||
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" /> | ||
</ToggleSwitch.OffContent> | ||
</ToggleSwitch> | ||
</ControlTemplate> | ||
</Setter> | ||
</ControlTheme> | ||
</ResourceDictionary> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<ResourceDictionary xmlns="https://github.com/avaloniaui" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> | ||
<!-- Add Resources Here --> | ||
<StreamGeometry x:Key="ThemeSelectorButtonDarkGlyph">M12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23ZM17 15C17.476 15 17.9408 14.9525 18.3901 14.862C17.296 17.3011 14.8464 19 12 19C8.13401 19 5 15.866 5 12C5 8.60996 7.40983 5.78277 10.6099 5.13803C10.218 6.01173 10 6.98041 10 8C10 11.866 13.134 15 17 15Z</StreamGeometry> | ||
<StreamGeometry x:Key="ThemeSelectorButtonLightGlyph">M3.55 19.09L4.96 20.5L6.76 18.71L5.34 17.29M12 6C8.69 6 6 8.69 6 12S8.69 18 12 18 18 15.31 18 12C18 8.68 15.31 6 12 6M20 13H23V11H20M17.24 18.71L19.04 20.5L20.45 19.09L18.66 17.29M20.45 5L19.04 3.6L17.24 5.39L18.66 6.81M13 1H11V4H13M6.76 5.39L4.96 3.6L3.55 5L5.34 6.81L6.76 5.39M1 13H4V11H1M13 20H11V23H13</StreamGeometry> | ||
</ResourceDictionary> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Avalonia; | ||
|
||
namespace Ursa.Common; | ||
|
||
public static class PropertyHelper | ||
{ | ||
public static void SetValue<TValue>(AvaloniaProperty<TValue> property, TValue value, params AvaloniaObject?[] elements) | ||
{ | ||
foreach (var element in elements) | ||
{ | ||
element?.SetValue(property, value); | ||
} | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Controls.Primitives; | ||
using Avalonia.LogicalTree; | ||
using Avalonia.Styling; | ||
using Ursa.Common; | ||
using System; | ||
|
||
namespace Ursa.Controls; | ||
|
||
public abstract class ThemeSelectorBase: TemplatedControl | ||
{ | ||
private bool _syncFromScope; | ||
private Application? _application; | ||
private ThemeVariantScope? _scope; | ||
|
||
public static readonly StyledProperty<ThemeVariant?> SelectedThemeProperty = AvaloniaProperty.Register<ThemeSelectorBase, ThemeVariant?>( | ||
nameof(SelectedTheme)); | ||
|
||
public ThemeVariant? SelectedTheme | ||
{ | ||
get => GetValue(SelectedThemeProperty); | ||
set => SetValue(SelectedThemeProperty, value); | ||
} | ||
|
||
public static readonly StyledProperty<ThemeVariantScope?> TargetScopeProperty = | ||
AvaloniaProperty.Register<ThemeSelectorBase, ThemeVariantScope?>( | ||
nameof(TargetScope)); | ||
|
||
public ThemeVariantScope? TargetScope | ||
{ | ||
get => GetValue(TargetScopeProperty); | ||
set => SetValue(TargetScopeProperty, value); | ||
} | ||
|
||
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) | ||
{ | ||
if (args.OldValue.Value is { } oldTarget) | ||
{ | ||
oldTarget.ActualThemeVariantChanged -= OnScopeThemeChanged; | ||
} | ||
if (args.NewValue.Value is { } newTarget) | ||
{ | ||
newTarget.ActualThemeVariantChanged += OnScopeThemeChanged; | ||
SyncThemeFromScope(newTarget.ActualThemeVariant); | ||
} | ||
} | ||
|
||
private void OnScopeThemeChanged(object sender, System.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 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) | ||
{ | ||
TargetScope.RequestedThemeVariant = newTheme; | ||
return; | ||
} | ||
if (_scope is not null) | ||
{ | ||
_scope.RequestedThemeVariant = newTheme; | ||
return; | ||
} | ||
if (_application is not null) | ||
{ | ||
_application.RequestedThemeVariant = newTheme; | ||
return; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Controls.Metadata; | ||
using Avalonia.Controls.Primitives; | ||
using Avalonia.Interactivity; | ||
using Avalonia.Styling; | ||
using Ursa.Common; | ||
|
||
namespace Ursa.Controls; | ||
|
||
[TemplatePart(PART_ThemeToggleButton, typeof(ToggleButton))] | ||
public class ThemeToggleButton: ThemeSelectorBase | ||
{ | ||
public const string PART_ThemeToggleButton = "PART_ThemeToggleButton"; | ||
|
||
/// <summary> | ||
/// This button IsChecked=true means ThemeVariant.Light, IsChecked=false means ThemeVariant.Dark. | ||
/// </summary> | ||
private ToggleButton? _button; | ||
private ThemeVariant? _currentTheme; | ||
|
||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) | ||
{ | ||
base.OnAttachedToVisualTree(e); | ||
_currentTheme = this.ActualThemeVariant; | ||
} | ||
|
||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) | ||
{ | ||
base.OnApplyTemplate(e); | ||
EventHelper.UnregisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button); | ||
_button = e.NameScope.Get<ToggleButton>(PART_ThemeToggleButton); | ||
EventHelper.RegisterEvent(Button.ClickEvent, OnButtonClickedChanged, _button); | ||
PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, _currentTheme == ThemeVariant.Light, _button); | ||
} | ||
|
||
private void OnButtonClickedChanged(object sender, RoutedEventArgs e) | ||
{ | ||
var newTheme = (sender as ToggleButton)!.IsChecked; | ||
if (newTheme is null) return; | ||
SetCurrentValue(SelectedThemeProperty, newTheme.Value ? ThemeVariant.Light : ThemeVariant.Dark); | ||
} | ||
|
||
protected override void SyncThemeFromScope(ThemeVariant? theme) | ||
{ | ||
base.SyncThemeFromScope(theme); | ||
PropertyHelper.SetValue(ToggleButton.IsCheckedProperty, theme == ThemeVariant.Light, _button); | ||
} | ||
} |