diff --git a/src/AtomUI.Base/AtomUI.Base.csproj b/src/AtomUI.Base/AtomUI.Base.csproj index f59805d27..b79f39bcc 100644 --- a/src/AtomUI.Base/AtomUI.Base.csproj +++ b/src/AtomUI.Base/AtomUI.Base.csproj @@ -17,6 +17,7 @@ + diff --git a/src/AtomUI.Base/Common.cs b/src/AtomUI.Base/Common.cs index a7222efcf..5e5f80920 100644 --- a/src/AtomUI.Base/Common.cs +++ b/src/AtomUI.Base/Common.cs @@ -59,4 +59,11 @@ public enum ColorNameFormat { HexRgb, HexArgb +} + +internal static class AtomUILogArea +{ + public const string Theme = "Theme"; + public const string Control = "Control"; + public const string System = "System"; } \ No newline at end of file diff --git a/src/AtomUI.Controls/Input/LineEdit.cs b/src/AtomUI.Controls/Input/LineEdit.cs index 3ff1ef276..659d6d1a6 100644 --- a/src/AtomUI.Controls/Input/LineEdit.cs +++ b/src/AtomUI.Controls/Input/LineEdit.cs @@ -1,6 +1,8 @@ using AtomUI.Controls.Utils; +using AtomUI.Theme; using Avalonia; using Avalonia.Controls; +using Avalonia.LogicalTree; namespace AtomUI.Controls; @@ -28,6 +30,27 @@ public class LineEdit : TextBox #endregion + #region 内部属性定义 + + private ControlTokenResourceRegister _controlTokenResourceRegister; + private bool _initialized; + #endregion + + public LineEdit() + { + _controlTokenResourceRegister = new ControlTokenResourceRegister(this, LineEditToken.ID); + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + base.OnAttachedToLogicalTree(e); + if (!_initialized) + { + _controlTokenResourceRegister.RegisterResources(); + _initialized = true; + } + } + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); diff --git a/src/AtomUI.Controls/Utils/WaveSpiritAdorner.cs b/src/AtomUI.Controls/Utils/WaveSpiritAdorner.cs index 5c37a63fd..1fde77748 100644 --- a/src/AtomUI.Controls/Utils/WaveSpiritAdorner.cs +++ b/src/AtomUI.Controls/Utils/WaveSpiritAdorner.cs @@ -1,3 +1,4 @@ +using AtomUI.Theme.Styling; using AtomUI.Theme.Utils; using Avalonia; using Avalonia.Animation; @@ -115,7 +116,59 @@ internal class WaveSpiritAdorner : Control, IDisposable { _targetControl = targetControl; _targetControl.PropertyChanged += HandleTargetControlChanged; - var sharedToken = TokenFinderUtils.FindSharedToken(_targetControl!); + var sharedToken = TokenFinderUtils.FindSharedToken(_targetControl!); + var themeVariant = TokenFinderUtils.FindThemeVariant(_targetControl); + + CornerRadius? borderRadius = null; + // 获取相关值 + { + if (_targetControl.TryGetResource(SharedTokenKey.BorderRadius, themeVariant, out var value)) + { + if (value is CornerRadius cornerRadius) + { + borderRadius = cornerRadius; + } + } + } + + borderRadius ??= sharedToken.BorderRadius; + + TimeSpan? motionDurationSlow = null; + { + if (_targetControl.TryGetResource(SharedTokenKey.MotionDurationSlow, themeVariant, out var value)) + { + if (value is TimeSpan timeSpan) + { + motionDurationSlow = timeSpan; + } + } + } + motionDurationSlow ??= sharedToken.MotionDurationSlow; + + double? waveStartOpacity = null; + { + if (_targetControl.TryGetResource(SharedTokenKey.WaveStartOpacity, themeVariant, out var value)) + { + if (value is double dvalue) + { + waveStartOpacity = dvalue; + } + } + } + waveStartOpacity ??= sharedToken.WaveStartOpacity; + + double? waveAnimationRange = null; + { + if (_targetControl.TryGetResource(SharedTokenKey.WaveAnimationRange, themeVariant, out var value)) + { + if (value is double dvalue) + { + waveAnimationRange = dvalue; + } + } + } + waveAnimationRange ??= sharedToken.WaveAnimationRange; + if (waveType == WaveType.CircleWave) { _wavePainter = new CircleWavePainter(); @@ -127,24 +180,34 @@ internal class WaveSpiritAdorner : Control, IDisposable else { var roundWavePainter = new RoundRectWavePainter(); - roundWavePainter.CornerRadius = sharedToken.BorderRadius; + roundWavePainter.CornerRadius = borderRadius.Value; _wavePainter = roundWavePainter; } - - var motionDurationSlow = sharedToken.MotionDurationSlow; + _wavePainter.SizeEasingCurve = new CubicEaseOut(); _wavePainter.OpacityEasingCurve = new CubicEaseOut(); - _wavePainter.OriginOpacity = Math.Clamp(sharedToken.WaveStartOpacity, 0.0, 1.0); - _wavePainter.SizeMotionDuration = motionDurationSlow; - _wavePainter.OpacityMotionDuration = motionDurationSlow.Add(TimeSpan.FromMilliseconds(50)); - _wavePainter.WaveRange = Math.Min(sharedToken.WaveAnimationRange, 8); + _wavePainter.OriginOpacity = Math.Clamp(waveStartOpacity.Value, 0.0, 1.0); + _wavePainter.SizeMotionDuration = motionDurationSlow.Value; + _wavePainter.OpacityMotionDuration = motionDurationSlow.Value.Add(TimeSpan.FromMilliseconds(50)); + _wavePainter.WaveRange = Math.Min(waveAnimationRange.Value, 8); if (waveColor is not null) { _wavePainter.WaveColor = waveColor.Value; } else { - _wavePainter.WaveColor = sharedToken.ColorPrimary; + Color? colorPrimary = null; + { + if (_targetControl.TryGetResource(SharedTokenKey.ColorPrimary, themeVariant, out var value)) + { + if (value is Color color) + { + colorPrimary = color; + } + } + } + colorPrimary ??= sharedToken.ColorPrimary; + _wavePainter.WaveColor = colorPrimary.Value; } LayoutUpdated += HandleLayoutUpdated; diff --git a/src/AtomUI.Theme/ControlTokenInfoSetter.cs b/src/AtomUI.Theme/ControlTokenInfoSetter.cs index 569a32303..b19b71ed5 100644 --- a/src/AtomUI.Theme/ControlTokenInfoSetter.cs +++ b/src/AtomUI.Theme/ControlTokenInfoSetter.cs @@ -1,9 +1,12 @@ +using Avalonia.Metadata; + namespace AtomUI.Theme; public class ControlTokenInfoSetter { public bool EnableAlgorithm { get; set; } = false; + [Content] public List Setters { get; set; } = new (); public string? Catalog { get; set; } diff --git a/src/AtomUI.Theme/ControlTokenResourceRegister.cs b/src/AtomUI.Theme/ControlTokenResourceRegister.cs index cdd985914..88b79b82e 100644 --- a/src/AtomUI.Theme/ControlTokenResourceRegister.cs +++ b/src/AtomUI.Theme/ControlTokenResourceRegister.cs @@ -11,7 +11,7 @@ public class ControlTokenResourceRegister private Control _target; private string _tokenId; private string? _resourceCatalog; - private IResourceDictionary? _resourceDictionary; + private ResourceDictionary? _resourceDictionary; public ControlTokenResourceRegister(Control target, string tokenId, string? resourceCatalog = null) { @@ -34,8 +34,9 @@ public class ControlTokenResourceRegister { _resourceDictionary.Add(entry.Key, entry.Value); } - - _target.Resources.MergedDictionaries.Add(_resourceDictionary); + + var themeVariant = TokenFinderUtils.FindThemeVariant(_target); + _target.Resources.ThemeDictionaries.Add(themeVariant, _resourceDictionary); } private IControlDesignToken? FindControlToken() diff --git a/src/AtomUI.Theme/IThemeConfigProvider.cs b/src/AtomUI.Theme/IThemeConfigProvider.cs index 27605a7c0..2382265a9 100644 --- a/src/AtomUI.Theme/IThemeConfigProvider.cs +++ b/src/AtomUI.Theme/IThemeConfigProvider.cs @@ -1,4 +1,5 @@ using AtomUI.Theme.TokenSystem; +using Avalonia.Styling; namespace AtomUI.Theme; @@ -9,4 +10,5 @@ public interface IThemeConfigProvider IControlDesignToken? GetControlToken(string tokenId, string? catalog = null); List Algorithms { get; } bool IsDarkMode { get; } + ThemeVariant ThemeVariant { get; } } \ No newline at end of file diff --git a/src/AtomUI.Theme/TokenSystem/AbstractControlDesignToken.cs b/src/AtomUI.Theme/TokenSystem/AbstractControlDesignToken.cs index de74d7d6e..364a4a064 100644 --- a/src/AtomUI.Theme/TokenSystem/AbstractControlDesignToken.cs +++ b/src/AtomUI.Theme/TokenSystem/AbstractControlDesignToken.cs @@ -83,7 +83,7 @@ public abstract class AbstractControlDesignToken : AbstractDesignToken, IControl } } - _sharedResourceDeltaDictionary.Add(name, localSharedTokenValue); + _sharedResourceDeltaDictionary.Add(new TokenResourceKey(name), localSharedTokenValue); } } diff --git a/src/AtomUI.Theme/TokenSystem/AbstractDesignToken.cs b/src/AtomUI.Theme/TokenSystem/AbstractDesignToken.cs index c8ab54338..a1f5d07a0 100644 --- a/src/AtomUI.Theme/TokenSystem/AbstractDesignToken.cs +++ b/src/AtomUI.Theme/TokenSystem/AbstractDesignToken.cs @@ -1,5 +1,7 @@ -using System.Reflection; +using System.Diagnostics; +using System.Reflection; using Avalonia.Controls; +using Avalonia.Logging; using Avalonia.Media; namespace AtomUI.Theme.TokenSystem; @@ -33,28 +35,33 @@ public abstract class AbstractDesignToken : IDesignToken try { var type = GetType(); - var tokenProperties = - type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - foreach (var property in tokenProperties) + var tokenPropertyMap = + type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy) + .ToDictionary(p => p.Name); + foreach (var tokenInfo in tokenConfigInfo) { - var tokenName = property.Name; - if (tokenConfigInfo.ContainsKey(tokenName)) + var tokenName = tokenInfo.Key; + if (!tokenPropertyMap.ContainsKey(tokenInfo.Key)) { - var propertyType = property.PropertyType; - if (_valueConverters.ContainsKey(propertyType)) + var logger = Logger.TryGet(LogEventLevel.Warning, AtomUILogArea.Theme); + logger?.Log(this, $"Token property: '{tokenInfo.Key}' found in token {type.Name}.'"); + continue; + } + var property = tokenPropertyMap[tokenName]; + var propertyType = property.PropertyType; + if (_valueConverters.ContainsKey(propertyType)) + { + property.SetValue(this, _valueConverters[propertyType].Convert(tokenConfigInfo[tokenName])); + } + else + { + try { - property.SetValue(this, _valueConverters[propertyType].Convert(tokenConfigInfo[tokenName])); + property.SetValue(tokenName, tokenConfigInfo[tokenName]); } - else + catch (Exception ex) { - try - { - property.SetValue(tokenName, tokenConfigInfo[tokenName]); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Unable to set token property: {tokenName}, maybe value type mismatch.", ex); - } + throw new InvalidOperationException($"Unable to set token property: {tokenName}, maybe value type mismatch.", ex); } } } diff --git a/src/AtomUI.Theme/Utils/TokenFinderUtils.cs b/src/AtomUI.Theme/Utils/TokenFinderUtils.cs index c4b37df36..d813be2fd 100644 --- a/src/AtomUI.Theme/Utils/TokenFinderUtils.cs +++ b/src/AtomUI.Theme/Utils/TokenFinderUtils.cs @@ -60,4 +60,28 @@ internal static class TokenFinderUtils return token; } + + public static ThemeVariant FindThemeVariant(Control control) + { + ThemeVariant? themeVariant = null; + var current = control; + while (current != null) + { + if (current is IThemeConfigProvider configProvider) + { + themeVariant = configProvider.ThemeVariant; + break; + } + + current = (current as IStyleHost).StylingParent as Control; + } + + if (themeVariant is null) + { + var theme = ThemeManager.Current.ActivatedTheme; + Debug.Assert(theme != null); + themeVariant = theme.ThemeVariant; + } + return themeVariant; + } } \ No newline at end of file