mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
0.4.12
模块化解耦
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.ComponentSystem;
|
||||||
|
|
||||||
|
public sealed record DesktopComponentRuntimeContext(
|
||||||
|
string ComponentId,
|
||||||
|
string? PlacementId,
|
||||||
|
IComponentInstanceSettingsStore ComponentSettingsStore);
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LanMountainDesktop.ComponentSystem;
|
||||||
|
|
||||||
|
public interface IComponentPlacementContextAware
|
||||||
|
{
|
||||||
|
void SetComponentPlacementContext(string componentId, string? placementId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace LanMountainDesktop.ComponentSystem;
|
||||||
|
|
||||||
|
public interface IComponentRuntimeContextAware
|
||||||
|
{
|
||||||
|
void SetComponentRuntimeContext(DesktopComponentRuntimeContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.ComponentSystem;
|
||||||
|
|
||||||
|
public interface IComponentSettingsStoreAware
|
||||||
|
{
|
||||||
|
void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore);
|
||||||
|
}
|
||||||
@@ -2,12 +2,13 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Services;
|
namespace LanMountainDesktop.Services;
|
||||||
|
|
||||||
public sealed class ComponentSettingsService
|
public sealed class ComponentSettingsService : IComponentInstanceSettingsStore
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions SerializerOptions = new()
|
private static readonly JsonSerializerOptions SerializerOptions = new()
|
||||||
{
|
{
|
||||||
@@ -18,12 +19,14 @@ public sealed class ComponentSettingsService
|
|||||||
private static readonly TimeSpan CacheProbeInterval = TimeSpan.FromMilliseconds(400);
|
private static readonly TimeSpan CacheProbeInterval = TimeSpan.FromMilliseconds(400);
|
||||||
|
|
||||||
private static string? _cachedPath;
|
private static string? _cachedPath;
|
||||||
private static ComponentSettingsSnapshot? _cachedSnapshot;
|
private static ComponentSettingsDocumentSnapshot? _cachedSnapshot;
|
||||||
private static DateTime _cachedWriteTimeUtc = DateTime.MinValue;
|
private static DateTime _cachedWriteTimeUtc = DateTime.MinValue;
|
||||||
private static DateTime _lastProbeUtc = DateTime.MinValue;
|
private static DateTime _lastProbeUtc = DateTime.MinValue;
|
||||||
|
|
||||||
private readonly string _settingsPath;
|
private readonly string _settingsPath;
|
||||||
private readonly string _legacyAppSettingsPath;
|
private readonly string _legacyAppSettingsPath;
|
||||||
|
private string _scopedComponentId = string.Empty;
|
||||||
|
private string _scopedPlacementId = string.Empty;
|
||||||
|
|
||||||
public ComponentSettingsService()
|
public ComponentSettingsService()
|
||||||
{
|
{
|
||||||
@@ -35,51 +38,17 @@ public sealed class ComponentSettingsService
|
|||||||
|
|
||||||
public ComponentSettingsSnapshot Load()
|
public ComponentSettingsSnapshot Load()
|
||||||
{
|
{
|
||||||
|
if (HasScopedComponentContext())
|
||||||
|
{
|
||||||
|
return LoadForComponent(_scopedComponentId, _scopedPlacementId);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lock (CacheGate)
|
lock (CacheGate)
|
||||||
{
|
{
|
||||||
var nowUtc = DateTime.UtcNow;
|
var document = LoadDocumentLocked();
|
||||||
if (TryGetCachedWithoutProbe(nowUtc, out var cached))
|
return document.DefaultSettings.Clone();
|
||||||
{
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasFile = File.Exists(_settingsPath);
|
|
||||||
var writeTimeUtc = hasFile
|
|
||||||
? File.GetLastWriteTimeUtc(_settingsPath)
|
|
||||||
: DateTime.MinValue;
|
|
||||||
|
|
||||||
_lastProbeUtc = nowUtc;
|
|
||||||
if (TryGetCachedAfterProbe(writeTimeUtc, out cached))
|
|
||||||
{
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
ComponentSettingsSnapshot loadedSnapshot;
|
|
||||||
var loadedFromLegacy = false;
|
|
||||||
if (hasFile)
|
|
||||||
{
|
|
||||||
loadedSnapshot = LoadSnapshotFromDisk();
|
|
||||||
}
|
|
||||||
else if (TryLoadLegacySnapshot(out var migratedSnapshot))
|
|
||||||
{
|
|
||||||
loadedSnapshot = migratedSnapshot;
|
|
||||||
loadedFromLegacy = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
loadedSnapshot = new ComponentSettingsSnapshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
var normalizedSnapshot = NormalizeSnapshot(loadedSnapshot);
|
|
||||||
if (loadedFromLegacy)
|
|
||||||
{
|
|
||||||
writeTimeUtc = PersistSnapshotToDisk(normalizedSnapshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCache(normalizedSnapshot, writeTimeUtc, nowUtc);
|
|
||||||
return normalizedSnapshot.Clone();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -90,15 +59,21 @@ public sealed class ComponentSettingsService
|
|||||||
|
|
||||||
public void Save(ComponentSettingsSnapshot snapshot)
|
public void Save(ComponentSettingsSnapshot snapshot)
|
||||||
{
|
{
|
||||||
|
if (HasScopedComponentContext())
|
||||||
|
{
|
||||||
|
SaveForComponent(_scopedComponentId, _scopedPlacementId, snapshot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var snapshotToPersist = NormalizeSnapshot(snapshot);
|
var snapshotToPersist = NormalizeSnapshot(snapshot);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var writeTimeUtc = PersistSnapshotToDisk(snapshotToPersist);
|
|
||||||
|
|
||||||
lock (CacheGate)
|
lock (CacheGate)
|
||||||
{
|
{
|
||||||
UpdateCache(snapshotToPersist, writeTimeUtc, DateTime.UtcNow);
|
var document = LoadDocumentLocked();
|
||||||
|
document.DefaultSettings = snapshotToPersist;
|
||||||
|
PersistDocumentLocked(document);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -107,7 +82,201 @@ public sealed class ComponentSettingsService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetCachedWithoutProbe(DateTime nowUtc, out ComponentSettingsSnapshot snapshot)
|
public ComponentSettingsSnapshot LoadForComponent(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(instanceKey) &&
|
||||||
|
document.InstanceSettings.TryGetValue(instanceKey, out var snapshot))
|
||||||
|
{
|
||||||
|
return snapshot.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.DefaultSettings.Clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new ComponentSettingsSnapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveForComponent(string componentId, string? placementId, ComponentSettingsSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var normalizedSnapshot = NormalizeSnapshot(snapshot);
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (string.IsNullOrWhiteSpace(instanceKey))
|
||||||
|
{
|
||||||
|
Save(normalizedSnapshot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
document.InstanceSettings[instanceKey] = normalizedSnapshot;
|
||||||
|
PersistDocumentLocked(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteForComponent(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (string.IsNullOrWhiteSpace(instanceKey))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
var changed = document.InstanceSettings.Remove(instanceKey);
|
||||||
|
changed |= document.PluginSettings.Remove(instanceKey);
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
PersistDocumentLocked(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T LoadPluginSettings<T>(string componentId, string? placementId) where T : new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (string.IsNullOrWhiteSpace(instanceKey) ||
|
||||||
|
!document.PluginSettings.TryGetValue(instanceKey, out var settingsElement))
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(settingsElement.GetRawText(), SerializerOptions) ?? new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SavePluginSettings<T>(string componentId, string? placementId, T settings)
|
||||||
|
{
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (string.IsNullOrWhiteSpace(instanceKey))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
document.PluginSettings[instanceKey] = JsonSerializer.SerializeToElement(settings, SerializerOptions).Clone();
|
||||||
|
PersistDocumentLocked(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeletePluginSettings(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
var instanceKey = BuildInstanceKey(componentId, placementId);
|
||||||
|
if (string.IsNullOrWhiteSpace(instanceKey))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (CacheGate)
|
||||||
|
{
|
||||||
|
var document = LoadDocumentLocked();
|
||||||
|
if (document.PluginSettings.Remove(instanceKey))
|
||||||
|
{
|
||||||
|
PersistDocumentLocked(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Swallow persistence errors to keep UI interactions uninterrupted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScopedComponentContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_scopedComponentId = componentId?.Trim() ?? string.Empty;
|
||||||
|
_scopedPlacementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearScopedComponentContext()
|
||||||
|
{
|
||||||
|
_scopedComponentId = string.Empty;
|
||||||
|
_scopedPlacementId = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ApplyScopedContextToTarget(object? target, string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
if (target is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||||
|
foreach (var field in target.GetType().GetFields(flags))
|
||||||
|
{
|
||||||
|
if (field.FieldType != typeof(ComponentSettingsService))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.GetValue(target) is ComponentSettingsService settingsService)
|
||||||
|
{
|
||||||
|
settingsService.SetScopedComponentContext(componentId, placementId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var property in target.GetType().GetProperties(flags))
|
||||||
|
{
|
||||||
|
if (property.PropertyType != typeof(ComponentSettingsService) ||
|
||||||
|
!property.CanRead)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property.GetValue(target) is ComponentSettingsService settingsService)
|
||||||
|
{
|
||||||
|
settingsService.SetScopedComponentContext(componentId, placementId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetCachedWithoutProbe(DateTime nowUtc, out ComponentSettingsDocumentSnapshot snapshot)
|
||||||
{
|
{
|
||||||
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
_cachedSnapshot is not null &&
|
_cachedSnapshot is not null &&
|
||||||
@@ -121,7 +290,7 @@ public sealed class ComponentSettingsService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetCachedAfterProbe(DateTime writeTimeUtc, out ComponentSettingsSnapshot snapshot)
|
private bool TryGetCachedAfterProbe(DateTime writeTimeUtc, out ComponentSettingsDocumentSnapshot snapshot)
|
||||||
{
|
{
|
||||||
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
|
||||||
_cachedSnapshot is not null &&
|
_cachedSnapshot is not null &&
|
||||||
@@ -135,17 +304,78 @@ public sealed class ComponentSettingsService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComponentSettingsSnapshot LoadSnapshotFromDisk()
|
private ComponentSettingsDocumentSnapshot LoadDocumentLocked()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentSettingsDocumentSnapshot loadedSnapshot;
|
||||||
|
var loadedFromLegacy = false;
|
||||||
|
if (hasFile)
|
||||||
|
{
|
||||||
|
loadedSnapshot = LoadSnapshotFromDisk();
|
||||||
|
}
|
||||||
|
else if (TryLoadLegacySnapshot(out var migratedSnapshot))
|
||||||
|
{
|
||||||
|
loadedSnapshot = new ComponentSettingsDocumentSnapshot
|
||||||
|
{
|
||||||
|
DefaultSettings = NormalizeSnapshot(migratedSnapshot)
|
||||||
|
};
|
||||||
|
loadedFromLegacy = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadedSnapshot = new ComponentSettingsDocumentSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedSnapshot = NormalizeDocument(loadedSnapshot);
|
||||||
|
if (loadedFromLegacy)
|
||||||
|
{
|
||||||
|
writeTimeUtc = PersistSnapshotToDisk(normalizedSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateCache(normalizedSnapshot, writeTimeUtc, nowUtc);
|
||||||
|
return normalizedSnapshot.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentSettingsDocumentSnapshot LoadSnapshotFromDisk()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(_settingsPath);
|
var json = File.ReadAllText(_settingsPath);
|
||||||
var snapshot = JsonSerializer.Deserialize<ComponentSettingsSnapshot>(json, SerializerOptions);
|
using var document = JsonDocument.Parse(json);
|
||||||
return NormalizeSnapshot(snapshot);
|
if (document.RootElement.ValueKind == JsonValueKind.Object &&
|
||||||
|
(document.RootElement.TryGetProperty("defaultSettings", out _) ||
|
||||||
|
document.RootElement.TryGetProperty("instanceSettings", out _) ||
|
||||||
|
document.RootElement.TryGetProperty("pluginSettings", out _)))
|
||||||
|
{
|
||||||
|
var snapshot = JsonSerializer.Deserialize<ComponentSettingsDocumentSnapshot>(json, SerializerOptions);
|
||||||
|
return NormalizeDocument(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacySnapshot = JsonSerializer.Deserialize<ComponentSettingsSnapshot>(json, SerializerOptions);
|
||||||
|
return new ComponentSettingsDocumentSnapshot
|
||||||
|
{
|
||||||
|
DefaultSettings = NormalizeSnapshot(legacySnapshot)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return new ComponentSettingsSnapshot();
|
return new ComponentSettingsDocumentSnapshot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +434,13 @@ public sealed class ComponentSettingsService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime PersistSnapshotToDisk(ComponentSettingsSnapshot snapshot)
|
private void PersistDocumentLocked(ComponentSettingsDocumentSnapshot snapshot)
|
||||||
|
{
|
||||||
|
var writeTimeUtc = PersistSnapshotToDisk(snapshot);
|
||||||
|
UpdateCache(snapshot, writeTimeUtc, DateTime.UtcNow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DateTime PersistSnapshotToDisk(ComponentSettingsDocumentSnapshot snapshot)
|
||||||
{
|
{
|
||||||
var directory = Path.GetDirectoryName(_settingsPath);
|
var directory = Path.GetDirectoryName(_settingsPath);
|
||||||
if (!string.IsNullOrWhiteSpace(directory))
|
if (!string.IsNullOrWhiteSpace(directory))
|
||||||
@@ -257,6 +493,40 @@ public sealed class ComponentSettingsService
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ComponentSettingsDocumentSnapshot NormalizeDocument(ComponentSettingsDocumentSnapshot? snapshot)
|
||||||
|
{
|
||||||
|
var normalized = snapshot?.Clone() ?? new ComponentSettingsDocumentSnapshot();
|
||||||
|
normalized.DefaultSettings = NormalizeSnapshot(normalized.DefaultSettings);
|
||||||
|
|
||||||
|
var instanceSettings = new Dictionary<string, ComponentSettingsSnapshot>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var pair in normalized.InstanceSettings)
|
||||||
|
{
|
||||||
|
var key = NormalizeInstanceKey(pair.Key);
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceSettings[key] = NormalizeSnapshot(pair.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluginSettings = new Dictionary<string, JsonElement>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var pair in normalized.PluginSettings)
|
||||||
|
{
|
||||||
|
var key = NormalizeInstanceKey(pair.Key);
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginSettings[key] = pair.Value.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized.InstanceSettings = instanceSettings;
|
||||||
|
normalized.PluginSettings = pluginSettings;
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
private static List<ImportedClassScheduleSnapshot> NormalizeImportedSchedules(
|
private static List<ImportedClassScheduleSnapshot> NormalizeImportedSchedules(
|
||||||
IReadOnlyList<ImportedClassScheduleSnapshot>? schedules)
|
IReadOnlyList<ImportedClassScheduleSnapshot>? schedules)
|
||||||
{
|
{
|
||||||
@@ -360,7 +630,30 @@ public sealed class ComponentSettingsService
|
|||||||
return RefreshIntervalCatalog.Normalize(minutes, 20);
|
return RefreshIntervalCatalog.Normalize(minutes, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateCache(ComponentSettingsSnapshot snapshot, DateTime writeTimeUtc, DateTime probeTimeUtc)
|
private static string BuildInstanceKey(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
var normalizedComponentId = componentId?.Trim() ?? string.Empty;
|
||||||
|
var normalizedPlacementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(normalizedComponentId) || string.IsNullOrWhiteSpace(normalizedPlacementId))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{normalizedComponentId}::{normalizedPlacementId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeInstanceKey(string? key)
|
||||||
|
{
|
||||||
|
return key?.Trim() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasScopedComponentContext()
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(_scopedComponentId) &&
|
||||||
|
!string.IsNullOrWhiteSpace(_scopedPlacementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCache(ComponentSettingsDocumentSnapshot snapshot, DateTime writeTimeUtc, DateTime probeTimeUtc)
|
||||||
{
|
{
|
||||||
_cachedPath = _settingsPath;
|
_cachedPath = _settingsPath;
|
||||||
_cachedSnapshot = snapshot.Clone();
|
_cachedSnapshot = snapshot.Clone();
|
||||||
@@ -368,6 +661,39 @@ public sealed class ComponentSettingsService
|
|||||||
_lastProbeUtc = probeTimeUtc;
|
_lastProbeUtc = probeTimeUtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class ComponentSettingsDocumentSnapshot
|
||||||
|
{
|
||||||
|
public ComponentSettingsSnapshot DefaultSettings { get; set; } = new();
|
||||||
|
|
||||||
|
public Dictionary<string, ComponentSettingsSnapshot> InstanceSettings { get; set; } =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public Dictionary<string, JsonElement> PluginSettings { get; set; } =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public ComponentSettingsDocumentSnapshot Clone()
|
||||||
|
{
|
||||||
|
var clone = new ComponentSettingsDocumentSnapshot
|
||||||
|
{
|
||||||
|
DefaultSettings = DefaultSettings?.Clone() ?? new ComponentSettingsSnapshot(),
|
||||||
|
InstanceSettings = new Dictionary<string, ComponentSettingsSnapshot>(StringComparer.OrdinalIgnoreCase),
|
||||||
|
PluginSettings = new Dictionary<string, JsonElement>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pair in InstanceSettings)
|
||||||
|
{
|
||||||
|
clone.InstanceSettings[pair.Key] = pair.Value?.Clone() ?? new ComponentSettingsSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var pair in PluginSettings)
|
||||||
|
{
|
||||||
|
clone.PluginSettings[pair.Key] = pair.Value.Clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class LegacyComponentSettingsSnapshot
|
private sealed class LegacyComponentSettingsSnapshot
|
||||||
{
|
{
|
||||||
public string DailyArtworkMirrorSource { get; set; } = DailyArtworkMirrorSources.Overseas;
|
public string DailyArtworkMirrorSource { get; set; } = DailyArtworkMirrorSources.Overseas;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using LanMountainDesktop.Models;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Services;
|
||||||
|
|
||||||
|
public interface IComponentInstanceSettingsStore
|
||||||
|
{
|
||||||
|
ComponentSettingsSnapshot Load();
|
||||||
|
|
||||||
|
void Save(ComponentSettingsSnapshot snapshot);
|
||||||
|
|
||||||
|
ComponentSettingsSnapshot LoadForComponent(string componentId, string? placementId);
|
||||||
|
|
||||||
|
void SaveForComponent(string componentId, string? placementId, ComponentSettingsSnapshot snapshot);
|
||||||
|
|
||||||
|
void DeleteForComponent(string componentId, string? placementId);
|
||||||
|
|
||||||
|
T LoadPluginSettings<T>(string componentId, string? placementId) where T : new();
|
||||||
|
|
||||||
|
void SavePluginSettings<T>(string componentId, string? placementId, T settings);
|
||||||
|
|
||||||
|
void DeletePluginSettings(string componentId, string? placementId);
|
||||||
|
}
|
||||||
@@ -7,11 +7,12 @@ using Avalonia.Controls.Shapes;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget
|
public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyDictionary<string, string> ZhCityNames =
|
private static readonly IReadOnlyDictionary<string, string> ZhCityNames =
|
||||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -54,9 +55,11 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
|||||||
|
|
||||||
private const double DialSize = 258;
|
private const double DialSize = 258;
|
||||||
private const double Center = DialSize / 2;
|
private const double Center = DialSize / 2;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopClock;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private TimeZoneService? _timeZoneService;
|
private TimeZoneService? _timeZoneService;
|
||||||
private double _currentCellSize = 48;
|
private double _currentCellSize = 48;
|
||||||
@@ -112,6 +115,21 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
|||||||
UpdateClock();
|
UpdateClock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopClock
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
InitializeDialIfNeeded();
|
InitializeDialIfNeeded();
|
||||||
@@ -359,7 +377,7 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
|||||||
private void LoadClockSettings()
|
private void LoadClockSettings()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
var configuredTimeZoneId = string.IsNullOrWhiteSpace(componentSnapshot.DesktopClockTimeZoneId)
|
var configuredTimeZoneId = string.IsNullOrWhiteSpace(componentSnapshot.DesktopClockTimeZoneId)
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ using System.Linq;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class AnalogClockWidgetSettingsWindow : UserControl
|
public partial class AnalogClockWidgetSettingsWindow : UserControl, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyDictionary<string, string> ZhTimeZoneNames =
|
private static readonly IReadOnlyDictionary<string, string> ZhTimeZoneNames =
|
||||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -28,11 +29,13 @@ public partial class AnalogClockWidgetSettingsWindow : UserControl
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly TimeZoneService _timeZoneService = new();
|
private readonly TimeZoneService _timeZoneService = new();
|
||||||
private bool _suppressEvents;
|
private bool _suppressEvents;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopClock;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private IReadOnlyList<TimeZoneInfo> _allTimeZones = Array.Empty<TimeZoneInfo>();
|
private IReadOnlyList<TimeZoneInfo> _allTimeZones = Array.Empty<TimeZoneInfo>();
|
||||||
private string _selectedTimeZoneId = string.Empty;
|
private string _selectedTimeZoneId = string.Empty;
|
||||||
private string _secondHandMode = ClockSecondHandMode.Tick;
|
private string _secondHandMode = ClockSecondHandMode.Tick;
|
||||||
@@ -47,10 +50,29 @@ public partial class AnalogClockWidgetSettingsWindow : UserControl
|
|||||||
PopulateTimeZoneComboBox();
|
PopulateTimeZoneComboBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopClock
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
PopulateTimeZoneComboBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
PopulateTimeZoneComboBox();
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadState()
|
private void LoadState()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
_selectedTimeZoneId = string.IsNullOrWhiteSpace(componentSnapshot.DesktopClockTimeZoneId)
|
_selectedTimeZoneId = string.IsNullOrWhiteSpace(componentSnapshot.DesktopClockTimeZoneId)
|
||||||
? "China Standard Time"
|
? "China Standard Time"
|
||||||
@@ -149,10 +171,10 @@ public partial class AnalogClockWidgetSettingsWindow : UserControl
|
|||||||
_selectedTimeZoneId = normalizedId;
|
_selectedTimeZoneId = normalizedId;
|
||||||
_secondHandMode = GetSelectedSecondHandMode();
|
_secondHandMode = GetSelectedSecondHandMode();
|
||||||
|
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
snapshot.DesktopClockTimeZoneId = normalizedId;
|
snapshot.DesktopClockTimeZoneId = normalizedId;
|
||||||
snapshot.DesktopClockSecondHandMode = _secondHandMode;
|
snapshot.DesktopClockSecondHandMode = _secondHandMode;
|
||||||
_componentSettingsService.Save(snapshot);
|
_componentSettingsStore.SaveForComponent(_componentId, _placementId, snapshot);
|
||||||
|
|
||||||
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,22 @@ using Avalonia.Interactivity;
|
|||||||
using Avalonia.Layout;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class ClassScheduleSettingsWindow : UserControl
|
public partial class ClassScheduleSettingsWindow : UserControl, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly List<ImportedClassScheduleSnapshot> _importedSchedules = [];
|
private readonly List<ImportedClassScheduleSnapshot> _importedSchedules = [];
|
||||||
private string _activeScheduleId = string.Empty;
|
private string _activeScheduleId = string.Empty;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopClassSchedule;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public event EventHandler? SettingsChanged;
|
public event EventHandler? SettingsChanged;
|
||||||
|
|
||||||
@@ -34,10 +37,29 @@ public partial class ClassScheduleSettingsWindow : UserControl
|
|||||||
RenderImportedSchedules();
|
RenderImportedSchedules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopClassSchedule
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
RenderImportedSchedules();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
RenderImportedSchedules();
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadState()
|
private void LoadState()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
_importedSchedules.Clear();
|
_importedSchedules.Clear();
|
||||||
@@ -299,7 +321,7 @@ public partial class ClassScheduleSettingsWindow : UserControl
|
|||||||
|
|
||||||
private void SaveState()
|
private void SaveState()
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
snapshot.ImportedClassSchedules = _importedSchedules
|
snapshot.ImportedClassSchedules = _importedSchedules
|
||||||
.Select(item => new ImportedClassScheduleSnapshot
|
.Select(item => new ImportedClassScheduleSnapshot
|
||||||
{
|
{
|
||||||
@@ -309,7 +331,7 @@ public partial class ClassScheduleSettingsWindow : UserControl
|
|||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
snapshot.ActiveImportedClassScheduleId = _activeScheduleId ?? string.Empty;
|
snapshot.ActiveImportedClassScheduleId = _activeScheduleId ?? string.Empty;
|
||||||
_componentSettingsService.Save(snapshot);
|
_componentSettingsStore.SaveForComponent(_componentId, _placementId, snapshot);
|
||||||
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget
|
public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private sealed record CourseItemViewModel(
|
private sealed record CourseItemViewModel(
|
||||||
string Name,
|
string Name,
|
||||||
@@ -26,7 +27,7 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly IClassIslandScheduleDataService _scheduleService = new ClassIslandScheduleDataService();
|
private readonly IClassIslandScheduleDataService _scheduleService = new ClassIslandScheduleDataService();
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
|||||||
private IReadOnlyList<CourseItemViewModel> _courseItems = Array.Empty<CourseItemViewModel>();
|
private IReadOnlyList<CourseItemViewModel> _courseItems = Array.Empty<CourseItemViewModel>();
|
||||||
private bool _isNightVisual = true;
|
private bool _isNightVisual = true;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopClassSchedule;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public ClassScheduleWidget()
|
public ClassScheduleWidget()
|
||||||
{
|
{
|
||||||
@@ -113,10 +116,25 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
|||||||
RefreshSchedule();
|
RefreshSchedule();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopClassSchedule
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshSchedule()
|
private void RefreshSchedule()
|
||||||
{
|
{
|
||||||
var appSettings = _appSettingsService.Load();
|
var appSettings = _appSettingsService.Load();
|
||||||
var componentSettings = _componentSettingsService.Load();
|
var componentSettings = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSettings.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSettings.LanguageCode);
|
||||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||||
UpdateHeader(now);
|
UpdateHeader(now);
|
||||||
|
|||||||
@@ -2,18 +2,21 @@ using System;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class DailyArtworkSettingsWindow : UserControl
|
public partial class DailyArtworkSettingsWindow : UserControl, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
private bool _suppressEvents;
|
private bool _suppressEvents;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopDailyArtwork;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public event EventHandler? SettingsChanged;
|
public event EventHandler? SettingsChanged;
|
||||||
|
|
||||||
@@ -24,10 +27,27 @@ public partial class DailyArtworkSettingsWindow : UserControl
|
|||||||
ApplyLocalization();
|
ApplyLocalization();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopDailyArtwork
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadState()
|
private void LoadState()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
var source = DailyArtworkMirrorSources.Normalize(componentSnapshot.DailyArtworkMirrorSource);
|
var source = DailyArtworkMirrorSources.Normalize(componentSnapshot.DailyArtworkMirrorSource);
|
||||||
@@ -59,9 +79,9 @@ public partial class DailyArtworkSettingsWindow : UserControl
|
|||||||
}
|
}
|
||||||
|
|
||||||
var source = GetSelectedSource();
|
var source = GetSelectedSource();
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
snapshot.DailyArtworkMirrorSource = source;
|
snapshot.DailyArtworkMirrorSource = source;
|
||||||
_componentSettingsService.Save(snapshot);
|
_componentSettingsStore.SaveForComponent(_componentId, _placementId, snapshot);
|
||||||
|
|
||||||
UpdateSourceStatus(source);
|
UpdateSourceStatus(source);
|
||||||
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget, IRecommendationInfoAwareComponentWidget
|
public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget, IRecommendationInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyDictionary<DayOfWeek, string> ZhWeekdays =
|
private static readonly IReadOnlyDictionary<DayOfWeek, string> ZhWeekdays =
|
||||||
new Dictionary<DayOfWeek, string>
|
new Dictionary<DayOfWeek, string>
|
||||||
@@ -58,6 +59,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
|
|
||||||
private IRecommendationInfoService _recommendationService = DefaultRecommendationService;
|
private IRecommendationInfoService _recommendationService = DefaultRecommendationService;
|
||||||
@@ -67,6 +69,8 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
|||||||
private double _currentCellSize = BaseCellSize;
|
private double _currentCellSize = BaseCellSize;
|
||||||
private bool _isAttached;
|
private bool _isAttached;
|
||||||
private bool _isRefreshing;
|
private bool _isRefreshing;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopDailyArtwork;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private string? _currentArtworkSourceUrl;
|
private string? _currentArtworkSourceUrl;
|
||||||
private string? _currentArtworkImageUrl;
|
private string? _currentArtworkImageUrl;
|
||||||
|
|
||||||
@@ -136,6 +140,21 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopDailyArtwork
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
_isAttached = true;
|
_isAttached = true;
|
||||||
@@ -203,6 +222,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
|||||||
{
|
{
|
||||||
var query = new DailyArtworkQuery(
|
var query = new DailyArtworkQuery(
|
||||||
Locale: _languageCode,
|
Locale: _languageCode,
|
||||||
|
MirrorSource: ResolveMirrorSource(),
|
||||||
ForceRefresh: forceRefresh);
|
ForceRefresh: forceRefresh);
|
||||||
var result = await _recommendationService.GetDailyArtworkAsync(query, cts.Token);
|
var result = await _recommendationService.GetDailyArtworkAsync(query, cts.Token);
|
||||||
if (!_isAttached || cts.IsCancellationRequested)
|
if (!_isAttached || cts.IsCancellationRequested)
|
||||||
@@ -640,6 +660,19 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ResolveMirrorSource()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
|
return DailyArtworkMirrorSources.Normalize(snapshot.DailyArtworkMirrorSource);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DailyArtworkMirrorSources.Overseas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateDateLabels()
|
private void UpdateDateLabels()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
|
|||||||
@@ -42,9 +42,33 @@ public sealed class DesktopComponentRuntimeDescriptor
|
|||||||
TimeZoneService timeZoneService,
|
TimeZoneService timeZoneService,
|
||||||
IWeatherInfoService weatherInfoService,
|
IWeatherInfoService weatherInfoService,
|
||||||
IRecommendationInfoService recommendationInfoService,
|
IRecommendationInfoService recommendationInfoService,
|
||||||
ICalculatorDataService calculatorDataService)
|
ICalculatorDataService calculatorDataService,
|
||||||
|
IComponentInstanceSettingsStore componentSettingsStore,
|
||||||
|
string? placementId = null)
|
||||||
{
|
{
|
||||||
var control = _controlFactory();
|
var control = _controlFactory();
|
||||||
|
var runtimeContext = new DesktopComponentRuntimeContext(
|
||||||
|
Definition.Id,
|
||||||
|
placementId,
|
||||||
|
componentSettingsStore);
|
||||||
|
|
||||||
|
if (control is IComponentRuntimeContextAware runtimeContextAwareComponent)
|
||||||
|
{
|
||||||
|
runtimeContextAwareComponent.SetComponentRuntimeContext(runtimeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control is IComponentPlacementContextAware placementAwareComponent)
|
||||||
|
{
|
||||||
|
placementAwareComponent.SetComponentPlacementContext(Definition.Id, placementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (control is IComponentSettingsStoreAware settingsStoreAwareComponent)
|
||||||
|
{
|
||||||
|
settingsStoreAwareComponent.SetComponentSettingsStore(componentSettingsStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentSettingsService.ApplyScopedContextToTarget(control, Definition.Id, placementId);
|
||||||
|
|
||||||
if (control is IDesktopComponentWidget sizedComponent)
|
if (control is IDesktopComponentWidget sizedComponent)
|
||||||
{
|
{
|
||||||
sizedComponent.ApplyCellSize(cellSize);
|
sizedComponent.ApplyCellSize(cellSize);
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Theme;
|
using LanMountainDesktop.Theme;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget
|
public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IWeatherInfoService DefaultWeatherInfoService = new XiaomiWeatherService();
|
private static readonly IWeatherInfoService DefaultWeatherInfoService = new XiaomiWeatherService();
|
||||||
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
||||||
@@ -25,7 +26,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
private readonly ScaleTransform _backgroundMotionScaleTransform = new(1, 1);
|
private readonly ScaleTransform _backgroundMotionScaleTransform = new(1, 1);
|
||||||
private readonly TranslateTransform _backgroundMotionTranslateTransform = new();
|
private readonly TranslateTransform _backgroundMotionTranslateTransform = new();
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
|
|
||||||
private IWeatherInfoService _weatherInfoService = DefaultWeatherInfoService;
|
private IWeatherInfoService _weatherInfoService = DefaultWeatherInfoService;
|
||||||
@@ -39,6 +40,8 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
private bool _autoRefreshEnabled = true;
|
private bool _autoRefreshEnabled = true;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
private HyperOS3WeatherVisualKind _activeVisualKind = HyperOS3WeatherVisualKind.ClearDay;
|
private HyperOS3WeatherVisualKind _activeVisualKind = HyperOS3WeatherVisualKind.ClearDay;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopExtendedWeather;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private readonly TextBlock[] _hourlyTempBlocks;
|
private readonly TextBlock[] _hourlyTempBlocks;
|
||||||
private readonly TextBlock[] _hourlyTimeBlocks;
|
private readonly TextBlock[] _hourlyTimeBlocks;
|
||||||
private readonly Image[] _hourlyIconBlocks;
|
private readonly Image[] _hourlyIconBlocks;
|
||||||
@@ -173,6 +176,21 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopExtendedWeather
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||||
{
|
{
|
||||||
_ = isEditMode;
|
_ = isEditMode;
|
||||||
@@ -935,7 +953,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ using Avalonia.Media;
|
|||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Theme;
|
using LanMountainDesktop.Theme;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget
|
public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private enum WeatherVisualKind
|
private enum WeatherVisualKind
|
||||||
{
|
{
|
||||||
@@ -95,7 +96,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
||||||
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
||||||
@@ -118,6 +119,8 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
|||||||
private bool _isOnActivePage = true;
|
private bool _isOnActivePage = true;
|
||||||
private bool _isRefreshing;
|
private bool _isRefreshing;
|
||||||
private bool _autoRefreshEnabled = true;
|
private bool _autoRefreshEnabled = true;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopHourlyWeather;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private readonly TextBlock[] _hourlyTimeBlocks;
|
private readonly TextBlock[] _hourlyTimeBlocks;
|
||||||
private readonly Image[] _hourlyIconBlocks;
|
private readonly Image[] _hourlyIconBlocks;
|
||||||
private readonly TextBlock[] _hourlyTempBlocks;
|
private readonly TextBlock[] _hourlyTempBlocks;
|
||||||
@@ -224,6 +227,21 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopHourlyWeather
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||||
{
|
{
|
||||||
_ = isEditMode;
|
_ = isEditMode;
|
||||||
@@ -1424,7 +1442,7 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Theme;
|
using LanMountainDesktop.Theme;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget
|
public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private enum WeatherVisualKind
|
private enum WeatherVisualKind
|
||||||
{
|
{
|
||||||
@@ -93,7 +94,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
||||||
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
||||||
@@ -116,6 +117,8 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
private bool _isOnActivePage = true;
|
private bool _isOnActivePage = true;
|
||||||
private bool _isRefreshing;
|
private bool _isRefreshing;
|
||||||
private bool _autoRefreshEnabled = true;
|
private bool _autoRefreshEnabled = true;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopMultiDayWeather;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private readonly TextBlock[] _hourlyTimeBlocks;
|
private readonly TextBlock[] _hourlyTimeBlocks;
|
||||||
private readonly Image[] _hourlyIconBlocks;
|
private readonly Image[] _hourlyIconBlocks;
|
||||||
private readonly TextBlock[] _hourlyTempBlocks;
|
private readonly TextBlock[] _hourlyTempBlocks;
|
||||||
@@ -222,6 +225,21 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopMultiDayWeather
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||||
{
|
{
|
||||||
_ = isEditMode;
|
_ = isEditMode;
|
||||||
@@ -1274,7 +1292,7 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ using Avalonia.Controls.Shapes;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget
|
public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private sealed record WeatherClockConfig(
|
private sealed record WeatherClockConfig(
|
||||||
string LanguageCode,
|
string LanguageCode,
|
||||||
@@ -41,7 +42,7 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly Line _hourHandLine = CreateHandLine("#232938", 4.0);
|
private readonly Line _hourHandLine = CreateHandLine("#232938", 4.0);
|
||||||
private readonly Line _minuteHandLine = CreateHandLine("#2F3749", 2.8);
|
private readonly Line _minuteHandLine = CreateHandLine("#2F3749", 2.8);
|
||||||
@@ -59,6 +60,8 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
|||||||
private bool? _isNightModeApplied;
|
private bool? _isNightModeApplied;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
private HyperOS3WeatherVisualKind _activeVisualKind = HyperOS3WeatherVisualKind.CloudyDay;
|
private HyperOS3WeatherVisualKind _activeVisualKind = HyperOS3WeatherVisualKind.CloudyDay;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopWeatherClock;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public WeatherClockWidget()
|
public WeatherClockWidget()
|
||||||
{
|
{
|
||||||
@@ -116,6 +119,21 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopWeatherClock
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyCellSize(double cellSize)
|
public void ApplyCellSize(double cellSize)
|
||||||
{
|
{
|
||||||
_currentCellSize = Math.Max(1, cellSize);
|
_currentCellSize = Math.Max(1, cellSize);
|
||||||
@@ -659,7 +677,7 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ using Avalonia.Media;
|
|||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Theme;
|
using LanMountainDesktop.Theme;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget
|
public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private enum WeatherVisualKind
|
private enum WeatherVisualKind
|
||||||
{
|
{
|
||||||
@@ -89,7 +90,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _settingsService = new();
|
private readonly AppSettingsService _settingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
private readonly Dictionary<WeatherVisualKind, IBrush> _backgroundBrushCache = new();
|
||||||
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
private readonly Dictionary<HyperOS3WeatherVisualKind, IBrush> _particleBrushCache = new();
|
||||||
@@ -112,6 +113,8 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
|
|||||||
private bool _isOnActivePage = true;
|
private bool _isOnActivePage = true;
|
||||||
private bool _isRefreshing;
|
private bool _isRefreshing;
|
||||||
private bool _autoRefreshEnabled = true;
|
private bool _autoRefreshEnabled = true;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopWeather;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public WeatherWidget()
|
public WeatherWidget()
|
||||||
{
|
{
|
||||||
@@ -167,6 +170,21 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopWeather
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||||
{
|
{
|
||||||
_ = isEditMode;
|
_ = isEditMode;
|
||||||
@@ -1063,7 +1081,7 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,20 +4,23 @@ using System.Linq;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class WeatherWidgetSettingsWindow : UserControl
|
public partial class WeatherWidgetSettingsWindow : UserControl, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyList<int> SupportedIntervals = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
private static readonly IReadOnlyList<int> SupportedIntervals = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private bool _suppressEvents;
|
private bool _suppressEvents;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopWeather;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public event EventHandler? SettingsChanged;
|
public event EventHandler? SettingsChanged;
|
||||||
|
|
||||||
@@ -29,10 +32,27 @@ public partial class WeatherWidgetSettingsWindow : UserControl
|
|||||||
ApplyLocalization();
|
ApplyLocalization();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopWeather
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadState()
|
private void LoadState()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
var enabled = componentSnapshot.WeatherAutoRefreshEnabled;
|
var enabled = componentSnapshot.WeatherAutoRefreshEnabled;
|
||||||
@@ -83,10 +103,10 @@ public partial class WeatherWidgetSettingsWindow : UserControl
|
|||||||
|
|
||||||
private void SaveState()
|
private void SaveState()
|
||||||
{
|
{
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
snapshot.WeatherAutoRefreshEnabled = AutoRefreshCheckBox.IsChecked == true;
|
snapshot.WeatherAutoRefreshEnabled = AutoRefreshCheckBox.IsChecked == true;
|
||||||
snapshot.WeatherAutoRefreshIntervalMinutes = GetSelectedInterval();
|
snapshot.WeatherAutoRefreshIntervalMinutes = GetSelectedInterval();
|
||||||
_componentSettingsService.Save(snapshot);
|
_componentSettingsStore.SaveForComponent(_componentId, _placementId, snapshot);
|
||||||
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ using Avalonia.Layout;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget
|
public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private const int BaseWidthCells = 4;
|
private const int BaseWidthCells = 4;
|
||||||
private const int BaseHeightCells = 2;
|
private const int BaseHeightCells = 2;
|
||||||
@@ -92,7 +93,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly ClockEntryVisual[] _entryVisuals = new ClockEntryVisual[WorldClockTimeZoneCatalog.ClockCount];
|
private readonly ClockEntryVisual[] _entryVisuals = new ClockEntryVisual[WorldClockTimeZoneCatalog.ClockCount];
|
||||||
private readonly TimeZoneInfo[] _entryTimeZones = new TimeZoneInfo[WorldClockTimeZoneCatalog.ClockCount];
|
private readonly TimeZoneInfo[] _entryTimeZones = new TimeZoneInfo[WorldClockTimeZoneCatalog.ClockCount];
|
||||||
@@ -103,6 +104,8 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
|
|||||||
private DateTime _nextLanguageProbeUtc = DateTime.MinValue;
|
private DateTime _nextLanguageProbeUtc = DateTime.MinValue;
|
||||||
private string _secondHandMode = ClockSecondHandMode.Tick;
|
private string _secondHandMode = ClockSecondHandMode.Tick;
|
||||||
private bool _isNightVisual = true;
|
private bool _isNightVisual = true;
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopWorldClock;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
|
|
||||||
public WorldClockWidget()
|
public WorldClockWidget()
|
||||||
{
|
{
|
||||||
@@ -147,6 +150,21 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
|
|||||||
UpdateClockVisuals();
|
UpdateClockVisuals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopWorldClock
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
RefreshFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyCellSize(double cellSize)
|
public void ApplyCellSize(double cellSize)
|
||||||
{
|
{
|
||||||
_currentCellSize = Math.Max(1, cellSize);
|
_currentCellSize = Math.Max(1, cellSize);
|
||||||
@@ -525,7 +543,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
|
|||||||
private void LoadFromSettings()
|
private void LoadFromSettings()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
var ids = WorldClockTimeZoneCatalog.NormalizeTimeZoneIds(componentSnapshot.WorldClockTimeZoneIds);
|
var ids = WorldClockTimeZoneCatalog.NormalizeTimeZoneIds(componentSnapshot.WorldClockTimeZoneIds);
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ using System.Linq;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.Components;
|
namespace LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
public partial class WorldClockWidgetSettingsWindow : UserControl
|
public partial class WorldClockWidgetSettingsWindow : UserControl, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||||
{
|
{
|
||||||
private static readonly IReadOnlyDictionary<string, string> ZhTimeZoneNames =
|
private static readonly IReadOnlyDictionary<string, string> ZhTimeZoneNames =
|
||||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
@@ -28,12 +29,14 @@ public partial class WorldClockWidgetSettingsWindow : UserControl
|
|||||||
};
|
};
|
||||||
|
|
||||||
private readonly AppSettingsService _appSettingsService = new();
|
private readonly AppSettingsService _appSettingsService = new();
|
||||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||||
private readonly LocalizationService _localizationService = new();
|
private readonly LocalizationService _localizationService = new();
|
||||||
private readonly TimeZoneService _timeZoneService = new();
|
private readonly TimeZoneService _timeZoneService = new();
|
||||||
private readonly ComboBox[] _timeZoneComboBoxes;
|
private readonly ComboBox[] _timeZoneComboBoxes;
|
||||||
private bool _suppressEvents;
|
private bool _suppressEvents;
|
||||||
private string _languageCode = "zh-CN";
|
private string _languageCode = "zh-CN";
|
||||||
|
private string _componentId = BuiltInComponentIds.DesktopWorldClock;
|
||||||
|
private string _placementId = string.Empty;
|
||||||
private IReadOnlyList<TimeZoneInfo> _allTimeZones = Array.Empty<TimeZoneInfo>();
|
private IReadOnlyList<TimeZoneInfo> _allTimeZones = Array.Empty<TimeZoneInfo>();
|
||||||
private IReadOnlyList<string> _selectedTimeZoneIds = Array.Empty<string>();
|
private IReadOnlyList<string> _selectedTimeZoneIds = Array.Empty<string>();
|
||||||
private string _secondHandMode = ClockSecondHandMode.Tick;
|
private string _secondHandMode = ClockSecondHandMode.Tick;
|
||||||
@@ -57,10 +60,29 @@ public partial class WorldClockWidgetSettingsWindow : UserControl
|
|||||||
PopulateTimeZoneComboBoxes();
|
PopulateTimeZoneComboBoxes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetComponentPlacementContext(string componentId, string? placementId)
|
||||||
|
{
|
||||||
|
_componentId = string.IsNullOrWhiteSpace(componentId)
|
||||||
|
? BuiltInComponentIds.DesktopWorldClock
|
||||||
|
: componentId.Trim();
|
||||||
|
_placementId = placementId?.Trim() ?? string.Empty;
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
PopulateTimeZoneComboBoxes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||||
|
{
|
||||||
|
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||||
|
LoadState();
|
||||||
|
ApplyLocalization();
|
||||||
|
PopulateTimeZoneComboBoxes();
|
||||||
|
}
|
||||||
|
|
||||||
private void LoadState()
|
private void LoadState()
|
||||||
{
|
{
|
||||||
var appSnapshot = _appSettingsService.Load();
|
var appSnapshot = _appSettingsService.Load();
|
||||||
var componentSnapshot = _componentSettingsService.Load();
|
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||||
|
|
||||||
_allTimeZones = _timeZoneService
|
_allTimeZones = _timeZoneService
|
||||||
@@ -167,10 +189,10 @@ public partial class WorldClockWidgetSettingsWindow : UserControl
|
|||||||
var normalizedIds = WorldClockTimeZoneCatalog.NormalizeTimeZoneIds(selectedIds, _allTimeZones);
|
var normalizedIds = WorldClockTimeZoneCatalog.NormalizeTimeZoneIds(selectedIds, _allTimeZones);
|
||||||
_secondHandMode = GetSelectedSecondHandMode();
|
_secondHandMode = GetSelectedSecondHandMode();
|
||||||
|
|
||||||
var snapshot = _componentSettingsService.Load();
|
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||||
snapshot.WorldClockTimeZoneIds = normalizedIds.ToList();
|
snapshot.WorldClockTimeZoneIds = normalizedIds.ToList();
|
||||||
snapshot.WorldClockSecondHandMode = _secondHandMode;
|
snapshot.WorldClockSecondHandMode = _secondHandMode;
|
||||||
_componentSettingsService.Save(snapshot);
|
_componentSettingsStore.SaveForComponent(_componentId, _placementId, snapshot);
|
||||||
|
|
||||||
_selectedTimeZoneIds = normalizedIds;
|
_selectedTimeZoneIds = normalizedIds;
|
||||||
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
SettingsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using FluentIcons.Avalonia;
|
|||||||
using FluentIcons.Common;
|
using FluentIcons.Common;
|
||||||
using LanMountainDesktop.ComponentSystem;
|
using LanMountainDesktop.ComponentSystem;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Theme;
|
using LanMountainDesktop.Theme;
|
||||||
using LanMountainDesktop.Views.Components;
|
using LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
@@ -689,6 +690,7 @@ public partial class MainWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
_desktopComponentPlacements.Remove(placement);
|
_desktopComponentPlacements.Remove(placement);
|
||||||
|
_componentSettingsService.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||||
|
|
||||||
ClearDesktopComponentSelection();
|
ClearDesktopComponentSelection();
|
||||||
|
|
||||||
@@ -724,80 +726,80 @@ public partial class MainWindow
|
|||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.Date)
|
if (placement.ComponentId == BuiltInComponentIds.Date)
|
||||||
{
|
{
|
||||||
OpenDateComponentSettings();
|
OpenDateComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopClock)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopClock)
|
||||||
{
|
{
|
||||||
OpenDesktopClockComponentSettings();
|
OpenDesktopClockComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopClassSchedule)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopClassSchedule)
|
||||||
{
|
{
|
||||||
OpenClassScheduleComponentSettings();
|
OpenClassScheduleComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopWorldClock)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopWorldClock)
|
||||||
{
|
{
|
||||||
OpenWorldClockComponentSettings();
|
OpenWorldClockComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsWeatherComponentId(placement.ComponentId))
|
if (IsWeatherComponentId(placement.ComponentId))
|
||||||
{
|
{
|
||||||
OpenWeatherComponentSettings();
|
OpenWeatherComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopDailyArtwork)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopDailyArtwork)
|
||||||
{
|
{
|
||||||
OpenDailyArtworkComponentSettings();
|
OpenDailyArtworkComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopCnrDailyNews)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopCnrDailyNews)
|
||||||
{
|
{
|
||||||
OpenCnrDailyNewsComponentSettings();
|
OpenCnrDailyNewsComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopIfengNews)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopIfengNews)
|
||||||
{
|
{
|
||||||
OpenIfengNewsComponentSettings();
|
OpenIfengNewsComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopDailyWord ||
|
if (placement.ComponentId == BuiltInComponentIds.DesktopDailyWord ||
|
||||||
placement.ComponentId == BuiltInComponentIds.DesktopDailyWord2x2)
|
placement.ComponentId == BuiltInComponentIds.DesktopDailyWord2x2)
|
||||||
{
|
{
|
||||||
OpenDailyWordComponentSettings();
|
OpenDailyWordComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopBilibiliHotSearch)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopBilibiliHotSearch)
|
||||||
{
|
{
|
||||||
OpenBilibiliHotSearchComponentSettings();
|
OpenBilibiliHotSearchComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopBaiduHotSearch)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopBaiduHotSearch)
|
||||||
{
|
{
|
||||||
OpenBaiduHotSearchComponentSettings();
|
OpenBaiduHotSearchComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopStcn24Forum)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopStcn24Forum)
|
||||||
{
|
{
|
||||||
OpenStcn24ForumComponentSettings();
|
OpenStcn24ForumComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (placement.ComponentId == BuiltInComponentIds.DesktopStudyEnvironment)
|
if (placement.ComponentId == BuiltInComponentIds.DesktopStudyEnvironment)
|
||||||
{
|
{
|
||||||
OpenStudyEnvironmentComponentSettings();
|
OpenStudyEnvironmentComponentSettings(placement);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -811,212 +813,129 @@ public partial class MainWindow
|
|||||||
string.Equals(componentId, BuiltInComponentIds.DesktopExtendedWeather, StringComparison.OrdinalIgnoreCase);
|
string.Equals(componentId, BuiltInComponentIds.DesktopExtendedWeather, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenDateComponentSettings()
|
private void ShowComponentSettings(Control settingsContent, DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingsContent = new DateWidgetSettingsWindow();
|
var runtimeContext = new DesktopComponentRuntimeContext(
|
||||||
|
placement.ComponentId,
|
||||||
|
placement.PlacementId,
|
||||||
|
_componentSettingsService);
|
||||||
|
|
||||||
|
if (settingsContent is IComponentRuntimeContextAware runtimeContextAwareComponent)
|
||||||
|
{
|
||||||
|
runtimeContextAwareComponent.SetComponentRuntimeContext(runtimeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsContent is IComponentPlacementContextAware placementAwareComponent)
|
||||||
|
{
|
||||||
|
placementAwareComponent.SetComponentPlacementContext(placement.ComponentId, placement.PlacementId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsContent is IComponentSettingsStoreAware settingsStoreAwareComponent)
|
||||||
|
{
|
||||||
|
settingsStoreAwareComponent.SetComponentSettingsStore(_componentSettingsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentSettingsService.ApplyScopedContextToTarget(settingsContent, placement.ComponentId, placement.PlacementId);
|
||||||
|
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ComponentSettingsContentHost.Content = settingsContent;
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
ComponentSettingsWindow.IsVisible = true;
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
ComponentSettingsWindow.Opacity = 0;
|
||||||
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
ComponentSettingsWindow.Opacity = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenClassScheduleComponentSettings()
|
private void OpenDateComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
var settingsContent = new DateWidgetSettingsWindow();
|
||||||
{
|
ShowComponentSettings(settingsContent, placement);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private void OpenClassScheduleComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
|
{
|
||||||
var settingsContent = new ClassScheduleSettingsWindow();
|
var settingsContent = new ClassScheduleSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnClassScheduleSettingsChanged;
|
settingsContent.SettingsChanged += OnClassScheduleSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenDesktopClockComponentSettings()
|
private void OpenDesktopClockComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new AnalogClockWidgetSettingsWindow();
|
var settingsContent = new AnalogClockWidgetSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnDesktopClockSettingsChanged;
|
settingsContent.SettingsChanged += OnDesktopClockSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenWorldClockComponentSettings()
|
private void OpenWorldClockComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new WorldClockWidgetSettingsWindow();
|
var settingsContent = new WorldClockWidgetSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnWorldClockSettingsChanged;
|
settingsContent.SettingsChanged += OnWorldClockSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenWeatherComponentSettings()
|
private void OpenWeatherComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new WeatherWidgetSettingsWindow();
|
var settingsContent = new WeatherWidgetSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnWeatherSettingsChanged;
|
settingsContent.SettingsChanged += OnWeatherSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenStudyEnvironmentComponentSettings()
|
private void OpenStudyEnvironmentComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new StudyEnvironmentWidgetSettingsWindow();
|
var settingsContent = new StudyEnvironmentWidgetSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnStudyEnvironmentSettingsChanged;
|
settingsContent.SettingsChanged += OnStudyEnvironmentSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenDailyArtworkComponentSettings()
|
private void OpenDailyArtworkComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new DailyArtworkSettingsWindow();
|
var settingsContent = new DailyArtworkSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnDailyArtworkSettingsChanged;
|
settingsContent.SettingsChanged += OnDailyArtworkSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenCnrDailyNewsComponentSettings()
|
private void OpenCnrDailyNewsComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new CnrDailyNewsSettingsWindow();
|
var settingsContent = new CnrDailyNewsSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnCnrDailyNewsSettingsChanged;
|
settingsContent.SettingsChanged += OnCnrDailyNewsSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenIfengNewsComponentSettings()
|
private void OpenIfengNewsComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new IfengNewsSettingsWindow();
|
var settingsContent = new IfengNewsSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnIfengNewsSettingsChanged;
|
settingsContent.SettingsChanged += OnIfengNewsSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenDailyWordComponentSettings()
|
private void OpenDailyWordComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new DailyWordSettingsWindow();
|
var settingsContent = new DailyWordSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnDailyWordSettingsChanged;
|
settingsContent.SettingsChanged += OnDailyWordSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenBilibiliHotSearchComponentSettings()
|
private void OpenBilibiliHotSearchComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new BilibiliHotSearchSettingsWindow();
|
var settingsContent = new BilibiliHotSearchSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnBilibiliHotSearchSettingsChanged;
|
settingsContent.SettingsChanged += OnBilibiliHotSearchSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenBaiduHotSearchComponentSettings()
|
private void OpenBaiduHotSearchComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new BaiduHotSearchSettingsWindow();
|
var settingsContent = new BaiduHotSearchSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnBaiduHotSearchSettingsChanged;
|
settingsContent.SettingsChanged += OnBaiduHotSearchSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenStcn24ForumComponentSettings()
|
private void OpenStcn24ForumComponentSettings(DesktopComponentPlacementSnapshot placement)
|
||||||
{
|
{
|
||||||
if (ComponentSettingsWindow is null || ComponentSettingsContentHost is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var settingsContent = new Stcn24ForumSettingsWindow();
|
var settingsContent = new Stcn24ForumSettingsWindow();
|
||||||
settingsContent.SettingsChanged += OnStcn24ForumSettingsChanged;
|
settingsContent.SettingsChanged += OnStcn24ForumSettingsChanged;
|
||||||
ComponentSettingsContentHost.Content = settingsContent;
|
ShowComponentSettings(settingsContent, placement);
|
||||||
|
|
||||||
ComponentSettingsWindow.IsVisible = true;
|
|
||||||
ComponentSettingsWindow.Opacity = 0;
|
|
||||||
ComponentSettingsWindow.Opacity = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClassScheduleSettingsChanged(object? sender, EventArgs e)
|
private void OnClassScheduleSettingsChanged(object? sender, EventArgs e)
|
||||||
@@ -1408,6 +1327,7 @@ public partial class MainWindow
|
|||||||
foreach (var placement in placementsToRemove)
|
foreach (var placement in placementsToRemove)
|
||||||
{
|
{
|
||||||
_desktopComponentPlacements.Remove(placement);
|
_desktopComponentPlacements.Remove(placement);
|
||||||
|
_componentSettingsService.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_desktopPageCount = Math.Clamp(_desktopPageCount - 1, MinDesktopPageCount, MaxDesktopPageCount);
|
_desktopPageCount = Math.Clamp(_desktopPageCount - 1, MinDesktopPageCount, MaxDesktopPageCount);
|
||||||
@@ -1601,7 +1521,7 @@ public partial class MainWindow
|
|||||||
placement.PlacementId = Guid.NewGuid().ToString("N");
|
placement.PlacementId = Guid.NewGuid().ToString("N");
|
||||||
}
|
}
|
||||||
|
|
||||||
var component = CreateDesktopComponentControl(placement.ComponentId);
|
var component = CreateDesktopComponentControl(placement.ComponentId, placement.PlacementId);
|
||||||
if (component is null)
|
if (component is null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -2035,7 +1955,7 @@ public partial class MainWindow
|
|||||||
return onLeft || onRight || onTop || onBottom;
|
return onLeft || onRight || onTop || onBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Control? CreateDesktopComponentControl(string componentId)
|
private Control? CreateDesktopComponentControl(string componentId, string? placementId = null)
|
||||||
{
|
{
|
||||||
if (!_componentRuntimeRegistry.TryGetDescriptor(componentId, out var runtimeDescriptor))
|
if (!_componentRuntimeRegistry.TryGetDescriptor(componentId, out var runtimeDescriptor))
|
||||||
{
|
{
|
||||||
@@ -2047,7 +1967,9 @@ public partial class MainWindow
|
|||||||
_timeZoneService,
|
_timeZoneService,
|
||||||
_weatherDataService,
|
_weatherDataService,
|
||||||
_recommendationInfoService,
|
_recommendationInfoService,
|
||||||
_calculatorDataService);
|
_calculatorDataService,
|
||||||
|
_componentSettingsService,
|
||||||
|
placementId);
|
||||||
component.Classes.Add(DesktopComponentClass);
|
component.Classes.Add(DesktopComponentClass);
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
@@ -3195,7 +3117,8 @@ public partial class MainWindow
|
|||||||
_timeZoneService,
|
_timeZoneService,
|
||||||
_weatherDataService,
|
_weatherDataService,
|
||||||
_recommendationInfoService,
|
_recommendationInfoService,
|
||||||
_calculatorDataService);
|
_calculatorDataService,
|
||||||
|
_componentSettingsService);
|
||||||
// Component library previews must stay non-interactive so drag gesture is reliable.
|
// Component library previews must stay non-interactive so drag gesture is reliable.
|
||||||
previewControl.IsHitTestVisible = false;
|
previewControl.IsHitTestVisible = false;
|
||||||
previewControl.Focusable = false;
|
previewControl.Focusable = false;
|
||||||
|
|||||||
@@ -38,8 +38,9 @@
|
|||||||
|
|
||||||
## 当前状态
|
## 当前状态
|
||||||
- 项目包含桌面端与推荐后端两个子项目,并在同一 solution 中维护。
|
- 项目包含桌面端与推荐后端两个子项目,并在同一 solution 中维护。
|
||||||
- 配置默认写入本地:`%LOCALAPPDATA%\LanMountainDesktop\settings.json`。
|
- 通用应用配置默认写入本地:`%LOCALAPPDATA%\LanMountainDesktop\settings.json`。
|
||||||
- 启动台与桌面布局现已拆分到独立文件:`%LOCALAPPDATA%\LanMountainDesktop\launcher-settings.json`、`%LOCALAPPDATA%\LanMountainDesktop\desktop-layout-settings.json`。
|
- 启动台与桌面布局已拆分到独立文件:`%LOCALAPPDATA%\LanMountainDesktop\launcher-settings.json`、`%LOCALAPPDATA%\LanMountainDesktop\desktop-layout-settings.json`。
|
||||||
|
- 组件配置统一写入:`%LOCALAPPDATA%\LanMountainDesktop\component-settings.json`;同类组件按实例 `componentId::placementId` 隔离存储,同时预留插件专属配置区。
|
||||||
- 当前体验以 Windows 为主要目标平台。
|
- 当前体验以 Windows 为主要目标平台。
|
||||||
|
|
||||||
## 运行说明
|
## 运行说明
|
||||||
|
|||||||
Reference in New Issue
Block a user