@@ -14,6 +14,13 @@ public record BackdropSettings(
1414 string FallbackColor = "#000000" ,
1515 string Kind = "Base" ) ;
1616
17+ public record PersistedWindowState (
18+ string Type ,
19+ string NoteId ,
20+ int X ,
21+ int Y ,
22+ bool IsCompact = false ) ;
23+
1724public static class AppSettings
1825{
1926 private static readonly string SettingsDir =
@@ -165,21 +172,42 @@ public static void SaveWebDavSettings(string url, string username, string passwo
165172 // ── Apply to any window ─────────────────────────────────────
166173
167174 public static void ApplyToWindow ( Window window , BackdropSettings settings ,
168- ref DesktopAcrylicController ? controller , ref SystemBackdropConfiguration ? configSource )
175+ ref IDisposable ? controller , ref SystemBackdropConfiguration ? configSource )
169176 {
170177 controller ? . Dispose ( ) ;
171178 controller = null ;
179+ configSource = null ;
180+ window . SystemBackdrop = null ;
181+
182+ if ( settings . Type == "none" )
183+ return ;
172184
173- if ( settings . Type == "acrylic_custom" )
185+ controller = CreateBackdropController ( settings ) ;
186+ if ( controller is null )
187+ return ;
188+
189+ configSource = new SystemBackdropConfiguration
174190 {
175- window . SystemBackdrop = null ;
191+ IsInputActive = true ,
192+ Theme = SystemBackdropTheme . Default
193+ } ;
176194
177- configSource = new SystemBackdropConfiguration ( ) ;
178- configSource . IsInputActive = true ;
179- if ( window . Content is FrameworkElement fe )
180- configSource . Theme = ( SystemBackdropTheme ) fe . ActualTheme ;
195+ if ( window . Content is FrameworkElement fe )
196+ configSource . Theme = ( SystemBackdropTheme ) fe . ActualTheme ;
181197
182- controller = new DesktopAcrylicController
198+ ConfigureBackdropController (
199+ controller ,
200+ window . As < Microsoft . UI . Composition . ICompositionSupportsSystemBackdrop > ( ) ,
201+ configSource ) ;
202+ }
203+
204+ private static IDisposable ? CreateBackdropController ( BackdropSettings settings )
205+ {
206+ return settings . Type switch
207+ {
208+ "mica" when MicaController . IsSupported ( ) => new MicaController ( ) ,
209+ "mica_alt" when MicaController . IsSupported ( ) => new MicaController { Kind = MicaKind . BaseAlt } ,
210+ "acrylic_custom" when DesktopAcrylicController . IsSupported ( ) => new DesktopAcrylicController
183211 {
184212 TintOpacity = ( float ) settings . TintOpacity ,
185213 LuminosityOpacity = ( float ) settings . LuminosityOpacity ,
@@ -188,22 +216,27 @@ public static void ApplyToWindow(Window window, BackdropSettings settings,
188216 Kind = settings . Kind == "Thin"
189217 ? DesktopAcrylicKind . Thin
190218 : DesktopAcrylicKind . Base ,
191- } ;
219+ } ,
220+ _ when DesktopAcrylicController . IsSupported ( ) => new DesktopAcrylicController ( ) ,
221+ _ => null
222+ } ;
223+ }
192224
193- controller . AddSystemBackdropTarget (
194- window . As < Microsoft . UI . Composition . ICompositionSupportsSystemBackdrop > ( ) ) ;
195- controller . SetSystemBackdropConfiguration ( configSource ) ;
196- }
197- else
225+ private static void ConfigureBackdropController (
226+ IDisposable controller ,
227+ Microsoft . UI . Composition . ICompositionSupportsSystemBackdrop target ,
228+ SystemBackdropConfiguration config )
229+ {
230+ switch ( controller )
198231 {
199- configSource = null ;
200- window . SystemBackdrop = settings . Type switch
201- {
202- "mica" => new MicaBackdrop ( ) ,
203- "mica_alt" => new MicaBackdrop { Kind = MicaKind . BaseAlt } ,
204- "none" => null ,
205- _ => new DesktopAcrylicBackdrop ( )
206- } ;
232+ case MicaController mica :
233+ mica . AddSystemBackdropTarget ( target ) ;
234+ mica . SetSystemBackdropConfiguration ( config ) ;
235+ break ;
236+ case DesktopAcrylicController acrylic :
237+ acrylic . AddSystemBackdropTarget ( target ) ;
238+ acrylic . SetSystemBackdropConfiguration ( config ) ;
239+ break ;
207240 }
208241 }
209242
@@ -268,6 +301,47 @@ public static string LoadThemeSetting()
268301 return "system" ;
269302 }
270303
304+ public static ( int X , int Y ) ? LoadMainWindowPosition ( )
305+ {
306+ try
307+ {
308+ if ( ! File . Exists ( SettingsPath ) )
309+ return null ;
310+
311+ var json = File . ReadAllText ( SettingsPath ) ;
312+ var doc = JsonDocument . Parse ( json ) ;
313+ var root = doc . RootElement ;
314+ if ( root . TryGetProperty ( "mainWindowX" , out var xProp ) &&
315+ root . TryGetProperty ( "mainWindowY" , out var yProp ) &&
316+ xProp . TryGetInt32 ( out var x ) &&
317+ yProp . TryGetInt32 ( out var y ) )
318+ {
319+ return ( x , y ) ;
320+ }
321+ }
322+ catch { }
323+ return null ;
324+ }
325+
326+ public static bool LoadMainWindowCompact ( )
327+ {
328+ try
329+ {
330+ if ( ! File . Exists ( SettingsPath ) )
331+ return false ;
332+
333+ var json = File . ReadAllText ( SettingsPath ) ;
334+ var doc = JsonDocument . Parse ( json ) ;
335+ if ( doc . RootElement . TryGetProperty ( "mainWindowCompact" , out var compactProp ) &&
336+ ( compactProp . ValueKind == JsonValueKind . True || compactProp . ValueKind == JsonValueKind . False ) )
337+ {
338+ return compactProp . GetBoolean ( ) ;
339+ }
340+ }
341+ catch { }
342+ return false ;
343+ }
344+
271345 public static void SaveBackdropSettings ( BackdropSettings settings )
272346 {
273347 MergeAndSaveSettings ( new Dictionary < string , object >
@@ -286,6 +360,107 @@ public static void SaveThemeSetting(string theme)
286360 MergeAndSaveSettings ( new Dictionary < string , object > { [ "theme" ] = theme } ) ;
287361 }
288362
363+ public static void SaveMainWindowPosition ( Windows . Graphics . PointInt32 position )
364+ {
365+ MergeAndSaveSettings ( new Dictionary < string , object >
366+ {
367+ [ "mainWindowX" ] = position . X ,
368+ [ "mainWindowY" ] = position . Y
369+ } ) ;
370+ }
371+
372+ public static void SaveMainWindowCompact ( bool isCompact )
373+ {
374+ MergeAndSaveSettings ( new Dictionary < string , object >
375+ {
376+ [ "mainWindowCompact" ] = isCompact
377+ } ) ;
378+ }
379+
380+ public static List < PersistedWindowState > LoadOpenWindows ( )
381+ {
382+ var result = new List < PersistedWindowState > ( ) ;
383+ try
384+ {
385+ if ( ! File . Exists ( SettingsPath ) )
386+ return result ;
387+
388+ var json = File . ReadAllText ( SettingsPath ) ;
389+ var doc = JsonDocument . Parse ( json ) ;
390+ if ( ! doc . RootElement . TryGetProperty ( "openWindows" , out var windowsProp ) )
391+ {
392+ return result ;
393+ }
394+
395+ var arrayElement = windowsProp ;
396+ JsonDocument ? nestedDoc = null ;
397+ if ( windowsProp . ValueKind == JsonValueKind . String )
398+ {
399+ var raw = windowsProp . GetString ( ) ;
400+ if ( ! string . IsNullOrWhiteSpace ( raw ) )
401+ {
402+ nestedDoc = JsonDocument . Parse ( raw ) ;
403+ arrayElement = nestedDoc . RootElement ;
404+ }
405+ }
406+
407+ if ( arrayElement . ValueKind != JsonValueKind . Array )
408+ {
409+ nestedDoc ? . Dispose ( ) ;
410+ return result ;
411+ }
412+
413+ foreach ( var item in arrayElement . EnumerateArray ( ) )
414+ {
415+ if ( item . ValueKind != JsonValueKind . Object )
416+ continue ;
417+
418+ var type = item . TryGetProperty ( "type" , out var typeProp )
419+ ? typeProp . GetString ( ) ?? ""
420+ : "" ;
421+ var noteId = item . TryGetProperty ( "noteId" , out var noteIdProp )
422+ ? noteIdProp . GetString ( ) ?? ""
423+ : "" ;
424+ if ( ! item . TryGetProperty ( "x" , out var xProp ) ||
425+ ! item . TryGetProperty ( "y" , out var yProp ) ||
426+ ! xProp . TryGetInt32 ( out var x ) ||
427+ ! yProp . TryGetInt32 ( out var y ) ||
428+ string . IsNullOrWhiteSpace ( type ) )
429+ {
430+ continue ;
431+ }
432+
433+ var isCompact =
434+ item . TryGetProperty ( "isCompact" , out var compactProp ) &&
435+ ( compactProp . ValueKind == JsonValueKind . True || compactProp . ValueKind == JsonValueKind . False ) &&
436+ compactProp . GetBoolean ( ) ;
437+
438+ result . Add ( new PersistedWindowState ( type , noteId , x , y , isCompact ) ) ;
439+ }
440+
441+ nestedDoc ? . Dispose ( ) ;
442+ }
443+ catch { }
444+ return result ;
445+ }
446+
447+ public static void SaveOpenWindows ( IEnumerable < PersistedWindowState > windows )
448+ {
449+ var payload = windows . Select ( w => new Dictionary < string , object >
450+ {
451+ [ "type" ] = w . Type ,
452+ [ "noteId" ] = w . NoteId ,
453+ [ "x" ] = w . X ,
454+ [ "y" ] = w . Y ,
455+ [ "isCompact" ] = w . IsCompact
456+ } ) . ToList ( ) ;
457+
458+ MergeAndSaveSettings ( new Dictionary < string , object >
459+ {
460+ [ "openWindows" ] = payload
461+ } ) ;
462+ }
463+
289464 private static void MergeAndSaveSettings ( Dictionary < string , object > newValues )
290465 {
291466 try
@@ -298,14 +473,7 @@ private static void MergeAndSaveSettings(Dictionary<string, object> newValues)
298473 foreach ( var prop in doc . RootElement . EnumerateObject ( ) )
299474 {
300475 if ( newValues . ContainsKey ( prop . Name ) ) continue ;
301- existing [ prop . Name ] = prop . Value . ValueKind switch
302- {
303- JsonValueKind . Number => prop . Value . TryGetInt64 ( out var l ) ? l : prop . Value . GetDouble ( ) ,
304- JsonValueKind . String => prop . Value . GetString ( ) ! ,
305- JsonValueKind . True => true ,
306- JsonValueKind . False => false ,
307- _ => prop . Value . GetRawText ( )
308- } ;
476+ existing [ prop . Name ] = ConvertJsonElement ( prop . Value ) ?? "" ;
309477 }
310478 }
311479
@@ -318,6 +486,24 @@ private static void MergeAndSaveSettings(Dictionary<string, object> newValues)
318486 catch { }
319487 }
320488
489+ private static object ? ConvertJsonElement ( JsonElement value )
490+ {
491+ return value . ValueKind switch
492+ {
493+ JsonValueKind . Object => value . EnumerateObject ( )
494+ . ToDictionary ( p => p . Name , p => ConvertJsonElement ( p . Value ) ) ,
495+ JsonValueKind . Array => value . EnumerateArray ( )
496+ . Select ( ConvertJsonElement )
497+ . ToList ( ) ,
498+ JsonValueKind . String => value . GetString ( ) ,
499+ JsonValueKind . Number => value . TryGetInt64 ( out var l ) ? l : value . GetDouble ( ) ,
500+ JsonValueKind . True => true ,
501+ JsonValueKind . False => false ,
502+ JsonValueKind . Null => null ,
503+ _ => value . GetRawText ( )
504+ } ;
505+ }
506+
321507 public static Windows . UI . Color ParseColor ( string hex )
322508 {
323509 hex = hex . TrimStart ( '#' ) ;
0 commit comments