using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.LogicalTree; using Avalonia.Styling; namespace Ursa.Controls; public abstract class ThemeSelectorBase: TemplatedControl { private bool _syncFromScope; private Application? _application; public static readonly StyledProperty SelectedThemeProperty = AvaloniaProperty.Register( nameof(SelectedTheme)); public ThemeVariant? SelectedTheme { get => GetValue(SelectedThemeProperty); set => SetValue(SelectedThemeProperty, value); } public static readonly StyledProperty ModeProperty = AvaloniaProperty.Register( nameof(Mode)); public ThemeSelectorMode Mode { get => GetValue(ModeProperty); set => SetValue(ModeProperty, value); } public static readonly StyledProperty TargetScopeProperty = AvaloniaProperty.Register( nameof(TargetScope)); public ThemeVariantScope? TargetScope { get => GetValue(TargetScopeProperty); set => SetValue(TargetScopeProperty, value); } static ThemeSelectorBase() { SelectedThemeProperty.Changed.AddClassHandler((s, e) => s.OnSelectedThemeChanged(e)); TargetScopeProperty.Changed.AddClassHandler((s, e) => s.OnTargetScopeChanged(e)); } private void OnTargetScopeChanged(AvaloniaPropertyChangedEventArgs 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 (TargetScope is { } target) { SyncThemeFromScope(Mode == ThemeSelectorMode.Controller ? target.RequestedThemeVariant : target.ActualThemeVariant); } else if (_application is { } app) { SyncThemeFromScope(Mode == ThemeSelectorMode.Controller ? app.RequestedThemeVariant : app.ActualThemeVariant); } _syncFromScope = false; } protected virtual void SyncThemeFromScope(ThemeVariant? theme) { SelectedTheme = theme; } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); _application = Application.Current; _syncFromScope = true; if (TargetScope is not null) { TargetScope.ActualThemeVariantChanged += OnScopeThemeChanged; SyncThemeFromScope(Mode == ThemeSelectorMode.Controller ? TargetScope.RequestedThemeVariant : TargetScope.ActualThemeVariant); } else if (this.GetLogicalAncestors().FirstOrDefault(a => a is ThemeVariantScope) is ThemeVariantScope scope) { TargetScope = scope; TargetScope.ActualThemeVariantChanged += OnScopeThemeChanged; SyncThemeFromScope(Mode == ThemeSelectorMode.Controller ? TargetScope.RequestedThemeVariant : TargetScope.ActualThemeVariant); } else if (_application is not null) { _application.ActualThemeVariantChanged += OnScopeThemeChanged; SyncThemeFromScope(Mode == ThemeSelectorMode.Controller ? _application.RequestedThemeVariant : _application.ActualThemeVariant); } _syncFromScope = false; } protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) { base.OnDetachedFromVisualTree(e); if (_application is not null) { _application.ActualThemeVariantChanged -= OnScopeThemeChanged; } if (TargetScope is not null) { TargetScope.ActualThemeVariantChanged -= OnScopeThemeChanged; } } protected virtual void OnSelectedThemeChanged(AvaloniaPropertyChangedEventArgs args) { if (_syncFromScope) return; ThemeVariant? newTheme = args.NewValue.Value; if (TargetScope is not null) { TargetScope.RequestedThemeVariant = newTheme; return; } if (_application is not null) { _application.RequestedThemeVariant = newTheme; } } }