2
2
using System . Collections . Generic ;
3
3
using System . IO ;
4
4
using System . Linq ;
5
+ using System . Xml ;
5
6
using System . Runtime . InteropServices ;
6
7
using System . Windows ;
7
8
using System . Windows . Controls ;
@@ -17,6 +18,10 @@ namespace Flow.Launcher.Core.Resource
17
18
{
18
19
public class Theme
19
20
{
21
+ private const string ThemeMetadataNamePrefix = "Name:" ;
22
+ private const string ThemeMetadataIsDarkPrefix = "IsDark:" ;
23
+ private const string ThemeMetadataHasBlurPrefix = "HasBlur:" ;
24
+
20
25
private const int ShadowExtraMargin = 32 ;
21
26
22
27
private readonly List < string > _themeDirectories = new List < string > ( ) ;
@@ -79,14 +84,14 @@ public bool ChangeTheme(string theme)
79
84
{
80
85
if ( string . IsNullOrEmpty ( path ) )
81
86
throw new DirectoryNotFoundException ( "Theme path can't be found <{path}>" ) ;
82
-
87
+
83
88
// reload all resources even if the theme itself hasn't changed in order to pickup changes
84
89
// to things like fonts
85
90
UpdateResourceDictionary ( GetResourceDictionary ( theme ) ) ;
86
-
91
+
87
92
Settings . Theme = theme ;
88
93
89
-
94
+
90
95
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
91
96
if ( _oldTheme != theme || theme == defaultTheme )
92
97
{
@@ -148,7 +153,7 @@ private ResourceDictionary GetThemeResourceDictionary(string theme)
148
153
public ResourceDictionary GetResourceDictionary ( string theme )
149
154
{
150
155
var dict = GetThemeResourceDictionary ( theme ) ;
151
-
156
+
152
157
if ( dict [ "QueryBoxStyle" ] is Style queryBoxStyle &&
153
158
dict [ "QuerySuggestionBoxStyle" ] is Style querySuggestionBoxStyle )
154
159
{
@@ -187,7 +192,7 @@ public ResourceDictionary GetResourceDictionary(string theme)
187
192
188
193
Setter [ ] setters = { fontFamily , fontStyle , fontWeight , fontStretch } ;
189
194
Array . ForEach (
190
- new [ ] { resultItemStyle , resultItemSelectedStyle , resultHotkeyItemStyle , resultHotkeyItemSelectedStyle } , o
195
+ new [ ] { resultItemStyle , resultItemSelectedStyle , resultHotkeyItemStyle , resultHotkeyItemSelectedStyle } , o
191
196
=> Array . ForEach ( setters , p => o . Setters . Add ( p ) ) ) ;
192
197
}
193
198
@@ -219,17 +224,53 @@ private ResourceDictionary GetCurrentResourceDictionary( )
219
224
return GetResourceDictionary ( Settings . Theme ) ;
220
225
}
221
226
222
- public List < string > LoadAvailableThemes ( )
227
+ public List < ThemeData > LoadAvailableThemes ( )
223
228
{
224
- List < string > themes = new List < string > ( ) ;
229
+ List < ThemeData > themes = new List < ThemeData > ( ) ;
225
230
foreach ( var themeDirectory in _themeDirectories )
226
231
{
227
- themes . AddRange (
228
- Directory . GetFiles ( themeDirectory )
229
- . Where ( filePath => filePath . EndsWith ( Extension ) && ! filePath . EndsWith ( "Base.xaml" ) )
230
- . ToList ( ) ) ;
232
+ var filePaths = Directory
233
+ . GetFiles ( themeDirectory )
234
+ . Where ( filePath => filePath . EndsWith ( Extension ) && ! filePath . EndsWith ( "Base.xaml" ) )
235
+ . Select ( GetThemeDataFromPath ) ;
236
+ themes . AddRange ( filePaths ) ;
231
237
}
232
- return themes . OrderBy ( o => o ) . ToList ( ) ;
238
+
239
+ return themes . OrderBy ( o => o . Name ) . ToList ( ) ;
240
+ }
241
+
242
+ private ThemeData GetThemeDataFromPath ( string path )
243
+ {
244
+ using var reader = XmlReader . Create ( path ) ;
245
+ reader . Read ( ) ;
246
+
247
+ var extensionlessName = Path . GetFileNameWithoutExtension ( path ) ;
248
+
249
+ if ( reader . NodeType is not XmlNodeType . Comment )
250
+ return new ThemeData ( extensionlessName , extensionlessName ) ;
251
+
252
+ var commentLines = reader . Value . Trim ( ) . Split ( '\n ' ) . Select ( v => v . Trim ( ) ) ;
253
+
254
+ var name = extensionlessName ;
255
+ bool ? isDark = null ;
256
+ bool ? hasBlur = null ;
257
+ foreach ( var line in commentLines )
258
+ {
259
+ if ( line . StartsWith ( ThemeMetadataNamePrefix , StringComparison . OrdinalIgnoreCase ) )
260
+ {
261
+ name = line . Remove ( 0 , ThemeMetadataNamePrefix . Length ) . Trim ( ) ;
262
+ }
263
+ else if ( line . StartsWith ( ThemeMetadataIsDarkPrefix , StringComparison . OrdinalIgnoreCase ) )
264
+ {
265
+ isDark = bool . Parse ( line . Remove ( 0 , ThemeMetadataIsDarkPrefix . Length ) . Trim ( ) ) ;
266
+ }
267
+ else if ( line . StartsWith ( ThemeMetadataHasBlurPrefix , StringComparison . OrdinalIgnoreCase ) )
268
+ {
269
+ hasBlur = bool . Parse ( line . Remove ( 0 , ThemeMetadataHasBlurPrefix . Length ) . Trim ( ) ) ;
270
+ }
271
+ }
272
+
273
+ return new ThemeData ( extensionlessName , name , isDark , hasBlur ) ;
233
274
}
234
275
235
276
private string GetThemePath ( string themeName )
@@ -407,5 +448,7 @@ private void SetWindowAccent(Window w, AccentState state)
407
448
Marshal . FreeHGlobal ( accentPtr ) ;
408
449
}
409
450
#endregion
451
+
452
+ public record ThemeData ( string FileNameWithoutExtension , string Name , bool ? IsDark = null , bool ? HasBlur = null ) ;
410
453
}
411
454
}
0 commit comments