66using System . Threading . Tasks ;
77using System . Windows ;
88using System . Windows . Controls ;
9+ using System . Windows . Controls . Primitives ;
910using System . Windows . Markup ;
1011using System . Windows . Media ;
1112using System . Windows . Media . Effects ;
1617using Flow . Launcher . Infrastructure . UserSettings ;
1718using Flow . Launcher . Plugin ;
1819using Microsoft . Win32 ;
19- using TextBox = System . Windows . Controls . TextBox ;
2020
2121namespace Flow . Launcher . Core . Resource
2222{
@@ -56,20 +56,23 @@ public Theme(IPublicAPI publicAPI, Settings settings)
5656 MakeSureThemeDirectoriesExist ( ) ;
5757
5858 var dicts = Application . Current . Resources . MergedDictionaries ;
59- _oldResource = dicts . First ( d =>
59+ _oldResource = dicts . FirstOrDefault ( d =>
6060 {
61- if ( d . Source == null )
62- return false ;
61+ if ( d . Source == null ) return false ;
6362
6463 var p = d . Source . AbsolutePath ;
65- var dir = Path . GetDirectoryName ( p ) . NonNull ( ) ;
66- var info = new DirectoryInfo ( dir ) ;
67- var f = info . Name ;
68- var e = Path . GetExtension ( p ) ;
69- var found = f == Folder && e == Extension ;
70- return found ;
64+ return p . Contains ( Folder ) && Path . GetExtension ( p ) == Extension ;
7165 } ) ;
72- _oldTheme = Path . GetFileNameWithoutExtension ( _oldResource . Source . AbsolutePath ) ;
66+
67+ if ( _oldResource != null )
68+ {
69+ _oldTheme = Path . GetFileNameWithoutExtension ( _oldResource . Source . AbsolutePath ) ;
70+ }
71+ else
72+ {
73+ Log . Error ( "Current theme resource not found. Initializing with default theme." ) ;
74+ _oldTheme = Constant . DefaultTheme ;
75+ } ;
7376 }
7477
7578 #endregion
@@ -98,13 +101,152 @@ private void MakeSureThemeDirectoriesExist()
98101
99102 private void UpdateResourceDictionary ( ResourceDictionary dictionaryToUpdate )
100103 {
101- var dicts = Application . Current . Resources . MergedDictionaries ;
104+ // Add new resources
105+ if ( ! Application . Current . Resources . MergedDictionaries . Contains ( dictionaryToUpdate ) )
106+ {
107+ Application . Current . Resources . MergedDictionaries . Add ( dictionaryToUpdate ) ;
108+ }
109+
110+ // Remove old resources
111+ if ( _oldResource != null && _oldResource != dictionaryToUpdate &&
112+ Application . Current . Resources . MergedDictionaries . Contains ( _oldResource ) )
113+ {
114+ Application . Current . Resources . MergedDictionaries . Remove ( _oldResource ) ;
115+ }
102116
103- dicts . Remove ( _oldResource ) ;
104- dicts . Add ( dictionaryToUpdate ) ;
105117 _oldResource = dictionaryToUpdate ;
106118 }
107119
120+ /// <summary>
121+ /// Updates only the font settings and refreshes the UI.
122+ /// </summary>
123+ public void UpdateFonts ( )
124+ {
125+ try
126+ {
127+ // Load a ResourceDictionary for the specified theme.
128+ var themeName = GetCurrentTheme ( ) ;
129+ var dict = GetThemeResourceDictionary ( themeName ) ;
130+
131+ // Apply font settings to the theme resource.
132+ ApplyFontSettings ( dict ) ;
133+ UpdateResourceDictionary ( dict ) ;
134+
135+ // Must apply blur and drop shadow effects
136+ _ = RefreshFrameAsync ( ) ;
137+ }
138+ catch ( Exception e )
139+ {
140+ Log . Exception ( "Error occurred while updating theme fonts" , e ) ;
141+ }
142+ }
143+
144+ /// <summary>
145+ /// Loads and applies font settings to the theme resource.
146+ /// </summary>
147+ private void ApplyFontSettings ( ResourceDictionary dict )
148+ {
149+ if ( dict [ "QueryBoxStyle" ] is Style queryBoxStyle &&
150+ dict [ "QuerySuggestionBoxStyle" ] is Style querySuggestionBoxStyle )
151+ {
152+ var fontFamily = new FontFamily ( _settings . QueryBoxFont ) ;
153+ var fontStyle = FontHelper . GetFontStyleFromInvariantStringOrNormal ( _settings . QueryBoxFontStyle ) ;
154+ var fontWeight = FontHelper . GetFontWeightFromInvariantStringOrNormal ( _settings . QueryBoxFontWeight ) ;
155+ var fontStretch = FontHelper . GetFontStretchFromInvariantStringOrNormal ( _settings . QueryBoxFontStretch ) ;
156+
157+ SetFontProperties ( queryBoxStyle , fontFamily , fontStyle , fontWeight , fontStretch , true ) ;
158+ SetFontProperties ( querySuggestionBoxStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
159+ }
160+
161+ if ( dict [ "ItemTitleStyle" ] is Style resultItemStyle &&
162+ dict [ "ItemTitleSelectedStyle" ] is Style resultItemSelectedStyle &&
163+ dict [ "ItemHotkeyStyle" ] is Style resultHotkeyItemStyle &&
164+ dict [ "ItemHotkeySelectedStyle" ] is Style resultHotkeyItemSelectedStyle )
165+ {
166+ var fontFamily = new FontFamily ( _settings . ResultFont ) ;
167+ var fontStyle = FontHelper . GetFontStyleFromInvariantStringOrNormal ( _settings . ResultFontStyle ) ;
168+ var fontWeight = FontHelper . GetFontWeightFromInvariantStringOrNormal ( _settings . ResultFontWeight ) ;
169+ var fontStretch = FontHelper . GetFontStretchFromInvariantStringOrNormal ( _settings . ResultFontStretch ) ;
170+
171+ SetFontProperties ( resultItemStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
172+ SetFontProperties ( resultItemSelectedStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
173+ SetFontProperties ( resultHotkeyItemStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
174+ SetFontProperties ( resultHotkeyItemSelectedStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
175+ }
176+
177+ if ( dict [ "ItemSubTitleStyle" ] is Style resultSubItemStyle &&
178+ dict [ "ItemSubTitleSelectedStyle" ] is Style resultSubItemSelectedStyle )
179+ {
180+ var fontFamily = new FontFamily ( _settings . ResultSubFont ) ;
181+ var fontStyle = FontHelper . GetFontStyleFromInvariantStringOrNormal ( _settings . ResultSubFontStyle ) ;
182+ var fontWeight = FontHelper . GetFontWeightFromInvariantStringOrNormal ( _settings . ResultSubFontWeight ) ;
183+ var fontStretch = FontHelper . GetFontStretchFromInvariantStringOrNormal ( _settings . ResultSubFontStretch ) ;
184+
185+ SetFontProperties ( resultSubItemStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
186+ SetFontProperties ( resultSubItemSelectedStyle , fontFamily , fontStyle , fontWeight , fontStretch , false ) ;
187+ }
188+ }
189+
190+ /// <summary>
191+ /// Applies font properties to a Style.
192+ /// </summary>
193+ private static void SetFontProperties ( Style style , FontFamily fontFamily , FontStyle fontStyle , FontWeight fontWeight , FontStretch fontStretch , bool isTextBox )
194+ {
195+ // Remove existing font-related setters
196+ if ( isTextBox )
197+ {
198+ // First, find the setters to remove and store them in a list
199+ var settersToRemove = style . Setters
200+ . OfType < Setter > ( )
201+ . Where ( setter =>
202+ setter . Property == Control . FontFamilyProperty ||
203+ setter . Property == Control . FontStyleProperty ||
204+ setter . Property == Control . FontWeightProperty ||
205+ setter . Property == Control . FontStretchProperty )
206+ . ToList ( ) ;
207+
208+ // Remove each found setter one by one
209+ foreach ( var setter in settersToRemove )
210+ {
211+ style . Setters . Remove ( setter ) ;
212+ }
213+
214+ // Add New font setter
215+ style . Setters . Add ( new Setter ( Control . FontFamilyProperty , fontFamily ) ) ;
216+ style . Setters . Add ( new Setter ( Control . FontStyleProperty , fontStyle ) ) ;
217+ style . Setters . Add ( new Setter ( Control . FontWeightProperty , fontWeight ) ) ;
218+ style . Setters . Add ( new Setter ( Control . FontStretchProperty , fontStretch ) ) ;
219+
220+ // Set caret brush (retain existing logic)
221+ var caretBrushPropertyValue = style . Setters . OfType < Setter > ( ) . Any ( x => x . Property . Name == "CaretBrush" ) ;
222+ var foregroundPropertyValue = style . Setters . OfType < Setter > ( ) . Where ( x => x . Property . Name == "Foreground" )
223+ . Select ( x => x . Value ) . FirstOrDefault ( ) ;
224+ if ( ! caretBrushPropertyValue && foregroundPropertyValue != null )
225+ style . Setters . Add ( new Setter ( TextBoxBase . CaretBrushProperty , foregroundPropertyValue ) ) ;
226+ }
227+ else
228+ {
229+ var settersToRemove = style . Setters
230+ . OfType < Setter > ( )
231+ . Where ( setter =>
232+ setter . Property == TextBlock . FontFamilyProperty ||
233+ setter . Property == TextBlock . FontStyleProperty ||
234+ setter . Property == TextBlock . FontWeightProperty ||
235+ setter . Property == TextBlock . FontStretchProperty )
236+ . ToList ( ) ;
237+
238+ foreach ( var setter in settersToRemove )
239+ {
240+ style . Setters . Remove ( setter ) ;
241+ }
242+
243+ style . Setters . Add ( new Setter ( TextBlock . FontFamilyProperty , fontFamily ) ) ;
244+ style . Setters . Add ( new Setter ( TextBlock . FontStyleProperty , fontStyle ) ) ;
245+ style . Setters . Add ( new Setter ( TextBlock . FontWeightProperty , fontWeight ) ) ;
246+ style . Setters . Add ( new Setter ( TextBlock . FontStretchProperty , fontStretch ) ) ;
247+ }
248+ }
249+
108250 private ResourceDictionary GetThemeResourceDictionary ( string theme )
109251 {
110252 var uri = GetThemePath ( theme ) ;
@@ -128,22 +270,22 @@ private ResourceDictionary GetResourceDictionary(string theme)
128270 var fontWeight = FontHelper . GetFontWeightFromInvariantStringOrNormal ( _settings . QueryBoxFontWeight ) ;
129271 var fontStretch = FontHelper . GetFontStretchFromInvariantStringOrNormal ( _settings . QueryBoxFontStretch ) ;
130272
131- queryBoxStyle . Setters . Add ( new Setter ( TextBox . FontFamilyProperty , fontFamily ) ) ;
132- queryBoxStyle . Setters . Add ( new Setter ( TextBox . FontStyleProperty , fontStyle ) ) ;
133- queryBoxStyle . Setters . Add ( new Setter ( TextBox . FontWeightProperty , fontWeight ) ) ;
134- queryBoxStyle . Setters . Add ( new Setter ( TextBox . FontStretchProperty , fontStretch ) ) ;
273+ queryBoxStyle . Setters . Add ( new Setter ( Control . FontFamilyProperty , fontFamily ) ) ;
274+ queryBoxStyle . Setters . Add ( new Setter ( Control . FontStyleProperty , fontStyle ) ) ;
275+ queryBoxStyle . Setters . Add ( new Setter ( Control . FontWeightProperty , fontWeight ) ) ;
276+ queryBoxStyle . Setters . Add ( new Setter ( Control . FontStretchProperty , fontStretch ) ) ;
135277
136278 var caretBrushPropertyValue = queryBoxStyle . Setters . OfType < Setter > ( ) . Any ( x => x . Property . Name == "CaretBrush" ) ;
137279 var foregroundPropertyValue = queryBoxStyle . Setters . OfType < Setter > ( ) . Where ( x => x . Property . Name == "Foreground" )
138280 . Select ( x => x . Value ) . FirstOrDefault ( ) ;
139281 if ( ! caretBrushPropertyValue && foregroundPropertyValue != null ) //otherwise BaseQueryBoxStyle will handle styling
140- queryBoxStyle . Setters . Add ( new Setter ( TextBox . CaretBrushProperty , foregroundPropertyValue ) ) ;
282+ queryBoxStyle . Setters . Add ( new Setter ( TextBoxBase . CaretBrushProperty , foregroundPropertyValue ) ) ;
141283
142284 // Query suggestion box's font style is aligned with query box
143- querySuggestionBoxStyle . Setters . Add ( new Setter ( TextBox . FontFamilyProperty , fontFamily ) ) ;
144- querySuggestionBoxStyle . Setters . Add ( new Setter ( TextBox . FontStyleProperty , fontStyle ) ) ;
145- querySuggestionBoxStyle . Setters . Add ( new Setter ( TextBox . FontWeightProperty , fontWeight ) ) ;
146- querySuggestionBoxStyle . Setters . Add ( new Setter ( TextBox . FontStretchProperty , fontStretch ) ) ;
285+ querySuggestionBoxStyle . Setters . Add ( new Setter ( Control . FontFamilyProperty , fontFamily ) ) ;
286+ querySuggestionBoxStyle . Setters . Add ( new Setter ( Control . FontStyleProperty , fontStyle ) ) ;
287+ querySuggestionBoxStyle . Setters . Add ( new Setter ( Control . FontWeightProperty , fontWeight ) ) ;
288+ querySuggestionBoxStyle . Setters . Add ( new Setter ( Control . FontStretchProperty , fontStretch ) ) ;
147289 }
148290
149291 if ( dict [ "ItemTitleStyle" ] is Style resultItemStyle &&
@@ -180,7 +322,7 @@ private ResourceDictionary GetResourceDictionary(string theme)
180322 /* Ignore Theme Window Width and use setting */
181323 var windowStyle = dict [ "WindowStyle" ] as Style ;
182324 var width = _settings . WindowSize ;
183- windowStyle . Setters . Add ( new Setter ( Window . WidthProperty , width ) ) ;
325+ windowStyle . Setters . Add ( new Setter ( FrameworkElement . WidthProperty , width ) ) ;
184326 return dict ;
185327 }
186328
@@ -265,11 +407,12 @@ public bool ChangeTheme(string theme = null)
265407 try
266408 {
267409 if ( string . IsNullOrEmpty ( path ) )
268- throw new DirectoryNotFoundException ( "Theme path can't be found <{path}>" ) ;
410+ throw new DirectoryNotFoundException ( $ "Theme path can't be found <{ path } >") ;
269411
270- // reload all resources even if the theme itself hasn't changed in order to pickup changes
271- // to things like fonts
272- UpdateResourceDictionary ( GetResourceDictionary ( theme ) ) ;
412+ // Retrieve theme resource – always use the resource with font settings applied.
413+ var resourceDict = GetResourceDictionary ( theme ) ;
414+
415+ UpdateResourceDictionary ( resourceDict ) ;
273416
274417 _settings . Theme = theme ;
275418
@@ -280,10 +423,11 @@ public bool ChangeTheme(string theme = null)
280423 }
281424
282425 BlurEnabled = IsBlurTheme ( ) ;
283- //if (_settings.UseDropShadowEffect)
284- // AddDropShadowEffectToCurrentTheme();
285- //Win32Helper.SetBlurForWindow(Application.Current.MainWindow, BlurEnabled);
286- _ = SetBlurForWindowAsync ( ) ;
426+
427+ // Can only apply blur but here also apply drop shadow effect to avoid possible drop shadow effect issues
428+ _ = RefreshFrameAsync ( ) ;
429+
430+ return true ;
287431 }
288432 catch ( DirectoryNotFoundException )
289433 {
@@ -305,7 +449,6 @@ public bool ChangeTheme(string theme = null)
305449 }
306450 return false ;
307451 }
308- return true ;
309452 }
310453
311454 #endregion
@@ -481,17 +624,14 @@ await Application.Current.Dispatcher.InvokeAsync(() =>
481624
482625 private void SetBlurForWindow ( string theme , BackdropTypes backdropType )
483626 {
484- var dict = GetThemeResourceDictionary ( theme ) ;
485- if ( dict == null )
486- return ;
627+ var dict = GetResourceDictionary ( theme ) ;
628+ if ( dict == null ) return ;
487629
488630 var windowBorderStyle = dict . Contains ( "WindowBorderStyle" ) ? dict [ "WindowBorderStyle" ] as Style : null ;
489- if ( windowBorderStyle == null )
490- return ;
631+ if ( windowBorderStyle == null ) return ;
491632
492- Window mainWindow = Application . Current . MainWindow ;
493- if ( mainWindow == null )
494- return ;
633+ var mainWindow = Application . Current . MainWindow ;
634+ if ( mainWindow == null ) return ;
495635
496636 // Check if the theme supports blur
497637 bool hasBlur = dict . Contains ( "ThemeBlurEnabled" ) && dict [ "ThemeBlurEnabled" ] is bool b && b ;
0 commit comments