@@ -9,18 +9,31 @@ namespace WebExpress.WebCore.WebTheme
99 /// application carried in the render context.
1010 /// </summary>
1111 /// <remarks>
12- /// Themes were moved out of <c>IApplicationContext</c> with the icon-theme
13- /// migration: the active theme is now the first theme registered for an
14- /// application via the <c>ThemeManager</c>. These helpers keep call sites
15- /// short for code that has a render context but no access to the visual
16- /// tree (e.g. <c>Icon</c> Funcs on form buttons).
12+ /// The resolution order mirrors <c>VisualTreeControl</c> so server-side
13+ /// icon factories and the visual tree end up with the same theme:
14+ /// <list type="number">
15+ /// <item><description>
16+ /// The application's declared default theme (<c>[Theme<T>]</c> →
17+ /// <c>IApplicationContext.DefaultTheme</c>).
18+ /// </description></item>
19+ /// <item><description>
20+ /// The first theme registered for the application (legacy fallback).
21+ /// </description></item>
22+ /// <item><description>
23+ /// <see langword="null"/>; downstream <see cref="TypeIconTheme"/>
24+ /// callers fall back to <see cref="TypeIconTheme.Default"/>.
25+ /// </description></item>
26+ /// </list>
27+ /// Per-user overrides are wired by application code: the page's
28+ /// <c>Process</c> hook calls <c>visualTree.UseTheme<TTheme>()</c>
29+ /// based on whatever store the application keeps; the framework itself
30+ /// does not consult cookies, sessions, or identities.
1731 /// </remarks>
1832 public static class RenderContextThemeExtensions
1933 {
2034 /// <summary>
21- /// Returns the first theme registered for the render context's
22- /// application, or <see langword="null"/> when no theme has been
23- /// registered.
35+ /// Returns the active theme for the render context using the
36+ /// resolution order documented on the class.
2437 /// </summary>
2538 /// <param name="renderContext">The current render context.</param>
2639 /// <returns>The active theme context or <see langword="null"/>.</returns>
@@ -32,6 +45,13 @@ public static IThemeContext GetActiveTheme(this IRenderContext renderContext)
3245 return null ;
3346 }
3447
48+ // 1. application's declared default
49+ if ( applicationContext . DefaultTheme is { } declared )
50+ {
51+ return declared ;
52+ }
53+
54+ // 2. first registered theme for the application
3555 return WebEx . ComponentHub ? . ThemeManager ? . Themes
3656 ? . FirstOrDefault ( t => t . ApplicationContext == applicationContext ) ;
3757 }
@@ -47,5 +67,61 @@ public static TypeIconTheme GetIconTheme(this IRenderContext renderContext)
4767 {
4868 return renderContext . GetActiveTheme ( ) ? . IconTheme ?? TypeIconTheme . Default ;
4969 }
70+
71+ /// <summary>
72+ /// Re-themes an existing <see cref="WebIcon.IIcon"/> for the active
73+ /// icon theme of <paramref name="renderContext"/>. Convenience over
74+ /// <see cref="ApplyIconTheme(WebIcon.IIcon, TypeIconTheme)"/> for
75+ /// callers that only have a render context in hand.
76+ /// </summary>
77+ /// <param name="icon">The icon to re-theme; may be <see langword="null"/>.</param>
78+ /// <param name="renderContext">The current render context.</param>
79+ /// <returns>The re-themed icon or the original instance.</returns>
80+ public static WebIcon . IIcon ApplyIconTheme ( this WebIcon . IIcon icon , IRenderContext renderContext )
81+ {
82+ return icon . ApplyIconTheme ( renderContext . GetIconTheme ( ) ) ;
83+ }
84+
85+ /// <summary>
86+ /// Re-themes an existing <see cref="WebIcon.IIcon"/> for the given
87+ /// <paramref name="theme"/>. Icons created at registration time
88+ /// (e.g. <c>PageContext.PageIcon</c>) carry the theme that was
89+ /// active when the page was discovered; this helper rebuilds them
90+ /// so the breadcrumb, sidebars, etc. swap glyphs at runtime when
91+ /// the application code activates a different theme via
92+ /// <c>visualTree.UseTheme<TTheme>()</c>. Controls that have
93+ /// a visual tree in hand should pass
94+ /// <c>visualTree.IconTheme</c> here.
95+ /// <para>
96+ /// Falls back to <paramref name="icon"/> when its concrete type does
97+ /// not expose a <c>(TypeIconTheme)</c> constructor.
98+ /// </para>
99+ /// </summary>
100+ /// <param name="icon">The icon to re-theme; may be <see langword="null"/>.</param>
101+ /// <param name="theme">The icon theme to apply.</param>
102+ /// <returns>The re-themed icon or the original instance.</returns>
103+ public static WebIcon . IIcon ApplyIconTheme ( this WebIcon . IIcon icon , TypeIconTheme theme )
104+ {
105+ if ( icon is null )
106+ {
107+ return null ;
108+ }
109+
110+ var iconType = icon . GetType ( ) ;
111+ var ctor = iconType . GetConstructor ( new [ ] { typeof ( TypeIconTheme ) } ) ;
112+ if ( ctor is null )
113+ {
114+ return icon ;
115+ }
116+
117+ try
118+ {
119+ return ctor . Invoke ( new object [ ] { theme } ) as WebIcon . IIcon ?? icon ;
120+ }
121+ catch
122+ {
123+ return icon ;
124+ }
125+ }
50126 }
51127}
0 commit comments