mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
0.4.11
This commit is contained in:
@@ -70,16 +70,6 @@ public sealed class AppSettingsSnapshot
|
|||||||
|
|
||||||
public int StatusBarCustomSpacingPercent { get; set; } = 12;
|
public int StatusBarCustomSpacingPercent { get; set; } = 12;
|
||||||
|
|
||||||
public int DesktopPageCount { get; set; } = 1;
|
|
||||||
|
|
||||||
public int CurrentDesktopSurfaceIndex { get; set; } = 0;
|
|
||||||
|
|
||||||
public List<DesktopComponentPlacementSnapshot> DesktopComponentPlacements { get; set; } = [];
|
|
||||||
|
|
||||||
public List<string> HiddenLauncherFolderPaths { get; set; } = [];
|
|
||||||
|
|
||||||
public List<string> HiddenLauncherAppPaths { get; set; } = [];
|
|
||||||
|
|
||||||
public AppSettingsSnapshot Clone()
|
public AppSettingsSnapshot Clone()
|
||||||
{
|
{
|
||||||
var clone = (AppSettingsSnapshot)MemberwiseClone();
|
var clone = (AppSettingsSnapshot)MemberwiseClone();
|
||||||
@@ -91,36 +81,6 @@ public sealed class AppSettingsSnapshot
|
|||||||
? new List<string>(PinnedTaskbarActions)
|
? new List<string>(PinnedTaskbarActions)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
var placements = new List<DesktopComponentPlacementSnapshot>(DesktopComponentPlacements?.Count ?? 0);
|
|
||||||
if (DesktopComponentPlacements is not null)
|
|
||||||
{
|
|
||||||
foreach (var placement in DesktopComponentPlacements)
|
|
||||||
{
|
|
||||||
if (placement is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
placements.Add(new DesktopComponentPlacementSnapshot
|
|
||||||
{
|
|
||||||
PlacementId = placement.PlacementId,
|
|
||||||
PageIndex = placement.PageIndex,
|
|
||||||
ComponentId = placement.ComponentId,
|
|
||||||
Row = placement.Row,
|
|
||||||
Column = placement.Column,
|
|
||||||
WidthCells = placement.WidthCells,
|
|
||||||
HeightCells = placement.HeightCells
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clone.DesktopComponentPlacements = placements;
|
|
||||||
clone.HiddenLauncherFolderPaths = HiddenLauncherFolderPaths is { Count: > 0 }
|
|
||||||
? new List<string>(HiddenLauncherFolderPaths)
|
|
||||||
: [];
|
|
||||||
clone.HiddenLauncherAppPaths = HiddenLauncherAppPaths is { Count: > 0 }
|
|
||||||
? new List<string>(HiddenLauncherAppPaths)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
LanMountainDesktop/Models/DesktopLayoutSettingsSnapshot.cs
Normal file
43
LanMountainDesktop/Models/DesktopLayoutSettingsSnapshot.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Models;
|
||||||
|
|
||||||
|
public sealed class DesktopLayoutSettingsSnapshot
|
||||||
|
{
|
||||||
|
public int DesktopPageCount { get; set; } = 1;
|
||||||
|
|
||||||
|
public int CurrentDesktopSurfaceIndex { get; set; }
|
||||||
|
|
||||||
|
public List<DesktopComponentPlacementSnapshot> DesktopComponentPlacements { get; set; } = [];
|
||||||
|
|
||||||
|
public DesktopLayoutSettingsSnapshot Clone()
|
||||||
|
{
|
||||||
|
var clone = (DesktopLayoutSettingsSnapshot)MemberwiseClone();
|
||||||
|
var placements = new List<DesktopComponentPlacementSnapshot>(DesktopComponentPlacements?.Count ?? 0);
|
||||||
|
|
||||||
|
if (DesktopComponentPlacements is not null)
|
||||||
|
{
|
||||||
|
foreach (var placement in DesktopComponentPlacements)
|
||||||
|
{
|
||||||
|
if (placement is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
placements.Add(new DesktopComponentPlacementSnapshot
|
||||||
|
{
|
||||||
|
PlacementId = placement.PlacementId,
|
||||||
|
PageIndex = placement.PageIndex,
|
||||||
|
ComponentId = placement.ComponentId,
|
||||||
|
Row = placement.Row,
|
||||||
|
Column = placement.Column,
|
||||||
|
WidthCells = placement.WidthCells,
|
||||||
|
HeightCells = placement.HeightCells
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clone.DesktopComponentPlacements = placements;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
LanMountainDesktop/Models/LauncherSettingsSnapshot.cs
Normal file
22
LanMountainDesktop/Models/LauncherSettingsSnapshot.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Models;
|
||||||
|
|
||||||
|
public sealed class LauncherSettingsSnapshot
|
||||||
|
{
|
||||||
|
public List<string> HiddenLauncherFolderPaths { get; set; } = [];
|
||||||
|
|
||||||
|
public List<string> HiddenLauncherAppPaths { get; set; } = [];
|
||||||
|
|
||||||
|
public LauncherSettingsSnapshot Clone()
|
||||||
|
{
|
||||||
|
var clone = (LauncherSettingsSnapshot)MemberwiseClone();
|
||||||
|
clone.HiddenLauncherFolderPaths = HiddenLauncherFolderPaths is { Count: > 0 }
|
||||||
|
? new List<string>(HiddenLauncherFolderPaths)
|
||||||
|
: [];
|
||||||
|
clone.HiddenLauncherAppPaths = HiddenLauncherAppPaths is { Count: > 0 }
|
||||||
|
? new List<string>(HiddenLauncherAppPaths)
|
||||||
|
: [];
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
248
LanMountainDesktop/Services/DesktopLayoutSettingsService.cs
Normal file
248
LanMountainDesktop/Services/DesktopLayoutSettingsService.cs
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using LanMountainDesktop.Models;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Services;
|
||||||
|
|
||||||
|
public sealed class DesktopLayoutSettingsService
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
private static readonly object CacheGate = new();
|
||||||
|
private static readonly TimeSpan CacheProbeInterval = TimeSpan.FromMilliseconds(400);
|
||||||
|
|
||||||
|
private static string? _cachedPath;
|
||||||
|
private static DesktopLayoutSettingsSnapshot? _cachedSnapshot;
|
||||||
|
private static DateTime _cachedWriteTimeUtc = DateTime.MinValue;
|
||||||
|
private static DateTime _lastProbeUtc = DateTime.MinValue;
|
||||||
|
|
||||||
|
private readonly string _settingsPath;
|
||||||
|
private readonly string _legacyAppSettingsPath;
|
||||||
|
|
||||||
|
public DesktopLayoutSettingsService()
|
||||||
|
{
|
||||||
|
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
|
var settingsDirectory = Path.Combine(appData, "LanMountainDesktop");
|
||||||
|
_settingsPath = Path.Combine(settingsDirectory, "desktop-layout-settings.json");
|
||||||
|
_legacyAppSettingsPath = Path.Combine(settingsDirectory, "settings.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DesktopLayoutSettingsSnapshot Load()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var nowUtc = DateTime.UtcNow;
|
||||||
|
if (TryGetCachedWithoutProbe(nowUtc, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasFile = File.Exists(_settingsPath);
|
||||||
|
var writeTimeUtc = hasFile
|
||||||
|
? File.GetLastWriteTimeUtc(_settingsPath)
|
||||||
|
: DateTime.MinValue;
|
||||||
|
|
||||||
|
_lastProbeUtc = nowUtc;
|
||||||
|
if (TryGetCachedAfterProbe(writeTimeUtc, out cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopLayoutSettingsSnapshot loadedSnapshot;
|
||||||
|
var loadedFromLegacy = false;
|
||||||
|
if (hasFile)
|
||||||
|
{
|
||||||
|
loadedSnapshot = LoadSnapshotFromDisk();
|
||||||
|
}
|
||||||
|
else if (TryLoadLegacySnapshot(out var migratedSnapshot))
|
||||||
|
{
|
||||||
|
loadedSnapshot = migratedSnapshot;
|
||||||
|
loadedFromLegacy = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadedSnapshot = new DesktopLayoutSettingsSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedSnapshot = NormalizeSnapshot(loadedSnapshot);
|
||||||
|
if (loadedFromLegacy)
|
||||||
|
{
|
||||||
|
writeTimeUtc = PersistSnapshotToDisk(normalizedSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCache(normalizedSnapshot, writeTimeUtc, nowUtc);
|
||||||
|
return normalizedSnapshot.Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new DesktopLayoutSettingsSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(DesktopLayoutSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var snapshotToPersist = NormalizeSnapshot(snapshot);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var writeTimeUtc = PersistSnapshotToDisk(snapshotToPersist);
|
||||||
|
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
UpdateCache(snapshotToPersist, writeTimeUtc, DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedWithoutProbe(DateTime nowUtc, out DesktopLayoutSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
|
_cachedSnapshot is not null &&
|
||||||
|
nowUtc - _lastProbeUtc < CacheProbeInterval)
|
||||||
|
{
|
||||||
|
snapshot = _cachedSnapshot.Clone();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = null!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedAfterProbe(DateTime writeTimeUtc, out DesktopLayoutSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
|
_cachedSnapshot is not null &&
|
||||||
|
writeTimeUtc == _cachedWriteTimeUtc)
|
||||||
|
{
|
||||||
|
snapshot = _cachedSnapshot.Clone();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = null!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DesktopLayoutSettingsSnapshot LoadSnapshotFromDisk()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(_settingsPath);
|
||||||
|
var snapshot = JsonSerializer.Deserialize<DesktopLayoutSettingsSnapshot>(json, SerializerOptions);
|
||||||
|
return NormalizeSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new DesktopLayoutSettingsSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryLoadLegacySnapshot(out DesktopLayoutSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
snapshot = new DesktopLayoutSettingsSnapshot();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_legacyAppSettingsPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacyJson = File.ReadAllText(_legacyAppSettingsPath);
|
||||||
|
var legacy = JsonSerializer.Deserialize<LegacyDesktopLayoutSettingsSnapshot>(legacyJson, SerializerOptions);
|
||||||
|
if (legacy is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = new DesktopLayoutSettingsSnapshot
|
||||||
|
{
|
||||||
|
DesktopPageCount = legacy.DesktopPageCount,
|
||||||
|
CurrentDesktopSurfaceIndex = legacy.CurrentDesktopSurfaceIndex,
|
||||||
|
DesktopComponentPlacements = legacy.DesktopComponentPlacements ?? []
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime PersistSnapshotToDisk(DesktopLayoutSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var directory = Path.GetDirectoryName(_settingsPath);
|
||||||
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(snapshot, SerializerOptions);
|
||||||
|
File.WriteAllText(_settingsPath, json);
|
||||||
|
|
||||||
|
return File.Exists(_settingsPath)
|
||||||
|
? File.GetLastWriteTimeUtc(_settingsPath)
|
||||||
|
: DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DesktopLayoutSettingsSnapshot NormalizeSnapshot(DesktopLayoutSettingsSnapshot? snapshot)
|
||||||
|
{
|
||||||
|
var normalized = snapshot?.Clone() ?? new DesktopLayoutSettingsSnapshot();
|
||||||
|
normalized.DesktopPageCount = Math.Max(1, normalized.DesktopPageCount);
|
||||||
|
normalized.CurrentDesktopSurfaceIndex = Math.Max(0, normalized.CurrentDesktopSurfaceIndex);
|
||||||
|
|
||||||
|
var placements = new List<DesktopComponentPlacementSnapshot>(normalized.DesktopComponentPlacements?.Count ?? 0);
|
||||||
|
if (normalized.DesktopComponentPlacements is not null)
|
||||||
|
{
|
||||||
|
foreach (var placement in normalized.DesktopComponentPlacements)
|
||||||
|
{
|
||||||
|
if (placement is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
placements.Add(new DesktopComponentPlacementSnapshot
|
||||||
|
{
|
||||||
|
PlacementId = placement.PlacementId?.Trim() ?? string.Empty,
|
||||||
|
PageIndex = Math.Max(0, placement.PageIndex),
|
||||||
|
ComponentId = placement.ComponentId?.Trim() ?? string.Empty,
|
||||||
|
Row = Math.Max(0, placement.Row),
|
||||||
|
Column = Math.Max(0, placement.Column),
|
||||||
|
WidthCells = Math.Max(1, placement.WidthCells),
|
||||||
|
HeightCells = Math.Max(1, placement.HeightCells)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized.DesktopComponentPlacements = placements;
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCache(DesktopLayoutSettingsSnapshot snapshot, DateTime writeTimeUtc, DateTime probeTimeUtc)
|
||||||
|
{
|
||||||
|
_cachedPath = _settingsPath;
|
||||||
|
_cachedSnapshot = snapshot.Clone();
|
||||||
|
_cachedWriteTimeUtc = writeTimeUtc;
|
||||||
|
_lastProbeUtc = probeTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class LegacyDesktopLayoutSettingsSnapshot
|
||||||
|
{
|
||||||
|
public int DesktopPageCount { get; set; } = 1;
|
||||||
|
|
||||||
|
public int CurrentDesktopSurfaceIndex { get; set; }
|
||||||
|
|
||||||
|
public List<DesktopComponentPlacementSnapshot>? DesktopComponentPlacements { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
236
LanMountainDesktop/Services/LauncherSettingsService.cs
Normal file
236
LanMountainDesktop/Services/LauncherSettingsService.cs
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using LanMountainDesktop.Models;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Services;
|
||||||
|
|
||||||
|
public sealed class LauncherSettingsService
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||||||
|
{
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
private static readonly object CacheGate = new();
|
||||||
|
private static readonly TimeSpan CacheProbeInterval = TimeSpan.FromMilliseconds(400);
|
||||||
|
|
||||||
|
private static string? _cachedPath;
|
||||||
|
private static LauncherSettingsSnapshot? _cachedSnapshot;
|
||||||
|
private static DateTime _cachedWriteTimeUtc = DateTime.MinValue;
|
||||||
|
private static DateTime _lastProbeUtc = DateTime.MinValue;
|
||||||
|
|
||||||
|
private readonly string _settingsPath;
|
||||||
|
private readonly string _legacyAppSettingsPath;
|
||||||
|
|
||||||
|
public LauncherSettingsService()
|
||||||
|
{
|
||||||
|
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
|
var settingsDirectory = Path.Combine(appData, "LanMountainDesktop");
|
||||||
|
_settingsPath = Path.Combine(settingsDirectory, "launcher-settings.json");
|
||||||
|
_legacyAppSettingsPath = Path.Combine(settingsDirectory, "settings.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
public LauncherSettingsSnapshot Load()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var nowUtc = DateTime.UtcNow;
|
||||||
|
if (TryGetCachedWithoutProbe(nowUtc, out var cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasFile = File.Exists(_settingsPath);
|
||||||
|
var writeTimeUtc = hasFile
|
||||||
|
? File.GetLastWriteTimeUtc(_settingsPath)
|
||||||
|
: DateTime.MinValue;
|
||||||
|
|
||||||
|
_lastProbeUtc = nowUtc;
|
||||||
|
if (TryGetCachedAfterProbe(writeTimeUtc, out cached))
|
||||||
|
{
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherSettingsSnapshot loadedSnapshot;
|
||||||
|
var loadedFromLegacy = false;
|
||||||
|
if (hasFile)
|
||||||
|
{
|
||||||
|
loadedSnapshot = LoadSnapshotFromDisk();
|
||||||
|
}
|
||||||
|
else if (TryLoadLegacySnapshot(out var migratedSnapshot))
|
||||||
|
{
|
||||||
|
loadedSnapshot = migratedSnapshot;
|
||||||
|
loadedFromLegacy = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadedSnapshot = new LauncherSettingsSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedSnapshot = NormalizeSnapshot(loadedSnapshot);
|
||||||
|
if (loadedFromLegacy)
|
||||||
|
{
|
||||||
|
writeTimeUtc = PersistSnapshotToDisk(normalizedSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCache(normalizedSnapshot, writeTimeUtc, nowUtc);
|
||||||
|
return normalizedSnapshot.Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new LauncherSettingsSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(LauncherSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var snapshotToPersist = NormalizeSnapshot(snapshot);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var writeTimeUtc = PersistSnapshotToDisk(snapshotToPersist);
|
||||||
|
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
UpdateCache(snapshotToPersist, writeTimeUtc, DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedWithoutProbe(DateTime nowUtc, out LauncherSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
|
_cachedSnapshot is not null &&
|
||||||
|
nowUtc - _lastProbeUtc < CacheProbeInterval)
|
||||||
|
{
|
||||||
|
snapshot = _cachedSnapshot.Clone();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = null!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedAfterProbe(DateTime writeTimeUtc, out LauncherSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
|
_cachedSnapshot is not null &&
|
||||||
|
writeTimeUtc == _cachedWriteTimeUtc)
|
||||||
|
{
|
||||||
|
snapshot = _cachedSnapshot.Clone();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = null!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LauncherSettingsSnapshot LoadSnapshotFromDisk()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = File.ReadAllText(_settingsPath);
|
||||||
|
var snapshot = JsonSerializer.Deserialize<LauncherSettingsSnapshot>(json, SerializerOptions);
|
||||||
|
return NormalizeSnapshot(snapshot);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new LauncherSettingsSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryLoadLegacySnapshot(out LauncherSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
snapshot = new LauncherSettingsSnapshot();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(_legacyAppSettingsPath))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacyJson = File.ReadAllText(_legacyAppSettingsPath);
|
||||||
|
var legacy = JsonSerializer.Deserialize<LegacyLauncherSettingsSnapshot>(legacyJson, SerializerOptions);
|
||||||
|
if (legacy is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = new LauncherSettingsSnapshot
|
||||||
|
{
|
||||||
|
HiddenLauncherFolderPaths = legacy.HiddenLauncherFolderPaths ?? [],
|
||||||
|
HiddenLauncherAppPaths = legacy.HiddenLauncherAppPaths ?? []
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime PersistSnapshotToDisk(LauncherSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var directory = Path.GetDirectoryName(_settingsPath);
|
||||||
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(snapshot, SerializerOptions);
|
||||||
|
File.WriteAllText(_settingsPath, json);
|
||||||
|
|
||||||
|
return File.Exists(_settingsPath)
|
||||||
|
? File.GetLastWriteTimeUtc(_settingsPath)
|
||||||
|
: DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LauncherSettingsSnapshot NormalizeSnapshot(LauncherSettingsSnapshot? snapshot)
|
||||||
|
{
|
||||||
|
var normalized = snapshot?.Clone() ?? new LauncherSettingsSnapshot();
|
||||||
|
normalized.HiddenLauncherFolderPaths = NormalizeKeys(normalized.HiddenLauncherFolderPaths);
|
||||||
|
normalized.HiddenLauncherAppPaths = NormalizeKeys(normalized.HiddenLauncherAppPaths);
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> NormalizeKeys(IReadOnlyList<string>? values)
|
||||||
|
{
|
||||||
|
if (values is null || values.Count == 0)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
|
.Select(value => value.Trim())
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.OrderBy(value => value, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCache(LauncherSettingsSnapshot snapshot, DateTime writeTimeUtc, DateTime probeTimeUtc)
|
||||||
|
{
|
||||||
|
_cachedPath = _settingsPath;
|
||||||
|
_cachedSnapshot = snapshot.Clone();
|
||||||
|
_cachedWriteTimeUtc = writeTimeUtc;
|
||||||
|
_lastProbeUtc = probeTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class LegacyLauncherSettingsSnapshot
|
||||||
|
{
|
||||||
|
public List<string>? HiddenLauncherFolderPaths { get; set; }
|
||||||
|
|
||||||
|
public List<string>? HiddenLauncherAppPaths { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1431,7 +1431,7 @@ public partial class MainWindow
|
|||||||
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeDesktopComponentPlacements(AppSettingsSnapshot snapshot)
|
private void InitializeDesktopComponentPlacements(DesktopLayoutSettingsSnapshot snapshot)
|
||||||
{
|
{
|
||||||
_desktopComponentPlacements.Clear();
|
_desktopComponentPlacements.Clear();
|
||||||
|
|
||||||
@@ -3598,4 +3598,3 @@ public partial class MainWindow
|
|||||||
ApplyComponentLibraryComponentOffset();
|
ApplyComponentLibraryComponentOffset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ public partial class MainWindow
|
|||||||
|
|
||||||
private int TotalSurfaceCount => LauncherSurfaceIndex + 1;
|
private int TotalSurfaceCount => LauncherSurfaceIndex + 1;
|
||||||
|
|
||||||
private void InitializeDesktopSurfaceState(AppSettingsSnapshot snapshot)
|
private void InitializeDesktopSurfaceState(DesktopLayoutSettingsSnapshot snapshot)
|
||||||
{
|
{
|
||||||
var loadedPageCount = snapshot.DesktopPageCount <= 0 ? MinDesktopPageCount : snapshot.DesktopPageCount;
|
var loadedPageCount = snapshot.DesktopPageCount <= 0 ? MinDesktopPageCount : snapshot.DesktopPageCount;
|
||||||
_desktopPageCount = Math.Clamp(loadedPageCount, MinDesktopPageCount, MaxDesktopPageCount);
|
_desktopPageCount = Math.Clamp(loadedPageCount, MinDesktopPageCount, MaxDesktopPageCount);
|
||||||
_currentDesktopSurfaceIndex = Math.Clamp(snapshot.CurrentDesktopSurfaceIndex, 0, LauncherSurfaceIndex);
|
_currentDesktopSurfaceIndex = Math.Clamp(snapshot.CurrentDesktopSurfaceIndex, 0, LauncherSurfaceIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeLauncherVisibilitySettings(AppSettingsSnapshot snapshot)
|
private void InitializeLauncherVisibilitySettings(LauncherSettingsSnapshot snapshot)
|
||||||
{
|
{
|
||||||
_hiddenLauncherFolderPaths.Clear();
|
_hiddenLauncherFolderPaths.Clear();
|
||||||
if (snapshot.HiddenLauncherFolderPaths is not null)
|
if (snapshot.HiddenLauncherFolderPaths is not null)
|
||||||
|
|||||||
@@ -864,7 +864,14 @@ public partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var snapshot = new AppSettingsSnapshot
|
_appSettingsService.Save(BuildAppSettingsSnapshot());
|
||||||
|
_desktopLayoutSettingsService.Save(BuildDesktopLayoutSettingsSnapshot());
|
||||||
|
_launcherSettingsService.Save(BuildLauncherSettingsSnapshot());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppSettingsSnapshot BuildAppSettingsSnapshot()
|
||||||
|
{
|
||||||
|
return new AppSettingsSnapshot
|
||||||
{
|
{
|
||||||
GridShortSideCells = _targetShortSideCells,
|
GridShortSideCells = _targetShortSideCells,
|
||||||
GridSpacingPreset = _gridSpacingPreset,
|
GridSpacingPreset = _gridSpacingPreset,
|
||||||
@@ -896,15 +903,27 @@ public partial class MainWindow
|
|||||||
TaskbarLayoutMode = _taskbarLayoutMode,
|
TaskbarLayoutMode = _taskbarLayoutMode,
|
||||||
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
||||||
StatusBarSpacingMode = _statusBarSpacingMode,
|
StatusBarSpacingMode = _statusBarSpacingMode,
|
||||||
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent,
|
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private DesktopLayoutSettingsSnapshot BuildDesktopLayoutSettingsSnapshot()
|
||||||
|
{
|
||||||
|
return new DesktopLayoutSettingsSnapshot
|
||||||
|
{
|
||||||
DesktopPageCount = _desktopPageCount,
|
DesktopPageCount = _desktopPageCount,
|
||||||
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
|
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
|
||||||
DesktopComponentPlacements = _desktopComponentPlacements.ToList(),
|
DesktopComponentPlacements = _desktopComponentPlacements.ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private LauncherSettingsSnapshot BuildLauncherSettingsSnapshot()
|
||||||
|
{
|
||||||
|
return new LauncherSettingsSnapshot
|
||||||
|
{
|
||||||
HiddenLauncherFolderPaths = _hiddenLauncherFolderPaths.OrderBy(path => path, StringComparer.OrdinalIgnoreCase).ToList(),
|
HiddenLauncherFolderPaths = _hiddenLauncherFolderPaths.OrderBy(path => path, StringComparer.OrdinalIgnoreCase).ToList(),
|
||||||
HiddenLauncherAppPaths = _hiddenLauncherAppPaths.OrderBy(path => path, StringComparer.OrdinalIgnoreCase).ToList()
|
HiddenLauncherAppPaths = _hiddenLauncherAppPaths.OrderBy(path => path, StringComparer.OrdinalIgnoreCase).ToList()
|
||||||
};
|
};
|
||||||
|
|
||||||
_appSettingsService.Save(snapshot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDisposable? _persistSettingsDebounceTimer;
|
private IDisposable? _persistSettingsDebounceTimer;
|
||||||
@@ -2288,4 +2307,3 @@ public partial class MainWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
private readonly MonetColorService _monetColorService = new();
|
private readonly MonetColorService _monetColorService = new();
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
|
private readonly DesktopLayoutSettingsService _desktopLayoutSettingsService = new();
|
||||||
|
private readonly LauncherSettingsService _launcherSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private readonly ComponentSettingsService _componentSettingsService = new();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly TimeZoneService _timeZoneService = new();
|
private readonly TimeZoneService _timeZoneService = new();
|
||||||
@@ -193,6 +195,8 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
_suppressSettingsPersistence = true;
|
_suppressSettingsPersistence = true;
|
||||||
var snapshot = _appSettingsService.Load();
|
var snapshot = _appSettingsService.Load();
|
||||||
|
var desktopLayoutSnapshot = _desktopLayoutSettingsService.Load();
|
||||||
|
var launcherSnapshot = _launcherSettingsService.Load();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(snapshot.TimeZoneId))
|
if (!string.IsNullOrWhiteSpace(snapshot.TimeZoneId))
|
||||||
{
|
{
|
||||||
@@ -247,9 +251,9 @@ public partial class MainWindow : Window
|
|||||||
_ = _componentSettingsService.Load();
|
_ = _componentSettingsService.Load();
|
||||||
InitializeAutoStartWithWindowsSetting(snapshot);
|
InitializeAutoStartWithWindowsSetting(snapshot);
|
||||||
InitializeUpdateSettings(snapshot);
|
InitializeUpdateSettings(snapshot);
|
||||||
InitializeDesktopSurfaceState(snapshot);
|
InitializeDesktopSurfaceState(desktopLayoutSnapshot);
|
||||||
InitializeLauncherVisibilitySettings(snapshot);
|
InitializeLauncherVisibilitySettings(launcherSnapshot);
|
||||||
InitializeDesktopComponentPlacements(snapshot);
|
InitializeDesktopComponentPlacements(desktopLayoutSnapshot);
|
||||||
InitializeSettingsIcons();
|
InitializeSettingsIcons();
|
||||||
|
|
||||||
TryRestoreWallpaper(snapshot.WallpaperPath);
|
TryRestoreWallpaper(snapshot.WallpaperPath);
|
||||||
@@ -1341,4 +1345,3 @@ public partial class MainWindow : Window
|
|||||||
PersistSettings();
|
PersistSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
## 当前状态
|
## 当前状态
|
||||||
- 项目包含桌面端与推荐后端两个子项目,并在同一 solution 中维护。
|
- 项目包含桌面端与推荐后端两个子项目,并在同一 solution 中维护。
|
||||||
- 配置默认写入本地:`%LOCALAPPDATA%\LanMountainDesktop\settings.json`。
|
- 配置默认写入本地:`%LOCALAPPDATA%\LanMountainDesktop\settings.json`。
|
||||||
|
- 启动台与桌面布局现已拆分到独立文件:`%LOCALAPPDATA%\LanMountainDesktop\launcher-settings.json`、`%LOCALAPPDATA%\LanMountainDesktop\desktop-layout-settings.json`。
|
||||||
- 当前体验以 Windows 为主要目标平台。
|
- 当前体验以 Windows 为主要目标平台。
|
||||||
|
|
||||||
## 运行说明
|
## 运行说明
|
||||||
|
|||||||
Reference in New Issue
Block a user