diff --git a/build/Version.props b/build/Version.props
index a530e89f0..8caed48b5 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -2,7 +2,7 @@
$(NoWarn);CS7035
11.3.9
- 5.1.2-build.2
+ 5.1.2-build.3
1.0.1
5.1.2
diff --git a/controlgallery/AtomUIGallery.Icons.Desktop/GalleryIconProvider.cs b/controlgallery/AtomUIGallery.Icons.Desktop/GalleryIconProvider.cs
index a2d3c8f0a..76308a86b 100644
--- a/controlgallery/AtomUIGallery.Icons.Desktop/GalleryIconProvider.cs
+++ b/controlgallery/AtomUIGallery.Icons.Desktop/GalleryIconProvider.cs
@@ -1,4 +1,3 @@
-using System.Diagnostics;
using System.Reflection;
using AtomUI.Controls;
@@ -14,27 +13,17 @@ public class GalleryDesktopIconProvider : IconProvider
: base(kind)
{
}
-
- protected override Icon GetIcon(DesktopIconKind kind)
+
+ protected override Type GetTypeForKind(DesktopIconKind kind)
{
- try
+ var typeName = $"AtomUIGallery.Icons.Desktop.{kind.ToString()}";
+
+ var type = Type.GetType(typeName)
+ ?? Assembly.GetExecutingAssembly().GetType(typeName);
+ if (type == null)
{
- var fullTypeName = $"AtomUIGallery.Icons.Desktop.{kind.ToString()}";
- var type = Type.GetType(fullTypeName)
- ?? Assembly.GetExecutingAssembly().GetType(fullTypeName);
-
- if (type == null)
- {
- throw new InvalidOperationException($"{fullTypeName} not exist");
- }
-
- var icon = (Icon?)Activator.CreateInstance(type);
- Debug.Assert(icon != null);
- return icon;
- }
- catch (Exception ex)
- {
- throw new InvalidOperationException($"create icon {kind.ToString()} failed", ex);
+ throw new InvalidOperationException($"Type {typeName} does not exist");
}
+ return type;
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls.Shared/Icon/Themes/IconTheme.axaml b/src/AtomUI.Controls.Shared/Icon/Themes/IconTheme.axaml
index 230aa4c1a..0daf23505 100644
--- a/src/AtomUI.Controls.Shared/Icon/Themes/IconTheme.axaml
+++ b/src/AtomUI.Controls.Shared/Icon/Themes/IconTheme.axaml
@@ -28,6 +28,11 @@
+
+
diff --git a/src/AtomUI.Core/Controls/Icon/Common.cs b/src/AtomUI.Core/Controls/Icon/Common.cs
index 0b22dd3ba..ca535fc33 100644
--- a/src/AtomUI.Core/Controls/Icon/Common.cs
+++ b/src/AtomUI.Core/Controls/Icon/Common.cs
@@ -23,5 +23,6 @@ public enum IconBrushType
Fill,
SecondaryStroke,
SecondaryFill,
- Fallback
+ Fallback,
+ None
}
\ No newline at end of file
diff --git a/src/AtomUI.Core/Controls/Icon/Icon.cs b/src/AtomUI.Core/Controls/Icon/Icon.cs
index 7623d6645..9f0721b5d 100644
--- a/src/AtomUI.Core/Controls/Icon/Icon.cs
+++ b/src/AtomUI.Core/Controls/Icon/Icon.cs
@@ -169,8 +169,8 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
private Animation? _animation;
private CancellationTokenSource? _animationCancellationTokenSource;
- private readonly IBrush?[] _brushes = new IBrush[5];
- private readonly Pen?[] _pens = new Pen?[5];
+ protected readonly IBrush?[] DrawBrushes = new IBrush[5];
+ protected readonly Pen?[] DrawPens = new Pen?[5];
static Icon()
{
@@ -195,17 +195,28 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
var secondaryFillIndex = (int)IconBrushType.SecondaryFill;
var fallbackIndex = (int)IconBrushType.Fallback;
- _brushes[strokeIndex] = StrokeBrush;
- _brushes[fillIndex] = FillBrush;
- _brushes[secondaryStrokeIndex] = SecondaryStrokeBrush;
- _brushes[secondaryFillIndex] = SecondaryFillBrush;
- _brushes[fallbackIndex] = FallbackBrush;
+ var strokeBrush = ProcessBrush(StrokeBrush);
+ var fillBrush = ProcessBrush(FillBrush);
+ var secondaryStrokeBrush = ProcessBrush(SecondaryStrokeBrush);
+ var secondaryFillBrush = ProcessBrush(SecondaryFillBrush);
+ var fallbackBrush = ProcessBrush(FallbackBrush);
- _pens[strokeIndex] = new Pen(StrokeBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
- _pens[fillIndex] = new Pen(FillBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
- _pens[secondaryStrokeIndex] = new Pen(SecondaryStrokeBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
- _pens[secondaryFillIndex] = new Pen(SecondaryFillBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
- _pens[fallbackIndex] = new Pen(FallbackBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawBrushes[strokeIndex] = strokeBrush;
+ DrawBrushes[fillIndex] = fillBrush;
+ DrawBrushes[secondaryStrokeIndex] = secondaryStrokeBrush;
+ DrawBrushes[secondaryFillIndex] = secondaryFillBrush;
+ DrawBrushes[fallbackIndex] = fallbackBrush;
+
+ DrawPens[strokeIndex] = new Pen(strokeBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawPens[fillIndex] = new Pen(fillBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawPens[secondaryStrokeIndex] = new Pen(secondaryStrokeBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawPens[secondaryFillIndex] = new Pen(secondaryFillBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawPens[fallbackIndex] = new Pen(fallbackBrush, StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ }
+
+ protected virtual IBrush? ProcessBrush(IBrush? brush)
+ {
+ return brush;
}
private void ConfigureTransitions(bool force)
@@ -298,14 +309,18 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
private void HandleBrushChanged(IconBrushType brushType, IBrush? brush)
{
+ if (brushType == IconBrushType.None)
+ {
+ return;
+ }
var brushIndex = (int)brushType;
- _brushes[brushIndex] = brush;
- _pens[brushIndex] = new Pen(_brushes[brushIndex], StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
+ DrawBrushes[brushIndex] = ProcessBrush(brush);
+ DrawPens[brushIndex] = new Pen(DrawBrushes[brushIndex], StrokeWidth, lineCap: StrokeLineCap, lineJoin: StrokeLineJoin);
}
private void HandleStrokeWidthChanged(double strokeWidth)
{
- foreach (var pen in _pens)
+ foreach (var pen in DrawPens)
{
if (pen != null)
{
@@ -316,7 +331,7 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
private void HandleLineCapChanged(PenLineCap lineCap)
{
- foreach (var pen in _pens)
+ foreach (var pen in DrawPens)
{
if (pen != null)
{
@@ -327,7 +342,7 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
private void HandleLineJoinChanged(PenLineJoin lineJoin)
{
- foreach (var pen in _pens)
+ foreach (var pen in DrawPens)
{
if (pen != null)
{
@@ -467,10 +482,14 @@ public abstract class Icon : PathIcon, ICustomHitTest, IMotionAwareControl
return true;
}
- internal IBrush? FindIconBrush(IconBrushType brushType)
+ public virtual IBrush? FindIconBrush(IconBrushType brushType)
{
+ if (brushType == IconBrushType.None)
+ {
+ return null;
+ }
var index = (int)brushType;
- Debug.Assert(index >= 0 && index < _brushes.Length);
- return _brushes[index];
+ Debug.Assert(index >= 0 && index < DrawBrushes.Length);
+ return DrawBrushes[index];
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Core/Controls/Icon/IconProvider.cs b/src/AtomUI.Core/Controls/Icon/IconProvider.cs
index 1c30a85bd..9e668e2ce 100644
--- a/src/AtomUI.Core/Controls/Icon/IconProvider.cs
+++ b/src/AtomUI.Core/Controls/Icon/IconProvider.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using System.Linq.Expressions;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
@@ -7,7 +8,20 @@ namespace AtomUI.Controls;
public abstract class IconProvider : MarkupExtension
where TIconKind : Enum
{
- public TIconKind? Kind { get; set; }
+ private TIconKind? _kind;
+
+ public TIconKind? Kind
+ {
+ get => _kind;
+ set
+ {
+ if (!EqualityComparer.Default.Equals(_kind, value))
+ {
+ _kind = value;
+ NotifyKindChanged();
+ }
+ }
+ }
public IBrush? StrokeBrush { get; set; }
public IBrush? FillBrush { get; set; }
@@ -26,6 +40,10 @@ public abstract class IconProvider : MarkupExtension
{
Kind = kind;
}
+
+ protected virtual void NotifyKindChanged()
+ {
+ }
public override object ProvideValue(IServiceProvider serviceProvider)
{
@@ -70,5 +88,58 @@ public abstract class IconProvider : MarkupExtension
return icon;
}
- protected abstract Icon GetIcon(TIconKind kind);
+ protected virtual Icon GetIcon(TIconKind kind)
+ {
+ try
+ {
+ var enumType = typeof(TIconKind);
+ var creator = IconProviderCache.GetOrAddCreator(
+ enumType,
+ kind,
+ value => GetTypeForKind((TIconKind)value),
+ CreateFactory);
+
+ var icon = creator();
+ Debug.Assert(icon != null);
+ return icon;
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Create icon {kind} failed", ex);
+ }
+ }
+
+ protected abstract Type GetTypeForKind(TIconKind kind);
+
+ protected virtual Func CreateFactory(Type type)
+ {
+ try
+ {
+ var constructor = type.GetConstructor(Type.EmptyTypes);
+ if (constructor == null)
+ {
+ throw new InvalidOperationException(
+ $"No parameterless constructor found for {type.Name}");
+ }
+
+ var newExpr = Expression.New(constructor);
+ var lambda = Expression.Lambda>(newExpr);
+ return lambda.Compile();
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Failed to create expression tree for {type.Name}: {ex.Message}");
+ return () => (Icon)Activator.CreateInstance(type)!;
+ }
+ }
+
+ public static void ClearCache()
+ {
+ IconProviderCache.ClearCache(typeof(TIconKind));
+ }
+
+ public static void ClearAllCache()
+ {
+ IconProviderCache.ClearAllCache();
+ }
}
\ No newline at end of file
diff --git a/src/AtomUI.Core/Controls/Icon/IconProviderCache.cs b/src/AtomUI.Core/Controls/Icon/IconProviderCache.cs
new file mode 100644
index 000000000..1f3cfccca
--- /dev/null
+++ b/src/AtomUI.Core/Controls/Icon/IconProviderCache.cs
@@ -0,0 +1,49 @@
+using System.Collections.Concurrent;
+
+namespace AtomUI.Controls;
+
+internal static class IconProviderCache
+{
+ private static readonly ConcurrentDictionary> TypeCache =
+ new();
+
+ private static readonly ConcurrentDictionary>> CreatorCache =
+ new();
+
+ private static readonly ConcurrentDictionary> TypeToCreator =
+ new();
+
+ public static Type GetOrAddType(Type enumType, object enumValue, Func