mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
setting_re3
This commit is contained in:
@@ -9,10 +9,11 @@ using Avalonia.Styling;
|
||||
using Avalonia.Threading;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, string> ZhCityNames =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
@@ -58,8 +59,7 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
||||
private string _componentId = BuiltInComponentIds.DesktopClock;
|
||||
private string _placementId = string.Empty;
|
||||
|
||||
private readonly AppSettingsService _appSettingsService = new();
|
||||
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||
private readonly ISettingsService _settingsService = HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
private TimeZoneService? _timeZoneService;
|
||||
private double _currentCellSize = 48;
|
||||
@@ -124,12 +124,6 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
InitializeDialIfNeeded();
|
||||
@@ -376,8 +370,11 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
|
||||
|
||||
private void LoadClockSettings()
|
||||
{
|
||||
var appSnapshot = _appSettingsService.Load();
|
||||
var componentSnapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||
var appSnapshot = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
var componentSnapshot = _settingsService.LoadSnapshot<ComponentSettingsSnapshot>(
|
||||
SettingsScope.ComponentInstance,
|
||||
_componentId,
|
||||
_placementId);
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
|
||||
|
||||
var configuredTimeZoneId = string.IsNullOrWhiteSpace(componentSnapshot.DesktopClockTimeZoneId)
|
||||
|
||||
@@ -10,10 +10,11 @@ using Avalonia.Threading;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private sealed record CourseItemViewModel(
|
||||
string Name,
|
||||
@@ -26,8 +27,7 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
||||
Interval = TimeSpan.FromMinutes(4)
|
||||
};
|
||||
|
||||
private readonly AppSettingsService _appSettingsService = new();
|
||||
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||
private readonly ISettingsService _settingsService = HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
private readonly IClassIslandScheduleDataService _scheduleService = new ClassIslandScheduleDataService();
|
||||
|
||||
@@ -125,16 +125,13 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
|
||||
RefreshSchedule();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshSchedule();
|
||||
}
|
||||
|
||||
private void RefreshSchedule()
|
||||
{
|
||||
var appSettings = _appSettingsService.Load();
|
||||
var componentSettings = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||
var appSettings = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
var componentSettings = _settingsService.LoadSnapshot<ComponentSettingsSnapshot>(
|
||||
SettingsScope.ComponentInstance,
|
||||
_componentId,
|
||||
_placementId);
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(appSettings.LanguageCode);
|
||||
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
|
||||
UpdateHeader(now);
|
||||
|
||||
@@ -16,10 +16,11 @@ using Avalonia.Threading;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget, IRecommendationInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget, IRecommendationInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<DayOfWeek, string> ZhWeekdays =
|
||||
new Dictionary<DayOfWeek, string>
|
||||
@@ -58,8 +59,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
||||
Interval = TimeSpan.FromHours(6)
|
||||
};
|
||||
|
||||
private readonly AppSettingsService _settingsService = new();
|
||||
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||
private readonly ISettingsService _settingsService = HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
|
||||
private IRecommendationInfoService _recommendationService = DefaultRecommendationService;
|
||||
@@ -149,12 +149,6 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_isAttached = true;
|
||||
@@ -651,7 +645,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
||||
{
|
||||
try
|
||||
{
|
||||
var snapshot = _settingsService.Load();
|
||||
var snapshot = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
|
||||
}
|
||||
catch
|
||||
@@ -664,7 +658,10 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
|
||||
{
|
||||
try
|
||||
{
|
||||
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||
var snapshot = _settingsService.LoadSnapshot<ComponentSettingsSnapshot>(
|
||||
SettingsScope.ComponentInstance,
|
||||
_componentId,
|
||||
_placementId);
|
||||
return DailyArtworkMirrorSources.Normalize(snapshot.DailyArtworkMirrorSource);
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -3,7 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
@@ -14,7 +16,8 @@ public sealed record DesktopComponentControlFactoryContext(
|
||||
IWeatherInfoService WeatherInfoService,
|
||||
IRecommendationInfoService RecommendationInfoService,
|
||||
ICalculatorDataService CalculatorDataService,
|
||||
IComponentInstanceSettingsStore ComponentSettingsStore,
|
||||
ISettingsService SettingsService,
|
||||
IComponentSettingsAccessor ComponentSettingsAccessor,
|
||||
string? PlacementId = null);
|
||||
|
||||
public sealed class DesktopComponentRuntimeRegistration
|
||||
@@ -84,9 +87,10 @@ public sealed class DesktopComponentRuntimeDescriptor
|
||||
IWeatherInfoService weatherInfoService,
|
||||
IRecommendationInfoService recommendationInfoService,
|
||||
ICalculatorDataService calculatorDataService,
|
||||
IComponentInstanceSettingsStore componentSettingsStore,
|
||||
string? placementId = null)
|
||||
{
|
||||
var settingsService = HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||
var componentAccessor = settingsService.GetComponentAccessor(Definition.Id, placementId);
|
||||
var control = _controlFactory(new DesktopComponentControlFactoryContext(
|
||||
Definition,
|
||||
cellSize,
|
||||
@@ -94,12 +98,14 @@ public sealed class DesktopComponentRuntimeDescriptor
|
||||
weatherInfoService,
|
||||
recommendationInfoService,
|
||||
calculatorDataService,
|
||||
componentSettingsStore,
|
||||
settingsService,
|
||||
componentAccessor,
|
||||
placementId));
|
||||
var runtimeContext = new DesktopComponentRuntimeContext(
|
||||
Definition.Id,
|
||||
placementId,
|
||||
componentSettingsStore);
|
||||
settingsService,
|
||||
componentAccessor);
|
||||
|
||||
if (control is IComponentRuntimeContextAware runtimeContextAwareComponent)
|
||||
{
|
||||
@@ -111,13 +117,6 @@ public sealed class DesktopComponentRuntimeDescriptor
|
||||
placementAwareComponent.SetComponentPlacementContext(Definition.Id, placementId);
|
||||
}
|
||||
|
||||
if (control is IComponentSettingsStoreAware settingsStoreAwareComponent)
|
||||
{
|
||||
settingsStoreAwareComponent.SetComponentSettingsStore(componentSettingsStore);
|
||||
}
|
||||
|
||||
ComponentSettingsService.ApplyScopedContextToTarget(control, Definition.Id, placementId);
|
||||
|
||||
if (control is IDesktopComponentWidget sizedComponent)
|
||||
{
|
||||
sizedComponent.ApplyCellSize(cellSize);
|
||||
|
||||
@@ -12,11 +12,12 @@ using Avalonia.Threading;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
using LanMountainDesktop.Theme;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private static readonly IWeatherInfoService DefaultWeatherInfoService = new XiaomiWeatherService();
|
||||
private static readonly IReadOnlyList<int> SupportedAutoRefreshIntervalsMinutes = RefreshIntervalCatalog.SupportedIntervalsMinutes;
|
||||
@@ -25,8 +26,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
private readonly DispatcherTimer _animationTimer = new() { Interval = FluttermotionToken.WeatherAnimationFrameInterval };
|
||||
private readonly ScaleTransform _backgroundMotionScaleTransform = new(1, 1);
|
||||
private readonly TranslateTransform _backgroundMotionTranslateTransform = new();
|
||||
private readonly AppSettingsService _settingsService = new();
|
||||
private IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||
private readonly ISettingsService _settingsService = HostSettingsFacadeProvider.GetOrCreate().Settings;
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
|
||||
private IWeatherInfoService _weatherInfoService = DefaultWeatherInfoService;
|
||||
@@ -185,12 +185,6 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
{
|
||||
_ = isEditMode;
|
||||
@@ -265,7 +259,7 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
}
|
||||
|
||||
_isRefreshing = true;
|
||||
var app = _settingsService.Load();
|
||||
var app = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(app.LanguageCode);
|
||||
var locale = string.Equals(_languageCode, "zh-CN", StringComparison.OrdinalIgnoreCase) ? "zh_cn" : "en_us";
|
||||
var latitude = double.IsFinite(app.WeatherLatitude) ? Math.Clamp(app.WeatherLatitude, -90, 90) : 39.9042;
|
||||
@@ -953,7 +947,10 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
|
||||
try
|
||||
{
|
||||
var snapshot = _componentSettingsStore.LoadForComponent(_componentId, _placementId);
|
||||
var snapshot = _settingsService.LoadSnapshot<ComponentSettingsSnapshot>(
|
||||
SettingsScope.ComponentInstance,
|
||||
_componentId,
|
||||
_placementId);
|
||||
enabled = snapshot.WeatherAutoRefreshEnabled;
|
||||
intervalMinutes = NormalizeAutoRefreshIntervalMinutes(snapshot.WeatherAutoRefreshIntervalMinutes);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ using LanMountainDesktop.Theme;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private enum WeatherVisualKind
|
||||
{
|
||||
@@ -236,12 +236,6 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
{
|
||||
_ = isEditMode;
|
||||
|
||||
@@ -16,7 +16,7 @@ using LanMountainDesktop.Theme;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private enum WeatherVisualKind
|
||||
{
|
||||
@@ -234,12 +234,6 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
{
|
||||
_ = isEditMode;
|
||||
|
||||
@@ -16,7 +16,7 @@ using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private sealed record WeatherClockConfig(
|
||||
string LanguageCode,
|
||||
@@ -128,12 +128,6 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void ApplyCellSize(double cellSize)
|
||||
{
|
||||
_currentCellSize = Math.Max(1, cellSize);
|
||||
|
||||
@@ -18,7 +18,7 @@ using LanMountainDesktop.Theme;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget, ITimeZoneAwareComponentWidget, IWeatherInfoAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private enum WeatherVisualKind
|
||||
{
|
||||
@@ -179,12 +179,6 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
{
|
||||
_ = isEditMode;
|
||||
|
||||
@@ -13,7 +13,7 @@ using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware, IComponentSettingsStoreAware
|
||||
public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget, IComponentPlacementContextAware
|
||||
{
|
||||
private const int BaseWidthCells = 4;
|
||||
private const int BaseHeightCells = 2;
|
||||
@@ -159,12 +159,6 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void SetComponentSettingsStore(IComponentInstanceSettingsStore settingsStore)
|
||||
{
|
||||
_componentSettingsStore = settingsStore ?? new ComponentSettingsService();
|
||||
RefreshFromSettings();
|
||||
}
|
||||
|
||||
public void ApplyCellSize(double cellSize)
|
||||
{
|
||||
_currentCellSize = Math.Max(1, cellSize);
|
||||
|
||||
@@ -101,19 +101,12 @@ public partial class MainWindow
|
||||
|
||||
private void OnOpenComponentLibraryClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
// "Desktop edit" toggle. While editing, show the component library window.
|
||||
if (_isComponentLibraryOpen)
|
||||
{
|
||||
CloseComponentLibraryWindow(reopenSettings: false);
|
||||
return;
|
||||
}
|
||||
|
||||
OpenComponentLibraryWindow();
|
||||
_componentLibraryWindowService.Toggle(this);
|
||||
}
|
||||
|
||||
private void OnCloseComponentLibraryClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
CloseComponentLibraryWindow(reopenSettings: false);
|
||||
_componentLibraryWindowService.Close(this);
|
||||
}
|
||||
|
||||
private void OnCloseComponentSettingsClick(object? sender, RoutedEventArgs e)
|
||||
@@ -202,21 +195,6 @@ public partial class MainWindow
|
||||
{
|
||||
ClockWidget.SetDisplayFormat(_clockDisplayFormat);
|
||||
}
|
||||
|
||||
if (_clockDisplayFormat == ClockDisplayFormat.HourMinute)
|
||||
{
|
||||
if (ClockFormatHMRadio is not null)
|
||||
{
|
||||
ClockFormatHMRadio.IsChecked = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ClockFormatHMSSRadio is not null)
|
||||
{
|
||||
ClockFormatHMSSRadio.IsChecked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyTopStatusComponentVisibility()
|
||||
@@ -233,15 +211,6 @@ public partial class MainWindow
|
||||
Grid.SetColumnSpan(ClockWidget, columnSpan);
|
||||
}
|
||||
}
|
||||
|
||||
if (WallpaperPreviewClockWidget is not null)
|
||||
{
|
||||
WallpaperPreviewClockWidget.IsVisible = showClock;
|
||||
if (showClock)
|
||||
{
|
||||
WallpaperPreviewClockWidget.SetDisplayFormat(_clockDisplayFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TaskbarContext GetCurrentTaskbarContext()
|
||||
@@ -251,11 +220,7 @@ public partial class MainWindow
|
||||
|
||||
private void ApplyTaskbarActionVisibility(TaskbarContext context)
|
||||
{
|
||||
if (BackToWindowsButton is null ||
|
||||
OpenComponentLibraryButton is null ||
|
||||
WallpaperPreviewBackButtonVisual is null ||
|
||||
WallpaperPreviewComponentLibraryVisual is null ||
|
||||
WallpaperPreviewSettingsButtonIcon is null)
|
||||
if (BackToWindowsButton is null || OpenComponentLibraryButton is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -266,9 +231,6 @@ public partial class MainWindow
|
||||
|
||||
BackToWindowsButton.IsVisible = showMinimize;
|
||||
OpenComponentLibraryButton.IsVisible = showDesktopEdit;
|
||||
WallpaperPreviewBackButtonVisual.IsVisible = showMinimize;
|
||||
WallpaperPreviewComponentLibraryVisual.IsVisible = showDesktopEdit;
|
||||
WallpaperPreviewSettingsButtonIcon.IsVisible = showSettings;
|
||||
|
||||
if (TaskbarFixedActionsHost is not null)
|
||||
{
|
||||
@@ -280,16 +242,6 @@ public partial class MainWindow
|
||||
TaskbarSettingsActionHost.IsVisible = showSettings || showDesktopEdit;
|
||||
}
|
||||
|
||||
if (WallpaperPreviewTaskbarFixedActionsHost is not null)
|
||||
{
|
||||
WallpaperPreviewTaskbarFixedActionsHost.IsVisible = showMinimize;
|
||||
}
|
||||
|
||||
if (WallpaperPreviewTaskbarSettingsActionHost is not null)
|
||||
{
|
||||
WallpaperPreviewTaskbarSettingsActionHost.IsVisible = showSettings || showDesktopEdit;
|
||||
}
|
||||
|
||||
var dynamicActions = ResolveDynamicTaskbarActions(context)
|
||||
.Where(action => action.IsVisible)
|
||||
.ToList();
|
||||
@@ -300,11 +252,6 @@ public partial class MainWindow
|
||||
{
|
||||
TaskbarDynamicActionsHost.IsVisible = hasDynamicActions;
|
||||
}
|
||||
|
||||
if (WallpaperPreviewTaskbarDynamicActionsHost is not null)
|
||||
{
|
||||
WallpaperPreviewTaskbarDynamicActionsHost.IsVisible = hasDynamicActions;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOpenSettingsActionVisualState()
|
||||
@@ -455,14 +402,7 @@ public partial class MainWindow
|
||||
TaskbarDynamicActionsPanel.Children.Clear();
|
||||
}
|
||||
|
||||
if (WallpaperPreviewTaskbarDynamicActionsHost is not null)
|
||||
{
|
||||
WallpaperPreviewTaskbarDynamicActionsHost.Children.Clear();
|
||||
}
|
||||
|
||||
if (actions.Count == 0 ||
|
||||
TaskbarDynamicActionsPanel is null ||
|
||||
WallpaperPreviewTaskbarDynamicActionsHost is null)
|
||||
if (actions.Count == 0 || TaskbarDynamicActionsPanel is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -556,37 +496,6 @@ public partial class MainWindow
|
||||
|
||||
TaskbarDynamicActionsPanel.Children.Add(button);
|
||||
|
||||
Control previewIcon = new SymbolIcon
|
||||
{
|
||||
Symbol = iconSymbol,
|
||||
IconVariant = IconVariant.Regular,
|
||||
FontSize = iconSize * 0.85
|
||||
};
|
||||
|
||||
var previewText = new TextBlock
|
||||
{
|
||||
Text = action.Title,
|
||||
FontSize = fontSize * 0.85,
|
||||
Foreground = (isDeleteAction || isHideAction)
|
||||
? new SolidColorBrush(Color.Parse("#FFFF6B6B"))
|
||||
: Foreground,
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||
};
|
||||
|
||||
var previewContent = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = spacing * 0.5,
|
||||
Children = { previewIcon, previewText }
|
||||
};
|
||||
|
||||
var previewBorder = new Border
|
||||
{
|
||||
Background = Brushes.Transparent,
|
||||
BorderThickness = new Thickness(0),
|
||||
Child = previewContent
|
||||
};
|
||||
WallpaperPreviewTaskbarDynamicActionsHost.Children.Add(previewBorder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,7 +546,7 @@ public partial class MainWindow
|
||||
}
|
||||
|
||||
_desktopComponentPlacements.Remove(placement);
|
||||
_componentSettingsService.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||
_componentSettingsStore.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||
|
||||
ClearDesktopComponentSelection();
|
||||
|
||||
@@ -698,7 +607,7 @@ public partial class MainWindow
|
||||
foreach (var placement in placementsToRemove)
|
||||
{
|
||||
_desktopComponentPlacements.Remove(placement);
|
||||
_componentSettingsService.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||
_componentSettingsStore.DeleteForComponent(placement.ComponentId, placement.PlacementId);
|
||||
}
|
||||
|
||||
_desktopPageCount = Math.Clamp(_desktopPageCount - 1, MinDesktopPageCount, MaxDesktopPageCount);
|
||||
@@ -1345,14 +1254,19 @@ public partial class MainWindow
|
||||
{
|
||||
try
|
||||
{
|
||||
var component = runtimeDescriptor.CreateControl(
|
||||
var createContext = new ComponentLibraryCreateContext(
|
||||
cellSize,
|
||||
_timeZoneService,
|
||||
_weatherDataService,
|
||||
_recommendationInfoService,
|
||||
_calculatorDataService,
|
||||
_componentSettingsService,
|
||||
placementId);
|
||||
if (!_componentLibraryService.TryCreateControl(runtimeDescriptor.Definition.Id, createContext, out var component, out var exception) ||
|
||||
component is null)
|
||||
{
|
||||
throw exception ?? new InvalidOperationException("Component library service returned no control.");
|
||||
}
|
||||
|
||||
component.Classes.Add(DesktopComponentClass);
|
||||
return component;
|
||||
}
|
||||
@@ -1376,6 +1290,18 @@ public partial class MainWindow
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsComponentLibraryOpenFromService => _isComponentLibraryOpen;
|
||||
|
||||
internal void OpenComponentLibraryWindowFromService()
|
||||
{
|
||||
OpenComponentLibraryWindow();
|
||||
}
|
||||
|
||||
internal void CloseComponentLibraryWindowFromService()
|
||||
{
|
||||
CloseComponentLibraryWindow(reopenSettings: false);
|
||||
}
|
||||
|
||||
private void CollapseComponentLibraryPanel()
|
||||
{
|
||||
// Animate component library panel collapsing downward
|
||||
|
||||
@@ -1,456 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using FluentIcons.Avalonia;
|
||||
using FluentIcons.Common;
|
||||
using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views;
|
||||
|
||||
public partial class MainWindow
|
||||
{
|
||||
private const string AppCodeName = "Administrate";
|
||||
private const string AppFontName = "MiSans";
|
||||
private const string FallbackAppVersion = "1.0.0";
|
||||
|
||||
private static readonly IReadOnlyDictionary<string, string> ZhTimeZoneNames =
|
||||
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["China Standard Time"] = "中国标准时间",
|
||||
["Asia/Shanghai"] = "中国标准时间",
|
||||
["Tokyo Standard Time"] = "日本标准时间",
|
||||
["Asia/Tokyo"] = "日本标准时间",
|
||||
["Pacific Standard Time"] = "太平洋标准时间",
|
||||
["America/Los_Angeles"] = "太平洋标准时间",
|
||||
["Eastern Standard Time"] = "美国东部标准时间",
|
||||
["America/New_York"] = "美国东部标准时间",
|
||||
["Central European Standard Time"] = "中欧标准时间",
|
||||
["Europe/Berlin"] = "中欧标准时间",
|
||||
["GMT Standard Time"] = "格林威治标准时间",
|
||||
["Europe/London"] = "格林威治标准时间",
|
||||
["UTC"] = "协调世界时",
|
||||
["Etc/UTC"] = "协调世界时"
|
||||
};
|
||||
|
||||
private void InitializeLocalization(string? languageCode)
|
||||
{
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(languageCode);
|
||||
|
||||
if (LanguageComboBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_suppressLanguageSelectionEvents = true;
|
||||
LanguageComboBox.SelectedIndex = string.Equals(_languageCode, "en-US", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||||
_suppressLanguageSelectionEvents = false;
|
||||
}
|
||||
|
||||
private string L(string key, string fallback)
|
||||
{
|
||||
return _localizationService.GetString(_languageCode, key, fallback);
|
||||
}
|
||||
|
||||
private string Lf(string key, string fallback, params object[] args)
|
||||
{
|
||||
var template = L(key, fallback);
|
||||
return string.Format(template, args);
|
||||
}
|
||||
|
||||
private string GetLanguageDisplayName(string languageCode)
|
||||
{
|
||||
return string.Equals(languageCode, "en-US", StringComparison.OrdinalIgnoreCase)
|
||||
? L("settings.region.language_en", "English")
|
||||
: L("settings.region.language_zh", "Chinese");
|
||||
}
|
||||
|
||||
private string GetLocalizedPlacementDisplayName(WallpaperPlacement placement)
|
||||
{
|
||||
return placement switch
|
||||
{
|
||||
WallpaperPlacement.Fill => L("placement.fill", "Fill"),
|
||||
WallpaperPlacement.Fit => L("placement.fit", "Fit"),
|
||||
WallpaperPlacement.Stretch => L("placement.stretch", "Stretch"),
|
||||
WallpaperPlacement.Center => L("placement.center", "Center"),
|
||||
WallpaperPlacement.Tile => L("placement.tile", "Tile"),
|
||||
_ => L("placement.fill", "Fill")
|
||||
};
|
||||
}
|
||||
|
||||
private void ApplyLocalization()
|
||||
{
|
||||
Title = L("app.title", "LanMountainDesktop");
|
||||
|
||||
BackToWindowsTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
||||
WallpaperPreviewBackButtonTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
||||
ToolTip.SetTip(BackToWindowsButton, L("tooltip.back_to_windows", "Back to Windows"));
|
||||
|
||||
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "Edit Desktop"));
|
||||
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Widgets");
|
||||
ToolTip.SetTip(CloseComponentLibraryButton, L("common.close", "Close"));
|
||||
ComponentLibraryEmptyTextBlock.Text = L(
|
||||
"component_library.empty",
|
||||
"Swipe to pick a category, tap to open, then drag a widget onto the desktop.");
|
||||
|
||||
LauncherTitleTextBlock.Text = L("launcher.title", "App Launcher");
|
||||
LauncherSubtitleTextBlock.Text = OperatingSystem.IsLinux()
|
||||
? L(
|
||||
"launcher.subtitle_linux",
|
||||
"Displays installed apps discovered from Linux desktop entries.")
|
||||
: L(
|
||||
"launcher.subtitle",
|
||||
"Displays all apps and folders based on the Windows Start menu structure.");
|
||||
ToolTip.SetTip(LauncherFolderBackButton, L("common.back", "Back"));
|
||||
ToolTip.SetTip(LauncherFolderCloseButton, L("common.close", "Close"));
|
||||
|
||||
// SettingsNavHeaderTextBlock no longer exists
|
||||
SettingsNavWallpaperItem.Content = L("settings.nav.wallpaper", "Wallpaper");
|
||||
SettingsNavGridItem.Content = L("settings.nav.grid", "Grid");
|
||||
SettingsNavColorItem.Content = L("settings.nav.color", "Color");
|
||||
SettingsNavStatusBarItem.Content = L("settings.nav.status_bar", "Status Bar");
|
||||
SettingsNavWeatherItem.Content = L("settings.nav.weather", "Weather");
|
||||
SettingsNavRegionItem.Content = L("settings.nav.region", "Region");
|
||||
SettingsNavUpdateItem.Content = L("settings.nav.update", "Update");
|
||||
SettingsNavLauncherItem.Content = L("settings.nav.launcher", "App Launcher");
|
||||
SettingsNavPluginsItem.Content = L("settings.nav.plugins", "Plugins");
|
||||
SettingsNavPluginMarketItem.Content = L("settings.nav.plugin_market", "Plugin Market");
|
||||
|
||||
WallpaperPanelTitleTextBlock.Text = L("settings.wallpaper.title", "Personalize your wallpaper");
|
||||
WallpaperPlacementSettingsExpander.Header = L("settings.wallpaper.placement_label", "Placement");
|
||||
WallpaperPlacementSettingsExpander.Description = L(
|
||||
"settings.wallpaper.placement_desc",
|
||||
"Adjust how the image fits on the desktop.");
|
||||
PickWallpaperButton.Content = L("settings.wallpaper.pick_button", "Browse");
|
||||
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "Reset");
|
||||
|
||||
GridPanelTitleTextBlock.Text = L("settings.grid.title", "Grid Layout");
|
||||
GridSpacingSettingsExpander.Header = L("settings.grid.spacing_label", "Grid Spacing");
|
||||
GridSpacingRelaxedComboBoxItem.Content = L("settings.grid.spacing_relaxed", "Relaxed");
|
||||
GridSpacingCompactComboBoxItem.Content = L("settings.grid.spacing_compact", "Compact");
|
||||
GridEdgeInsetSettingsExpander.Header = L("settings.grid.edge_inset_label", "Screen Inset");
|
||||
ApplyGridButton.Content = L("settings.grid.apply_button", "Apply");
|
||||
UpdateGridEdgeInsetComputedPxText(_currentDesktopCellSize);
|
||||
|
||||
ColorPanelTitleTextBlock.Text = L("settings.color.title", "Color");
|
||||
ThemeModeSettingsExpander.Header = L("settings.color.day_night_label", "Day/Night");
|
||||
NightModeToggleSwitch.OffContent = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = 6,
|
||||
Children =
|
||||
{
|
||||
new SymbolIcon { Symbol = Symbol.WeatherSunny, IconVariant = IconVariant.Regular, FontSize = 14 },
|
||||
new TextBlock
|
||||
{
|
||||
Text = L("settings.color.day_night_off", "Day"),
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||
}
|
||||
}
|
||||
};
|
||||
NightModeToggleSwitch.OnContent = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = 6,
|
||||
Children =
|
||||
{
|
||||
new SymbolIcon { Symbol = Symbol.WeatherMoon, IconVariant = IconVariant.Regular, FontSize = 14 },
|
||||
new TextBlock
|
||||
{
|
||||
Text = L("settings.color.day_night_on", "Night"),
|
||||
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
|
||||
}
|
||||
}
|
||||
};
|
||||
RecommendedColorsLabelTextBlock.Text = L("settings.color.recommended_label", "Recommended Colors");
|
||||
SystemMonetColorsLabelTextBlock.Text = L("settings.color.system_monet_label", "System Monet Colors");
|
||||
RefreshMonetColorsButton.Content = L("settings.color.refresh_button", "Refresh");
|
||||
|
||||
StatusBarPanelTitleTextBlock.Text = L("settings.status_bar.title", "Status Bar");
|
||||
StatusBarClockSettingsExpander.Header = L("settings.status_bar.clock_header", "Clock");
|
||||
StatusBarSpacingSettingsExpander.Header = L("settings.status_bar.spacing_header", "Component Spacing");
|
||||
StatusBarSpacingSettingsExpander.Description = L("settings.status_bar.spacing_desc", "Adjust spacing between status bar components.");
|
||||
StatusBarSpacingModeCompactItem.Content = L("settings.status_bar.spacing_mode_compact", "Compact");
|
||||
StatusBarSpacingModeRelaxedItem.Content = L("settings.status_bar.spacing_mode_relaxed", "Relaxed");
|
||||
StatusBarSpacingModeCustomItem.Content = L("settings.status_bar.spacing_mode_custom", "Custom");
|
||||
StatusBarSpacingCustomPanel.Content = L("settings.status_bar.spacing_custom_label", "Custom spacing (%)");
|
||||
|
||||
WeatherPanelTitleTextBlock.Text = L("settings.weather.title", "Weather");
|
||||
WeatherPreviewSectionTextBlock.Text = L("settings.weather.preview_section", "Weather Preview");
|
||||
WeatherSettingsSectionTextBlock.Text = L("settings.weather.settings_section", "Settings");
|
||||
WeatherPreviewSettingsExpander.Header = L("settings.weather.preview_panel_header", "Weather Preview");
|
||||
WeatherPreviewSettingsExpander.Description = L(
|
||||
"settings.weather.preview_panel_desc",
|
||||
"Refresh and verify current weather service status.");
|
||||
WeatherPreviewButton.Content = L("settings.weather.refresh_button", "Refresh");
|
||||
|
||||
WeatherLocationSettingsExpander.Header = L("settings.weather.location_source_header", "Location Source");
|
||||
WeatherLocationSettingsExpander.Description = L(
|
||||
"settings.weather.location_source_desc",
|
||||
"Choose how weather widgets resolve location.");
|
||||
WeatherLocationModeCityItem.Content = L("settings.weather.mode_city_search", "City Search");
|
||||
WeatherLocationModeCoordinatesItem.Content = L("settings.weather.mode_coordinates", "Coordinates");
|
||||
WeatherLocationModeCityChipItem.Content = L("settings.weather.mode_city_search", "City Search");
|
||||
WeatherLocationModeCoordinatesChipItem.Content = L("settings.weather.mode_coordinates", "Coordinates");
|
||||
WeatherAutoRefreshToggleSwitch.Content = L("settings.weather.auto_refresh", "Auto refresh location on startup");
|
||||
WeatherLocationSelectionTitleTextBlock.Text = L("settings.weather.city_selection_label", "City Selection");
|
||||
WeatherLocationSelectionDescriptionTextBlock.Text = L(
|
||||
"settings.weather.location_city_summary_desc",
|
||||
"Select the current city used for weather queries.");
|
||||
|
||||
WeatherCitySearchSettingsExpander.Header = L("settings.weather.city_search_header", "City Search");
|
||||
WeatherCitySearchSettingsExpander.Description = L(
|
||||
"settings.weather.city_search_desc",
|
||||
"Search cities and apply one weather location.");
|
||||
WeatherCitySearchTextBox.Watermark = L("settings.weather.search_placeholder", "e.g. Beijing");
|
||||
WeatherSearchButton.Content = L("settings.weather.search_button", "Search");
|
||||
WeatherApplyCityButton.Content = L("settings.weather.apply_city_button", "Apply City");
|
||||
|
||||
WeatherCoordinateSettingsExpander.Header = L("settings.weather.coordinates_header", "Coordinates");
|
||||
WeatherCoordinateSettingsExpander.Description = L(
|
||||
"settings.weather.coordinates_desc",
|
||||
"Set latitude/longitude and optional key/name.");
|
||||
WeatherLatitudeNumberBox.Header = L("settings.weather.latitude_label", "Latitude");
|
||||
WeatherLongitudeNumberBox.Header = L("settings.weather.longitude_label", "Longitude");
|
||||
WeatherLocationKeyTextBox.Watermark = L("settings.weather.location_key_placeholder", "Location key (optional)");
|
||||
WeatherLocationNameTextBox.Watermark = L("settings.weather.location_name_placeholder", "Display name (optional)");
|
||||
WeatherApplyCoordinatesButton.Content = L("settings.weather.apply_coordinates_button", "Apply Coordinates");
|
||||
|
||||
WeatherAlertFilterSettingsExpander.Header = L("settings.weather.alert_filter_header", "Excluded Alerts");
|
||||
WeatherAlertFilterSettingsExpander.Description = L(
|
||||
"settings.weather.alert_filter_desc",
|
||||
"Alerts containing these words will not be shown. One rule per line.");
|
||||
WeatherAlertListTitleTextBlock.Text = L("settings.weather.alert_list_label", "Exclude List");
|
||||
WeatherAlertListDescriptionTextBlock.Text = L("settings.weather.alert_list_desc", "One exclusion rule per line.");
|
||||
WeatherExcludedAlertsTextBox.Watermark = L("settings.weather.alert_filter_placeholder", "One keyword per line");
|
||||
|
||||
WeatherIconPackSettingsExpander.Header = L("settings.weather.icon_style_header", "Weather Icon Style");
|
||||
WeatherIconPackSettingsExpander.Description = L(
|
||||
"settings.weather.icon_style_desc",
|
||||
"Choose Fluent Icon style for weather symbols.");
|
||||
WeatherIconPackFluentRegularItem.Content = L("settings.weather.icon_style_fluent_regular", "Fluent Regular");
|
||||
WeatherIconPackFluentFilledItem.Content = L("settings.weather.icon_style_fluent_filled", "Fluent Filled");
|
||||
|
||||
WeatherNoTlsSettingsExpander.Header = L("settings.weather.no_tls_header", "No TLS Weather Request");
|
||||
WeatherNoTlsSettingsExpander.Description = L(
|
||||
"settings.weather.no_tls_desc",
|
||||
"Not recommended. Enable only for incompatible network environments.");
|
||||
WeatherNoTlsToggleSwitch.Content = L("settings.weather.no_tls_toggle", "Allow non-TLS request fallback");
|
||||
WeatherFooterHintTextBlock.Text = L(
|
||||
"settings.weather.footer_hint",
|
||||
"Desktop weather widgets will reuse the location and alert exclusion settings configured here.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_weatherSearchKeyword))
|
||||
{
|
||||
WeatherSearchStatusTextBlock.Text = L(
|
||||
"settings.weather.search_hint",
|
||||
"Search by city name and apply one location.");
|
||||
}
|
||||
|
||||
if (!_isWeatherPreviewInProgress)
|
||||
{
|
||||
WeatherPreviewResultTextBlock.Text = L(
|
||||
"settings.weather.preview_hint",
|
||||
"Use test fetch to verify your weather configuration.");
|
||||
}
|
||||
|
||||
UpdateWeatherLocationStatusText();
|
||||
|
||||
RegionPanelTitleTextBlock.Text = L("settings.region.title", "Region");
|
||||
LanguageSettingsExpander.Header = L("settings.region.language_header", "Language");
|
||||
LanguageSettingsExpander.Description = L("settings.region.language_desc", "Select application language. Changes apply immediately.");
|
||||
LanguageChineseItem.Content = L("settings.region.language_zh", "Chinese");
|
||||
LanguageEnglishItem.Content = L("settings.region.language_en", "English");
|
||||
TimeZoneSettingsExpander.Header = L("settings.region.timezone_header", "Time Zone");
|
||||
TimeZoneSettingsExpander.Description = L(
|
||||
"settings.region.timezone_desc",
|
||||
"Select a time zone. Clock and calendar widgets will follow this zone.");
|
||||
|
||||
ApplyUpdateLocalization();
|
||||
|
||||
LauncherSettingsPanelTitleTextBlock.Text = L("settings.launcher.title", "App Launcher");
|
||||
LauncherHiddenItemsSettingsExpander.Header = L("settings.launcher.hidden_header", "Hidden Items");
|
||||
LauncherHiddenItemsSettingsExpander.Description = L(
|
||||
"settings.launcher.hidden_desc",
|
||||
"Review hidden launcher entries and show them again.");
|
||||
LauncherHiddenItemsDescriptionTextBlock.Text = L(
|
||||
"settings.launcher.hidden_hint",
|
||||
"Right-click an icon in launcher to hide it. Hidden entries appear here.");
|
||||
LauncherHiddenItemsEmptyTextBlock.Text = L("settings.launcher.hidden_empty", "No hidden items.");
|
||||
|
||||
ApplyPluginSettingsLocalization();
|
||||
ApplyPluginMarketSettingsLocalization();
|
||||
|
||||
SettingsNavAboutItem.Content = L("settings.nav.about", "About");
|
||||
AboutPanelTitleTextBlock.Text = L("settings.about.title", "About");
|
||||
VersionTextBlock.Text = Lf(
|
||||
"settings.about.version_format",
|
||||
"Version: {0}",
|
||||
GetAppVersionText());
|
||||
CodeNameTextBlock.Text = Lf(
|
||||
"settings.about.codename_format",
|
||||
"Code Name: {0}",
|
||||
AppCodeName);
|
||||
FontInfoTextBlock.Text = Lf(
|
||||
"settings.about.font_format",
|
||||
"Font: {0}",
|
||||
AppFontName);
|
||||
AboutStartupSettingsExpander.Header = L("settings.about.startup_header", "Windows Startup");
|
||||
AboutStartupSettingsExpander.Description = L(
|
||||
"settings.about.startup_desc",
|
||||
"Launch the app automatically when signing in to Windows.");
|
||||
AboutRenderModeSettingsExpander.Header = L("settings.about.render_mode_header", "Rendering Mode");
|
||||
AboutRenderModeSettingsExpander.Description = L(
|
||||
"settings.about.render_mode_desc",
|
||||
"Choose the rendering backend. Restart the app after changing this option. Unsupported modes fall back to software.");
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Default, L("settings.about.render_mode.default", "Default"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Software, L("settings.about.render_mode.software", "Software"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.AngleEgl, L("settings.about.render_mode.angle_egl", "angleEgl"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL"));
|
||||
SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan"));
|
||||
UpdateCurrentRenderBackendStatus();
|
||||
UpdatePendingRestartDock();
|
||||
|
||||
if (WallpaperPlacementComboBox?.ItemCount >= 5)
|
||||
{
|
||||
if (WallpaperPlacementComboBox.Items[0] is ComboBoxItem fillItem) fillItem.Content = L("placement.fill", "Fill");
|
||||
if (WallpaperPlacementComboBox.Items[1] is ComboBoxItem fitItem) fitItem.Content = L("placement.fit", "Fit");
|
||||
if (WallpaperPlacementComboBox.Items[2] is ComboBoxItem stretchItem) stretchItem.Content = L("placement.stretch", "Stretch");
|
||||
if (WallpaperPlacementComboBox.Items[3] is ComboBoxItem centerItem) centerItem.Content = L("placement.center", "Center");
|
||||
if (WallpaperPlacementComboBox.Items[4] is ComboBoxItem tileItem) tileItem.Content = L("placement.tile", "Tile");
|
||||
}
|
||||
|
||||
|
||||
GridInfoTextBlock.Text = Lf(
|
||||
"settings.grid.info_format",
|
||||
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||
DesktopGrid.ColumnDefinitions.Count,
|
||||
DesktopGrid.RowDefinitions.Count,
|
||||
DesktopGrid.RowDefinitions.Count > 0 ? DesktopGrid.RowDefinitions[0].Height.Value : 0d);
|
||||
|
||||
InitializeTimeZoneSettings();
|
||||
BuildComponentLibraryCategoryPages();
|
||||
RenderLauncherRootTiles();
|
||||
RenderLauncherHiddenItemsList();
|
||||
UpdateOpenSettingsActionVisualState();
|
||||
UpdateWallpaperDisplay();
|
||||
}
|
||||
|
||||
private void SetAppRenderModeComboItemContent(string tag, string content)
|
||||
{
|
||||
var item = AppRenderModeComboBox.Items
|
||||
.OfType<ComboBoxItem>()
|
||||
.FirstOrDefault(candidate =>
|
||||
string.Equals(candidate.Tag?.ToString(), tag, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
item.Content = content;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLocalizedTimeZoneDisplayName(TimeZoneInfo timeZone)
|
||||
{
|
||||
var offset = timeZone.GetUtcOffset(DateTime.UtcNow);
|
||||
var sign = offset >= TimeSpan.Zero ? "+" : "-";
|
||||
var hours = Math.Abs(offset.Hours);
|
||||
var minutes = Math.Abs(offset.Minutes);
|
||||
var name = string.IsNullOrWhiteSpace(timeZone.StandardName)
|
||||
? timeZone.DisplayName
|
||||
: timeZone.StandardName;
|
||||
|
||||
if (string.Equals(_languageCode, "zh-CN", StringComparison.OrdinalIgnoreCase) &&
|
||||
ZhTimeZoneNames.TryGetValue(timeZone.Id, out var localizedName))
|
||||
{
|
||||
name = localizedName;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
name = timeZone.Id;
|
||||
}
|
||||
|
||||
return $"(UTC{sign}{hours:D2}:{minutes:D2}) {name}";
|
||||
}
|
||||
|
||||
private static string GetAppVersionText()
|
||||
{
|
||||
var version = typeof(MainWindow).Assembly.GetName().Version;
|
||||
if (version is null || version.Major < 0 || version.Minor < 0 || version.Build < 0)
|
||||
{
|
||||
return FallbackAppVersion;
|
||||
}
|
||||
|
||||
return $"{version.Major}.{version.Minor}.{version.Build}";
|
||||
}
|
||||
|
||||
private void OnLanguageSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_suppressLanguageSelectionEvents || LanguageComboBox?.SelectedItem is not ComboBoxItem item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedLanguage = item.Tag as string;
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(selectedLanguage);
|
||||
ApplyLocalization();
|
||||
ThemeColorStatusTextBlock.Text = Lf(
|
||||
"settings.region.applied_format",
|
||||
"Language switched to: {0}",
|
||||
GetLanguageDisplayName(_languageCode));
|
||||
PersistSettings();
|
||||
}
|
||||
|
||||
private void UpdateWeatherLocationStatusText()
|
||||
{
|
||||
if (WeatherLocationStatusTextBlock is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var modeText = _weatherLocationMode == WeatherLocationMode.Coordinates
|
||||
? L("settings.weather.mode_coordinates", "Coordinates")
|
||||
: L("settings.weather.mode_city_search", "City Search");
|
||||
|
||||
if (_weatherLocationMode == WeatherLocationMode.CitySearch)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_weatherLocationKey))
|
||||
{
|
||||
WeatherLocationStatusTextBlock.Text = L(
|
||||
"settings.weather.status_city_empty",
|
||||
"No city location is configured.");
|
||||
UpdateWeatherLocationSummaryCard();
|
||||
return;
|
||||
}
|
||||
|
||||
var locationName = string.IsNullOrWhiteSpace(_weatherLocationName)
|
||||
? _weatherLocationKey
|
||||
: _weatherLocationName;
|
||||
WeatherLocationStatusTextBlock.Text = Lf(
|
||||
"settings.weather.status_city_format",
|
||||
"Mode: {0} | {1} | Key: {2}",
|
||||
modeText,
|
||||
locationName,
|
||||
_weatherLocationKey);
|
||||
UpdateWeatherLocationSummaryCard();
|
||||
return;
|
||||
}
|
||||
|
||||
WeatherLocationStatusTextBlock.Text = Lf(
|
||||
"settings.weather.status_coordinates_format",
|
||||
"Mode: {0} | Lat {1:F4}, Lon {2:F4} | Key: {3}",
|
||||
modeText,
|
||||
_weatherLatitude,
|
||||
_weatherLongitude,
|
||||
string.IsNullOrWhiteSpace(_weatherLocationKey)
|
||||
? BuildCoordinateLocationKey(_weatherLatitude, _weatherLongitude)
|
||||
: _weatherLocationKey);
|
||||
UpdateWeatherLocationSummaryCard();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@ public partial class MainWindow
|
||||
{
|
||||
private void UpdateCurrentRenderBackendStatus()
|
||||
{
|
||||
if (CurrentRenderBackendLabelTextBlock is null ||
|
||||
CurrentRenderBackendValueTextBlock is null ||
|
||||
CurrentRenderBackendImplementationTextBlock is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var backendInfo = AppRenderBackendDiagnostics.Detect();
|
||||
var localizedBackend = GetLocalizedRenderBackendName(backendInfo.ActualBackend);
|
||||
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views;
|
||||
|
||||
public partial class MainWindow
|
||||
{
|
||||
private bool _isRestartPromptVisible;
|
||||
|
||||
private void OnPendingRestartStateChanged()
|
||||
{
|
||||
if (Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
UpdatePendingRestartDock();
|
||||
return;
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.Post(UpdatePendingRestartDock);
|
||||
}
|
||||
|
||||
private void UpdatePendingRestartDock()
|
||||
{
|
||||
PendingRestartDock.IsVisible = PendingRestartStateService.HasPendingRestart;
|
||||
PendingRestartDockTitleTextBlock.Text = L("settings.restart_dock.title", "Restart required");
|
||||
PendingRestartDockDescriptionTextBlock.Text = L(
|
||||
"settings.restart_dock.description",
|
||||
"Some changes will take effect after restarting the app.");
|
||||
PendingRestartDockButtonTextBlock.Text = L("settings.restart_dock.button", "Restart app");
|
||||
}
|
||||
|
||||
private async void OnPendingRestartDockButtonClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
await ShowGenericRestartPromptAsync();
|
||||
}
|
||||
|
||||
private Task ShowRenderModeRestartPromptAsync(string selectedMode)
|
||||
{
|
||||
var message = Lf(
|
||||
"settings.restart_dialog.render_mode_message",
|
||||
"Restart the app to switch the rendering mode from \"{0}\" to \"{1}\". Restart now?",
|
||||
GetLocalizedAppRenderModeDisplayName(_runningAppRenderMode),
|
||||
GetLocalizedAppRenderModeDisplayName(selectedMode));
|
||||
|
||||
return ShowRestartPromptCoreAsync(message);
|
||||
}
|
||||
|
||||
private Task ShowGenericRestartPromptAsync()
|
||||
{
|
||||
return ShowRestartPromptCoreAsync(L(
|
||||
"settings.restart_dock.description",
|
||||
"Some changes will take effect after restarting the app."));
|
||||
}
|
||||
|
||||
private async Task ShowRestartPromptCoreAsync(string message)
|
||||
{
|
||||
if (_isRestartPromptVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isRestartPromptVisible = true;
|
||||
|
||||
try
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = L("settings.restart_dialog.title", "Restart required"),
|
||||
Content = message,
|
||||
PrimaryButtonText = L("settings.restart_dialog.restart", "Restart now"),
|
||||
CloseButtonText = L("settings.restart_dialog.cancel", "Cancel"),
|
||||
DefaultButton = ContentDialogButton.Primary
|
||||
};
|
||||
|
||||
var result = await dialog.ShowAsync(this);
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
if (App.CurrentHostApplicationLifecycle?.TryRestart(new HostApplicationLifecycleRequest(
|
||||
Source: nameof(MainWindow),
|
||||
Reason: "User confirmed a pending restart prompt.")) != true)
|
||||
{
|
||||
UpdatePendingRestartDock();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePendingRestartDock();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isRestartPromptVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetLocalizedAppRenderModeDisplayName(string renderMode)
|
||||
{
|
||||
if (renderMode == AppRenderBackendDiagnostics.Unknown)
|
||||
{
|
||||
return L("settings.about.render_mode.unknown", "Unknown");
|
||||
}
|
||||
|
||||
return AppRenderingModeHelper.Normalize(renderMode) switch
|
||||
{
|
||||
AppRenderingModeHelper.Software => L("settings.about.render_mode.software", "Software"),
|
||||
AppRenderingModeHelper.AngleEgl => L("settings.about.render_mode.angle_egl", "angleEgl"),
|
||||
AppRenderingModeHelper.Wgl => L("settings.about.render_mode.wgl", "WGL"),
|
||||
AppRenderingModeHelper.Vulkan => L("settings.about.render_mode.vulkan", "Vulkan"),
|
||||
_ => L("settings.about.render_mode.default", "Default")
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
404
LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs
Normal file
404
LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Views.Components;
|
||||
|
||||
namespace LanMountainDesktop.Views;
|
||||
|
||||
public partial class MainWindow
|
||||
{
|
||||
private TextBlock? CurrentRenderBackendLabelTextBlock => this.FindControl<TextBlock>("CurrentRenderBackendLabelTextBlock");
|
||||
private TextBlock? CurrentRenderBackendValueTextBlock => this.FindControl<TextBlock>("CurrentRenderBackendValueTextBlock");
|
||||
private TextBlock? CurrentRenderBackendImplementationTextBlock => this.FindControl<TextBlock>("CurrentRenderBackendImplementationTextBlock");
|
||||
private Slider? GridSizeSlider => this.FindControl<Slider>("GridSizeSlider");
|
||||
private NumberBox? GridSizeNumberBox => this.FindControl<NumberBox>("GridSizeNumberBox");
|
||||
private Slider? GridEdgeInsetSlider => this.FindControl<Slider>("GridEdgeInsetSlider");
|
||||
private NumberBox? GridEdgeInsetNumberBox => this.FindControl<NumberBox>("GridEdgeInsetNumberBox");
|
||||
private TextBlock? GridEdgeInsetComputedPxTextBlock => this.FindControl<TextBlock>("GridEdgeInsetComputedPxTextBlock");
|
||||
private TextBlock? GridInfoTextBlock => this.FindControl<TextBlock>("GridInfoTextBlock");
|
||||
private ComboBox? GridSpacingPresetComboBox => this.FindControl<ComboBox>("GridSpacingPresetComboBox");
|
||||
private Border? GridPreviewHost => this.FindControl<Border>("GridPreviewHost");
|
||||
private Border? GridPreviewFrame => this.FindControl<Border>("GridPreviewFrame");
|
||||
private Border? GridPreviewViewport => this.FindControl<Border>("GridPreviewViewport");
|
||||
private Grid? GridPreviewGrid => this.FindControl<Grid>("GridPreviewGrid");
|
||||
private Canvas? GridPreviewLinesCanvas => this.FindControl<Canvas>("GridPreviewLinesCanvas");
|
||||
private Border? GridPreviewTopStatusBarHost => this.FindControl<Border>("GridPreviewTopStatusBarHost");
|
||||
private StackPanel? GridPreviewTopStatusComponentsPanel => this.FindControl<StackPanel>("GridPreviewTopStatusComponentsPanel");
|
||||
private Border? GridPreviewBottomTaskbarContainer => this.FindControl<Border>("GridPreviewBottomTaskbarContainer");
|
||||
private StackPanel? GridPreviewBackButtonVisual => this.FindControl<StackPanel>("GridPreviewBackButtonVisual");
|
||||
private TextBlock? GridPreviewBackButtonTextBlock => this.FindControl<TextBlock>("GridPreviewBackButtonTextBlock");
|
||||
private StackPanel? GridPreviewComponentLibraryVisual => this.FindControl<StackPanel>("GridPreviewComponentLibraryVisual");
|
||||
private FluentIcons.Avalonia.FluentIcon? GridPreviewComponentLibraryIcon => this.FindControl<FluentIcons.Avalonia.FluentIcon>("GridPreviewComponentLibraryIcon");
|
||||
private TextBlock? GridPreviewComponentLibraryTextBlock => this.FindControl<TextBlock>("GridPreviewComponentLibraryTextBlock");
|
||||
private FluentIcons.Avalonia.SymbolIcon? GridPreviewSettingsButtonIcon => this.FindControl<FluentIcons.Avalonia.SymbolIcon>("GridPreviewSettingsButtonIcon");
|
||||
private Border? WallpaperPreviewHost => this.FindControl<Border>("WallpaperPreviewHost");
|
||||
private Border? WallpaperPreviewFrame => this.FindControl<Border>("WallpaperPreviewFrame");
|
||||
private Border? WallpaperPreviewViewport => this.FindControl<Border>("WallpaperPreviewViewport");
|
||||
private Grid? WallpaperPreviewGrid => this.FindControl<Grid>("WallpaperPreviewGrid");
|
||||
private Border? WallpaperPreviewTopStatusBarHost => this.FindControl<Border>("WallpaperPreviewTopStatusBarHost");
|
||||
private StackPanel? WallpaperPreviewTopStatusComponentsPanel => this.FindControl<StackPanel>("WallpaperPreviewTopStatusComponentsPanel");
|
||||
private Border? WallpaperPreviewBottomTaskbarContainer => this.FindControl<Border>("WallpaperPreviewBottomTaskbarContainer");
|
||||
private ClockWidget? WallpaperPreviewClockWidget => this.FindControl<ClockWidget>("WallpaperPreviewClockWidget");
|
||||
private StackPanel? WallpaperPreviewBackButtonVisual => this.FindControl<StackPanel>("WallpaperPreviewBackButtonVisual");
|
||||
private TextBlock? WallpaperPreviewBackButtonTextBlock => this.FindControl<TextBlock>("WallpaperPreviewBackButtonTextBlock");
|
||||
private StackPanel? WallpaperPreviewComponentLibraryVisual => this.FindControl<StackPanel>("WallpaperPreviewComponentLibraryVisual");
|
||||
private TextBlock? WallpaperPreviewComponentLibraryTextBlock => this.FindControl<TextBlock>("WallpaperPreviewComponentLibraryTextBlock");
|
||||
private FluentIcons.Avalonia.SymbolIcon? WallpaperPreviewSettingsButtonIcon => this.FindControl<FluentIcons.Avalonia.SymbolIcon>("WallpaperPreviewSettingsButtonIcon");
|
||||
private ComboBox? StatusBarSpacingModeComboBox => this.FindControl<ComboBox>("StatusBarSpacingModeComboBox");
|
||||
private SettingsExpanderItem? StatusBarSpacingCustomPanel => this.FindControl<SettingsExpanderItem>("StatusBarSpacingCustomPanel");
|
||||
private Slider? StatusBarSpacingSlider => this.FindControl<Slider>("StatusBarSpacingSlider");
|
||||
private NumberBox? StatusBarSpacingNumberBox => this.FindControl<NumberBox>("StatusBarSpacingNumberBox");
|
||||
private TextBlock? StatusBarSpacingComputedPxTextBlock => this.FindControl<TextBlock>("StatusBarSpacingComputedPxTextBlock");
|
||||
private ComboBox? TimeZoneComboBox => this.FindControl<ComboBox>("TimeZoneComboBox");
|
||||
private SettingsExpander? LauncherHiddenItemsSettingsExpander => this.FindControl<SettingsExpander>("LauncherHiddenItemsSettingsExpander");
|
||||
private TextBlock? LauncherHiddenItemsEmptyTextBlock => this.FindControl<TextBlock>("LauncherHiddenItemsEmptyTextBlock");
|
||||
|
||||
private void OnSettingsChanged(object? sender, SettingsChangedEvent e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ScheduleReloadFromExternalSettings();
|
||||
}
|
||||
|
||||
private void ScheduleReloadFromExternalSettings()
|
||||
{
|
||||
if (_externalSettingsReloadPending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_externalSettingsReloadPending = true;
|
||||
DispatcherTimer.RunOnce(() =>
|
||||
{
|
||||
_externalSettingsReloadPending = false;
|
||||
ReloadFromPersistedSettings();
|
||||
}, TimeSpan.FromMilliseconds(120));
|
||||
}
|
||||
|
||||
private void OnNightModeChecked(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ApplyNightModeState(true, refreshPalettes: true);
|
||||
SchedulePersistSettings();
|
||||
}
|
||||
|
||||
private void OnNightModeUnchecked(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ApplyNightModeState(false, refreshPalettes: true);
|
||||
SchedulePersistSettings();
|
||||
}
|
||||
|
||||
private void InitializeLocalization(string? languageCode)
|
||||
{
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(languageCode);
|
||||
}
|
||||
|
||||
private string L(string key, string fallback)
|
||||
{
|
||||
return _localizationService.GetString(_languageCode, key, fallback);
|
||||
}
|
||||
|
||||
private string Lf(string key, string fallback, params object[] args)
|
||||
{
|
||||
var template = L(key, fallback);
|
||||
return string.Format(CultureInfo.CurrentCulture, template, args);
|
||||
}
|
||||
|
||||
private void ApplyLocalization()
|
||||
{
|
||||
Title = L("app.title", "LanMountainDesktop");
|
||||
BackToWindowsTextBlock.Text = L("button.back_to_windows", "Back to Windows");
|
||||
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Widgets");
|
||||
LauncherTitleTextBlock.Text = L("launcher.title", "App Launcher");
|
||||
LauncherSubtitleTextBlock.Text = OperatingSystem.IsLinux()
|
||||
? L("launcher.subtitle_linux", "Displays installed apps discovered from Linux desktop entries.")
|
||||
: L("launcher.subtitle", "Displays all apps and folders based on the Windows Start menu structure.");
|
||||
|
||||
UpdateCurrentRenderBackendStatus();
|
||||
RenderLauncherHiddenItemsList();
|
||||
}
|
||||
|
||||
private string GetLocalizedTimeZoneDisplayName(TimeZoneInfo timeZone)
|
||||
{
|
||||
var offset = timeZone.GetUtcOffset(DateTime.UtcNow);
|
||||
var sign = offset >= TimeSpan.Zero ? "+" : "-";
|
||||
var hours = Math.Abs(offset.Hours);
|
||||
var minutes = Math.Abs(offset.Minutes);
|
||||
var name = string.IsNullOrWhiteSpace(timeZone.StandardName) ? timeZone.Id : timeZone.StandardName;
|
||||
return $"(UTC{sign}{hours:D2}:{minutes:D2}) {name}";
|
||||
}
|
||||
|
||||
private void InitializeWeatherSettings(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_weatherLocationMode = string.Equals(snapshot.WeatherLocationMode, "Coordinates", StringComparison.OrdinalIgnoreCase)
|
||||
? WeatherLocationMode.Coordinates
|
||||
: WeatherLocationMode.CitySearch;
|
||||
_weatherLocationKey = snapshot.WeatherLocationKey ?? string.Empty;
|
||||
_weatherLocationName = snapshot.WeatherLocationName ?? string.Empty;
|
||||
_weatherLatitude = snapshot.WeatherLatitude;
|
||||
_weatherLongitude = snapshot.WeatherLongitude;
|
||||
_weatherAutoRefreshLocation = snapshot.WeatherAutoRefreshLocation;
|
||||
_weatherExcludedAlertsRaw = snapshot.WeatherExcludedAlerts ?? string.Empty;
|
||||
_weatherIconPackId = string.IsNullOrWhiteSpace(snapshot.WeatherIconPackId) ? "FluentRegular" : snapshot.WeatherIconPackId;
|
||||
_weatherNoTlsRequests = snapshot.WeatherNoTlsRequests;
|
||||
}
|
||||
|
||||
private void InitializeAutoStartWithWindowsSetting(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_autoStartWithWindows = snapshot.AutoStartWithWindows;
|
||||
}
|
||||
|
||||
private void InitializeAppRenderModeSetting(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_selectedAppRenderMode = string.IsNullOrWhiteSpace(snapshot.AppRenderMode)
|
||||
? AppRenderingModeHelper.Default
|
||||
: snapshot.AppRenderMode;
|
||||
_runningAppRenderMode = AppRenderingModeHelper.Normalize(snapshot.AppRenderMode);
|
||||
}
|
||||
|
||||
private void InitializeUpdateSettings(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_ = snapshot;
|
||||
_ = _updateSettingsService.Get();
|
||||
}
|
||||
|
||||
private void InitializeSettingsIcons()
|
||||
{
|
||||
}
|
||||
|
||||
private static bool TryParseColor(string? colorText, out Color color)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(colorText) && Color.TryParse(colorText, out color))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
color = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void TryRestoreWallpaper(string? savedWallpaperPath)
|
||||
{
|
||||
_wallpaperPath = string.IsNullOrWhiteSpace(savedWallpaperPath) ? null : savedWallpaperPath;
|
||||
|
||||
_wallpaperBitmap?.Dispose();
|
||||
_wallpaperBitmap = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_wallpaperPath) || !File.Exists(_wallpaperPath))
|
||||
{
|
||||
_wallpaperMediaType = WallpaperMediaType.None;
|
||||
return;
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(_wallpaperPath);
|
||||
if (SupportedVideoExtensions.Contains(extension))
|
||||
{
|
||||
_wallpaperMediaType = WallpaperMediaType.Video;
|
||||
_wallpaperVideoPath = _wallpaperPath;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SupportedImageExtensions.Contains(extension))
|
||||
{
|
||||
_wallpaperMediaType = WallpaperMediaType.None;
|
||||
_wallpaperPath = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead(_wallpaperPath);
|
||||
_wallpaperBitmap = new Bitmap(stream);
|
||||
_wallpaperMediaType = WallpaperMediaType.Image;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_wallpaperMediaType = WallpaperMediaType.None;
|
||||
_wallpaperPath = null;
|
||||
_wallpaperBitmap?.Dispose();
|
||||
_wallpaperBitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyWallpaperBrush()
|
||||
{
|
||||
if (_wallpaperMediaType == WallpaperMediaType.Image && _wallpaperBitmap is not null)
|
||||
{
|
||||
DesktopWallpaperLayer.Background = new ImageBrush(_wallpaperBitmap)
|
||||
{
|
||||
Stretch = Stretch.UniformToFill
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
DesktopWallpaperLayer.Background = _defaultDesktopBackground ?? Brushes.Transparent;
|
||||
}
|
||||
|
||||
private void UpdateWallpaperDisplay()
|
||||
{
|
||||
ApplyWallpaperBrush();
|
||||
}
|
||||
|
||||
private void StopVideoWallpaper()
|
||||
{
|
||||
_wallpaperVideoPath = null;
|
||||
if (_wallpaperMediaType == WallpaperMediaType.Video)
|
||||
{
|
||||
_wallpaperMediaType = WallpaperMediaType.None;
|
||||
}
|
||||
}
|
||||
|
||||
private double CalculateCurrentBackgroundLuminance()
|
||||
{
|
||||
var brush = DesktopWallpaperLayer.Background;
|
||||
if (brush is SolidColorBrush solid)
|
||||
{
|
||||
return CalculateRelativeLuminance(solid.Color);
|
||||
}
|
||||
|
||||
return CalculateRelativeLuminance(_selectedThemeColor);
|
||||
}
|
||||
|
||||
private void ApplyNightModeState(bool enabled, bool refreshPalettes)
|
||||
{
|
||||
_isNightMode = enabled;
|
||||
if (!refreshPalettes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var palette = _themeSettingsService.BuildPalette(enabled, _wallpaperPath);
|
||||
_recommendedColors = palette.RecommendedColors;
|
||||
_monetColors = palette.MonetColors;
|
||||
}
|
||||
|
||||
private static double CalculateRelativeLuminance(Color color)
|
||||
{
|
||||
return CalculateRelativeLuminance(color.R / 255d, color.G / 255d, color.B / 255d);
|
||||
}
|
||||
|
||||
private static double CalculateRelativeLuminance(double red, double green, double blue)
|
||||
{
|
||||
static double ToLinear(double value) =>
|
||||
value <= 0.03928 ? value / 12.92 : Math.Pow((value + 0.055) / 1.055, 2.4);
|
||||
|
||||
return 0.2126 * ToLinear(red) + 0.7152 * ToLinear(green) + 0.0722 * ToLinear(blue);
|
||||
}
|
||||
|
||||
private void TriggerAutoUpdateCheckIfEnabled()
|
||||
{
|
||||
}
|
||||
|
||||
private void PersistSettings()
|
||||
{
|
||||
if (_suppressSettingsPersistence)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_settingsService.SaveSnapshot(SettingsScope.App, BuildAppSettingsSnapshot());
|
||||
_componentLayoutStore.SaveLayout(BuildDesktopLayoutSettingsSnapshot());
|
||||
_settingsService.SaveSnapshot(SettingsScope.Launcher, BuildLauncherSettingsSnapshot());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.Warn("SettingsRuntime", "Failed to persist settings.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SchedulePersistSettings(int delayMs = 200)
|
||||
{
|
||||
DispatcherTimer.RunOnce(PersistSettings, TimeSpan.FromMilliseconds(Math.Max(0, delayMs)));
|
||||
}
|
||||
|
||||
internal void ReloadFromPersistedSettings()
|
||||
{
|
||||
var snapshot = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
var layoutSnapshot = _componentLayoutStore.LoadLayout();
|
||||
var launcherSnapshot = _settingsService.LoadSnapshot<LauncherSettingsSnapshot>(SettingsScope.Launcher);
|
||||
_suppressSettingsPersistence = true;
|
||||
try
|
||||
{
|
||||
ApplyTaskbarSettings(snapshot);
|
||||
InitializeWeatherSettings(snapshot);
|
||||
InitializeDesktopSurfaceState(layoutSnapshot);
|
||||
InitializeLauncherVisibilitySettings(launcherSnapshot);
|
||||
InitializeDesktopComponentPlacements(layoutSnapshot);
|
||||
TryRestoreWallpaper(snapshot.WallpaperPath);
|
||||
ApplyWallpaperBrush();
|
||||
RebuildDesktopGrid();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_suppressSettingsPersistence = false;
|
||||
}
|
||||
}
|
||||
|
||||
private AppSettingsSnapshot BuildAppSettingsSnapshot()
|
||||
{
|
||||
return new AppSettingsSnapshot
|
||||
{
|
||||
GridShortSideCells = _targetShortSideCells,
|
||||
GridSpacingPreset = _gridSpacingPreset,
|
||||
DesktopEdgeInsetPercent = _desktopEdgeInsetPercent,
|
||||
IsNightMode = _isNightMode,
|
||||
ThemeColor = _selectedThemeColor.ToString(),
|
||||
WallpaperPath = _wallpaperPath,
|
||||
LanguageCode = _languageCode,
|
||||
TimeZoneId = _timeZoneService.CurrentTimeZone.Id,
|
||||
WeatherLocationMode = _weatherLocationMode.ToString(),
|
||||
WeatherLocationKey = _weatherLocationKey,
|
||||
WeatherLocationName = _weatherLocationName,
|
||||
WeatherLatitude = _weatherLatitude,
|
||||
WeatherLongitude = _weatherLongitude,
|
||||
WeatherAutoRefreshLocation = _weatherAutoRefreshLocation,
|
||||
WeatherExcludedAlerts = _weatherExcludedAlertsRaw,
|
||||
WeatherIconPackId = _weatherIconPackId,
|
||||
WeatherNoTlsRequests = _weatherNoTlsRequests,
|
||||
AutoStartWithWindows = _autoStartWithWindows,
|
||||
AppRenderMode = _selectedAppRenderMode,
|
||||
TopStatusComponentIds = [.. _topStatusComponentIds],
|
||||
PinnedTaskbarActions = [.. _pinnedTaskbarActions.Select(v => v.ToString())],
|
||||
EnableDynamicTaskbarActions = _enableDynamicTaskbarActions,
|
||||
TaskbarLayoutMode = _taskbarLayoutMode,
|
||||
ClockDisplayFormat = _clockDisplayFormat == ClockDisplayFormat.HourMinute ? "HourMinute" : "HourMinuteSecond",
|
||||
StatusBarSpacingMode = _statusBarSpacingMode,
|
||||
StatusBarCustomSpacingPercent = _statusBarCustomSpacingPercent
|
||||
};
|
||||
}
|
||||
|
||||
private DesktopLayoutSettingsSnapshot BuildDesktopLayoutSettingsSnapshot()
|
||||
{
|
||||
return new DesktopLayoutSettingsSnapshot
|
||||
{
|
||||
DesktopPageCount = _desktopPageCount,
|
||||
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
|
||||
DesktopComponentPlacements = [.. _desktopComponentPlacements]
|
||||
};
|
||||
}
|
||||
|
||||
private LauncherSettingsSnapshot BuildLauncherSettingsSnapshot()
|
||||
{
|
||||
return new LauncherSettingsSnapshot
|
||||
{
|
||||
HiddenLauncherAppPaths = [.. _hiddenLauncherAppPaths],
|
||||
HiddenLauncherFolderPaths = [.. _hiddenLauncherFolderPaths]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,493 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views;
|
||||
|
||||
public partial class MainWindow
|
||||
{
|
||||
private const string UpdateChannelStable = "Stable";
|
||||
private const string UpdateChannelPreview = "Preview";
|
||||
|
||||
private bool _autoCheckUpdates = true;
|
||||
private string _updateChannel = UpdateChannelStable;
|
||||
private bool _suppressUpdateOptionEvents;
|
||||
private bool _isCheckingUpdates;
|
||||
private bool _isDownloadingUpdate;
|
||||
private string _latestReleaseVersionText = "-";
|
||||
private DateTimeOffset? _latestReleasePublishedAt;
|
||||
private string _updateStatusText = string.Empty;
|
||||
private string _updateDownloadProgressText = string.Empty;
|
||||
private double _updateDownloadProgressPercent;
|
||||
private GitHubReleaseAsset? _latestReleaseInstallerAsset;
|
||||
private string? _downloadedUpdateInstallerPath;
|
||||
|
||||
private bool IncludePrereleaseUpdates => string.Equals(
|
||||
_updateChannel,
|
||||
UpdateChannelPreview,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private void InitializeUpdateSettings(AppSettingsSnapshot snapshot)
|
||||
{
|
||||
_autoCheckUpdates = snapshot.AutoCheckUpdates;
|
||||
_updateChannel = NormalizeUpdateChannel(snapshot.UpdateChannel, snapshot.IncludePrereleaseUpdates);
|
||||
_latestReleaseVersionText = "-";
|
||||
_latestReleasePublishedAt = null;
|
||||
_updateDownloadProgressPercent = 0;
|
||||
_updateDownloadProgressText = L("settings.update.download_progress_idle", "Download progress: -");
|
||||
_updateStatusText = L("settings.update.status_ready", "Ready to check for updates.");
|
||||
_latestReleaseInstallerAsset = null;
|
||||
_downloadedUpdateInstallerPath = null;
|
||||
|
||||
_suppressUpdateOptionEvents = true;
|
||||
try
|
||||
{
|
||||
if (AutoCheckUpdatesToggleSwitch is not null)
|
||||
{
|
||||
AutoCheckUpdatesToggleSwitch.IsChecked = _autoCheckUpdates;
|
||||
}
|
||||
|
||||
if (UpdateChannelChipListBox is not null)
|
||||
{
|
||||
UpdateChannelChipListBox.SelectedIndex = IncludePrereleaseUpdates ? 1 : 0;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_suppressUpdateOptionEvents = false;
|
||||
}
|
||||
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
|
||||
private void TriggerAutoUpdateCheckIfEnabled()
|
||||
{
|
||||
if (!_autoCheckUpdates)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = CheckForUpdatesAsync(silentWhenNoUpdate: true);
|
||||
}
|
||||
|
||||
private void ApplyUpdateLocalization()
|
||||
{
|
||||
SettingsNavUpdateItem.Content = L("settings.nav.update", "Update");
|
||||
UpdatePanelTitleTextBlock.Text = L("settings.update.title", "Update");
|
||||
|
||||
UpdateCurrentVersionLabelTextBlock.Text = L("settings.update.current_version_label", "Current Version");
|
||||
UpdateLatestVersionLabelTextBlock.Text = L("settings.update.latest_version_label", "Latest Release");
|
||||
UpdatePublishedAtLabelTextBlock.Text = L("settings.update.published_at_label", "Published At");
|
||||
|
||||
UpdateOptionsSettingsExpander.Header = L("settings.update.options_header", "Update Options");
|
||||
UpdateOptionsSettingsExpander.Description = L(
|
||||
"settings.update.options_desc",
|
||||
"Configure update checks and release channel.");
|
||||
|
||||
AutoCheckUpdatesToggleSwitch.Content = L(
|
||||
"settings.update.auto_check_toggle",
|
||||
"Automatically check for updates on startup");
|
||||
UpdateChannelLabelTextBlock.Text = L(
|
||||
"settings.update.channel_label",
|
||||
"Update Channel");
|
||||
UpdateChannelStableChipItem.Content = L(
|
||||
"settings.update.channel_stable",
|
||||
"Stable");
|
||||
UpdateChannelPreviewChipItem.Content = L(
|
||||
"settings.update.channel_preview",
|
||||
"Preview");
|
||||
|
||||
UpdateActionsSettingsExpander.Header = L("settings.update.actions_header", "Update Actions");
|
||||
UpdateActionsSettingsExpander.Description = L(
|
||||
"settings.update.actions_desc",
|
||||
"Check releases, download installer, and start update.");
|
||||
|
||||
CheckForUpdatesButton.Content = L("settings.update.check_button", "Check for Updates");
|
||||
DownloadAndInstallUpdateButton.Content = L("settings.update.download_install_button", "Download & Install");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_updateDownloadProgressText))
|
||||
{
|
||||
_updateDownloadProgressText = L("settings.update.download_progress_idle", "Download progress: -");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_updateStatusText))
|
||||
{
|
||||
_updateStatusText = L("settings.update.status_ready", "Ready to check for updates.");
|
||||
}
|
||||
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
|
||||
private async void OnCheckForUpdatesClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
await CheckForUpdatesAsync(silentWhenNoUpdate: false);
|
||||
}
|
||||
|
||||
private async void OnDownloadAndInstallUpdateClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isCheckingUpdates || _isDownloadingUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_latestReleaseInstallerAsset is null)
|
||||
{
|
||||
await CheckForUpdatesAsync(silentWhenNoUpdate: false);
|
||||
}
|
||||
|
||||
if (_latestReleaseInstallerAsset is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await DownloadAndInstallUpdateAsync(_latestReleaseInstallerAsset);
|
||||
}
|
||||
|
||||
private void OnAutoCheckUpdatesToggled(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_suppressUpdateOptionEvents || AutoCheckUpdatesToggleSwitch is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_autoCheckUpdates = AutoCheckUpdatesToggleSwitch.IsChecked == true;
|
||||
PersistSettings();
|
||||
}
|
||||
|
||||
private void OnUpdateChannelSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_suppressUpdateOptionEvents || UpdateChannelChipListBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedChannel = UpdateChannelChipListBox.SelectedIndex == 1
|
||||
? UpdateChannelPreview
|
||||
: UpdateChannelStable;
|
||||
|
||||
if (string.Equals(_updateChannel, selectedChannel, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateChannel = selectedChannel;
|
||||
_latestReleaseInstallerAsset = null;
|
||||
_latestReleaseVersionText = "-";
|
||||
_latestReleasePublishedAt = null;
|
||||
_downloadedUpdateInstallerPath = null;
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_channel_changed_format",
|
||||
"Update channel switched to {0}. Please check again.",
|
||||
GetLocalizedUpdateChannelName(_updateChannel));
|
||||
PersistSettings();
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
|
||||
private async Task CheckForUpdatesAsync(bool silentWhenNoUpdate)
|
||||
{
|
||||
if (_isCheckingUpdates || _isDownloadingUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
{
|
||||
_updateStatusText = L(
|
||||
"settings.update.status_windows_only",
|
||||
"Automatic installer update is currently available only on Windows.");
|
||||
UpdateUpdatePanelState();
|
||||
return;
|
||||
}
|
||||
|
||||
_isCheckingUpdates = true;
|
||||
_updateStatusText = L("settings.update.status_checking", "Checking GitHub releases...");
|
||||
_updateDownloadProgressPercent = 0;
|
||||
_updateDownloadProgressText = L("settings.update.download_progress_idle", "Download progress: -");
|
||||
UpdateUpdatePanelState();
|
||||
|
||||
try
|
||||
{
|
||||
if (!Version.TryParse(GetAppVersionText(), out var currentVersion))
|
||||
{
|
||||
currentVersion = new Version(0, 0, 0);
|
||||
}
|
||||
|
||||
var result = await _releaseUpdateService.CheckForUpdatesAsync(
|
||||
currentVersion,
|
||||
IncludePrereleaseUpdates);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
_latestReleaseInstallerAsset = null;
|
||||
_latestReleaseVersionText = "-";
|
||||
_latestReleasePublishedAt = null;
|
||||
_downloadedUpdateInstallerPath = null;
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_check_failed_format",
|
||||
"Update check failed: {0}",
|
||||
result.ErrorMessage ?? L("common.unknown", "Unknown error"));
|
||||
return;
|
||||
}
|
||||
|
||||
_latestReleaseInstallerAsset = result.PreferredAsset;
|
||||
_latestReleaseVersionText = result.LatestVersionText;
|
||||
_latestReleasePublishedAt = result.Release?.PublishedAt;
|
||||
_downloadedUpdateInstallerPath = null;
|
||||
|
||||
if (!result.IsUpdateAvailable)
|
||||
{
|
||||
_latestReleaseInstallerAsset = null;
|
||||
_updateStatusText = silentWhenNoUpdate
|
||||
? L("settings.update.status_up_to_date", "You are already on the latest version.")
|
||||
: L("settings.update.status_up_to_date", "You are already on the latest version.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_latestReleaseInstallerAsset is null)
|
||||
{
|
||||
_updateStatusText = L(
|
||||
"settings.update.status_asset_missing",
|
||||
"A new release is available, but no compatible installer was found.");
|
||||
return;
|
||||
}
|
||||
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_available_format",
|
||||
"New version {0} is available. Click Download & Install.",
|
||||
_latestReleaseVersionText);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_check_failed_format",
|
||||
"Update check failed: {0}",
|
||||
ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isCheckingUpdates = false;
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadAndInstallUpdateAsync(GitHubReleaseAsset asset)
|
||||
{
|
||||
if (_isCheckingUpdates || _isDownloadingUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDownloadingUpdate = true;
|
||||
_updateStatusText = L("settings.update.status_downloading", "Downloading installer...");
|
||||
_updateDownloadProgressPercent = 0;
|
||||
_updateDownloadProgressText = Lf(
|
||||
"settings.update.download_progress_format",
|
||||
"Download progress: {0:F0}%",
|
||||
_updateDownloadProgressPercent);
|
||||
UpdateUpdatePanelState();
|
||||
|
||||
try
|
||||
{
|
||||
var destinationPath = BuildUpdateInstallerPath(asset.Name);
|
||||
var progress = new Progress<double>(value =>
|
||||
{
|
||||
_updateDownloadProgressPercent = Math.Clamp(value * 100d, 0d, 100d);
|
||||
_updateDownloadProgressText = Lf(
|
||||
"settings.update.download_progress_format",
|
||||
"Download progress: {0:F0}%",
|
||||
_updateDownloadProgressPercent);
|
||||
UpdateUpdatePanelState();
|
||||
});
|
||||
|
||||
var result = await _releaseUpdateService.DownloadAssetAsync(asset, destinationPath, progress);
|
||||
if (!result.Success || string.IsNullOrWhiteSpace(result.FilePath))
|
||||
{
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_download_failed_format",
|
||||
"Download failed: {0}",
|
||||
result.ErrorMessage ?? L("common.unknown", "Unknown error"));
|
||||
return;
|
||||
}
|
||||
|
||||
_downloadedUpdateInstallerPath = result.FilePath;
|
||||
_updateDownloadProgressPercent = 100;
|
||||
_updateDownloadProgressText = Lf(
|
||||
"settings.update.download_progress_format",
|
||||
"Download progress: {0:F0}%",
|
||||
_updateDownloadProgressPercent);
|
||||
_updateStatusText = L("settings.update.status_launching_installer", "Download complete. Launching installer...");
|
||||
UpdateUpdatePanelState();
|
||||
|
||||
LaunchInstallerAndExit(_downloadedUpdateInstallerPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_download_failed_format",
|
||||
"Download failed: {0}",
|
||||
ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDownloadingUpdate = false;
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
}
|
||||
|
||||
private void LaunchInstallerAndExit(string installerPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(installerPath) || !File.Exists(installerPath))
|
||||
{
|
||||
_updateStatusText = L(
|
||||
"settings.update.status_installer_missing",
|
||||
"Installer file was not found after download.");
|
||||
UpdateUpdatePanelState();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = installerPath,
|
||||
WorkingDirectory = Path.GetDirectoryName(installerPath) ?? Environment.CurrentDirectory,
|
||||
UseShellExecute = true,
|
||||
Verb = "runas"
|
||||
});
|
||||
|
||||
_updateStatusText = L(
|
||||
"settings.update.status_installer_started",
|
||||
"Installer started. The app will close for update.");
|
||||
UpdateUpdatePanelState();
|
||||
|
||||
_ = App.CurrentHostApplicationLifecycle?.TryExit(new HostApplicationLifecycleRequest(
|
||||
Source: nameof(MainWindow),
|
||||
Reason: "Update installer started successfully."));
|
||||
}
|
||||
catch (Win32Exception ex) when (ex.NativeErrorCode == 1223)
|
||||
{
|
||||
_updateStatusText = L(
|
||||
"settings.update.status_elevation_cancelled",
|
||||
"Administrator permission was not granted. Update was cancelled.");
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_updateStatusText = Lf(
|
||||
"settings.update.status_launch_failed_format",
|
||||
"Failed to start installer: {0}",
|
||||
ex.Message);
|
||||
UpdateUpdatePanelState();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUpdatePanelState()
|
||||
{
|
||||
if (UpdateCurrentVersionValueTextBlock is not null)
|
||||
{
|
||||
UpdateCurrentVersionValueTextBlock.Text = GetAppVersionText();
|
||||
}
|
||||
|
||||
if (UpdateLatestVersionValueTextBlock is not null)
|
||||
{
|
||||
UpdateLatestVersionValueTextBlock.Text = string.IsNullOrWhiteSpace(_latestReleaseVersionText)
|
||||
? "-"
|
||||
: _latestReleaseVersionText;
|
||||
}
|
||||
|
||||
if (UpdatePublishedAtValueTextBlock is not null)
|
||||
{
|
||||
UpdatePublishedAtValueTextBlock.Text = _latestReleasePublishedAt.HasValue &&
|
||||
_latestReleasePublishedAt.Value != DateTimeOffset.MinValue
|
||||
? _latestReleasePublishedAt.Value.LocalDateTime.ToString("yyyy-MM-dd HH:mm")
|
||||
: "-";
|
||||
}
|
||||
|
||||
if (UpdateStatusTextBlock is not null)
|
||||
{
|
||||
UpdateStatusTextBlock.Text = string.IsNullOrWhiteSpace(_updateStatusText)
|
||||
? L("settings.update.status_ready", "Ready to check for updates.")
|
||||
: _updateStatusText;
|
||||
}
|
||||
|
||||
if (UpdateDownloadProgressTextBlock is not null)
|
||||
{
|
||||
UpdateDownloadProgressTextBlock.Text = string.IsNullOrWhiteSpace(_updateDownloadProgressText)
|
||||
? L("settings.update.download_progress_idle", "Download progress: -")
|
||||
: _updateDownloadProgressText;
|
||||
}
|
||||
|
||||
if (UpdateDownloadProgressBar is not null)
|
||||
{
|
||||
UpdateDownloadProgressBar.IsVisible = _isDownloadingUpdate;
|
||||
UpdateDownloadProgressBar.Value = Math.Clamp(_updateDownloadProgressPercent, 0d, 100d);
|
||||
}
|
||||
|
||||
if (CheckForUpdatesButton is not null)
|
||||
{
|
||||
CheckForUpdatesButton.IsEnabled = !_isCheckingUpdates && !_isDownloadingUpdate;
|
||||
}
|
||||
|
||||
if (DownloadAndInstallUpdateButton is not null)
|
||||
{
|
||||
DownloadAndInstallUpdateButton.IsEnabled = !_isCheckingUpdates &&
|
||||
!_isDownloadingUpdate &&
|
||||
_latestReleaseInstallerAsset is not null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeUpdateChannel(string? channel, bool includePrereleaseFallback)
|
||||
{
|
||||
if (string.Equals(channel, UpdateChannelPreview, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return UpdateChannelPreview;
|
||||
}
|
||||
|
||||
if (string.Equals(channel, UpdateChannelStable, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return UpdateChannelStable;
|
||||
}
|
||||
|
||||
return includePrereleaseFallback ? UpdateChannelPreview : UpdateChannelStable;
|
||||
}
|
||||
|
||||
private string GetLocalizedUpdateChannelName(string channel)
|
||||
{
|
||||
return string.Equals(channel, UpdateChannelPreview, StringComparison.OrdinalIgnoreCase)
|
||||
? L("settings.update.channel_preview", "Preview")
|
||||
: L("settings.update.channel_stable", "Stable");
|
||||
}
|
||||
|
||||
private static string BuildUpdateInstallerPath(string assetName)
|
||||
{
|
||||
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
var updatesDirectory = Path.Combine(appData, "LanMountainDesktop", "Updates");
|
||||
Directory.CreateDirectory(updatesDirectory);
|
||||
|
||||
var safeName = SanitizeFileName(assetName);
|
||||
return Path.Combine(updatesDirectory, safeName);
|
||||
}
|
||||
|
||||
private static string SanitizeFileName(string fileName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
return $"LanMountainDesktop-Update-{DateTime.Now:yyyyMMddHHmmss}.exe";
|
||||
}
|
||||
|
||||
var sanitized = fileName;
|
||||
foreach (var c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
sanitized = sanitized.Replace(c, '_');
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent"
|
||||
xmlns:comp="using:LanMountainDesktop.Views.Components"
|
||||
xmlns:pages="using:LanMountainDesktop.Views.SettingsPages"
|
||||
xmlns:vlc="clr-namespace:LibVLCSharp.Avalonia;assembly=LibVLCSharp.Avalonia"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -321,183 +320,6 @@
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Grid x:Name="SettingsPage"
|
||||
Classes="settings-scope"
|
||||
IsVisible="False"
|
||||
Opacity="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="{StaticResource FluttermotionToken.Duration.Page}" />
|
||||
</Transitions>
|
||||
</Grid.Transitions>
|
||||
|
||||
<Border x:Name="SettingsContentPanel"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Margin="0"
|
||||
Padding="8"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
ClipToBounds="False">
|
||||
<Border.RenderTransform>
|
||||
<TranslateTransform Y="30">
|
||||
<TranslateTransform.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Y" Duration="{StaticResource FluttermotionToken.Duration.Page}" />
|
||||
</Transitions>
|
||||
</TranslateTransform.Transitions>
|
||||
</TranslateTransform>
|
||||
</Border.RenderTransform>
|
||||
|
||||
<Border Classes="mica-strong"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusXl}"
|
||||
Padding="18">
|
||||
<Grid RowDefinitions="*,Auto"
|
||||
RowSpacing="14">
|
||||
<ui:NavigationView x:Name="SettingsNavView"
|
||||
Grid.Row="0"
|
||||
PaneDisplayMode="Left"
|
||||
IsSettingsVisible="False"
|
||||
OpenPaneLength="220"
|
||||
SelectionChanged="OnSettingsNavSelectionChanged">
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWallpaperItem" Content="壁纸" Tag="Wallpaper" ToolTip.Tip="壁纸">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Wallpaper" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavGridItem" Content="网格" Tag="Grid" ToolTip.Tip="网格">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Grid" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavColorItem" Content="颜色" Tag="Color" ToolTip.Tip="颜色">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Color" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavStatusBarItem" Content="状态栏" Tag="StatusBar" ToolTip.Tip="状态栏">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Status" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavWeatherItem" Content="天气" Tag="Weather" ToolTip.Tip="天气">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="WeatherSunny" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavRegionItem" Content="地区" Tag="Region" ToolTip.Tip="地区">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Globe" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavUpdateItem" Content="更新" Tag="Update" ToolTip.Tip="更新">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="ArrowSync" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavAboutItem" Content="关于" Tag="About" ToolTip.Tip="关于">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Info" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavLauncherItem" Content="应用启动台" Tag="Launcher" ToolTip.Tip="应用启动台">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Apps" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavPluginsItem" Content="插件" Tag="Plugins" ToolTip.Tip="插件">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="PuzzlePiece" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
<ui:NavigationViewItem x:Name="SettingsNavPluginMarketItem" Content="插件市场" Tag="PluginMarket" ToolTip.Tip="插件市场">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ic:SymbolIconSource Symbol="PuzzlePiece" IconVariant="Regular" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</ui:NavigationView.MenuItems>
|
||||
|
||||
<ScrollViewer x:Name="SettingsContentScrollViewer"
|
||||
Padding="0,0,16,0"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Grid x:Name="SettingsContentPagesHost">
|
||||
<pages:WallpaperSettingsPage x:Name="WallpaperSettingsPanel" IsVisible="True" />
|
||||
|
||||
<pages:GridSettingsPage x:Name="GridSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:ColorSettingsPage x:Name="ColorSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:StatusBarSettingsPage x:Name="StatusBarSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:WeatherSettingsPage x:Name="WeatherSettingsPanel" IsVisible="False" />
|
||||
<pages:RegionSettingsPage x:Name="RegionSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:UpdateSettingsPage x:Name="UpdateSettingsPanel" IsVisible="False" />
|
||||
|
||||
<pages:LauncherSettingsPage x:Name="LauncherSettingsPanel" IsVisible="False" />
|
||||
<pages:AboutSettingsPage x:Name="AboutSettingsPanel" IsVisible="False" />
|
||||
<pages:PluginSettingsPage x:Name="PluginSettingsPanel" IsVisible="False" />
|
||||
<pages:PluginMarketSettingsPage x:Name="PluginMarketSettingsPanel" IsVisible="False" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</ui:NavigationView>
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Spacing="12">
|
||||
<Border x:Name="PendingRestartDock"
|
||||
IsVisible="False"
|
||||
Classes="glass-panel"
|
||||
CornerRadius="18"
|
||||
Padding="14,12">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||
ColumnSpacing="12">
|
||||
<Border Width="34"
|
||||
Height="34"
|
||||
CornerRadius="17"
|
||||
Background="{DynamicResource AdaptiveAccentBrush}">
|
||||
<fi:FluentIcon Icon="ArrowSync"
|
||||
IconVariant="Regular"
|
||||
FontSize="16"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1"
|
||||
Spacing="2"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock x:Name="PendingRestartDockTitleTextBlock"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Text="Restart required" />
|
||||
<TextBlock x:Name="PendingRestartDockDescriptionTextBlock"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||
Text="Your changes will apply after restarting the app." />
|
||||
</StackPanel>
|
||||
<Button x:Name="PendingRestartDockButton"
|
||||
Grid.Column="2"
|
||||
Padding="14,8"
|
||||
Click="OnPendingRestartDockButtonClick">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<fi:FluentIcon Icon="ArrowSync"
|
||||
IconVariant="Regular" />
|
||||
<TextBlock x:Name="PendingRestartDockButtonTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Text="Restart app" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<Border x:Name="ComponentLibraryWindow"
|
||||
IsVisible="False"
|
||||
Opacity="0"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -20,7 +20,9 @@ using Avalonia.Threading;
|
||||
using FluentAvalonia.Styling;
|
||||
using LanMountainDesktop.ComponentSystem;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.PluginSdk;
|
||||
using LanMountainDesktop.Services;
|
||||
using LanMountainDesktop.Services.Settings;
|
||||
using LanMountainDesktop.Theme;
|
||||
using LanMountainDesktop.Views.Components;
|
||||
using LibVLCSharp.Shared;
|
||||
@@ -73,21 +75,26 @@ public partial class MainWindow : Window
|
||||
[
|
||||
TaskbarActionId.MinimizeToWindows
|
||||
];
|
||||
private readonly DesktopGridLayoutService _gridLayoutService = new();
|
||||
private readonly MonetColorService _monetColorService = new();
|
||||
private readonly AppSettingsService _appSettingsService = new();
|
||||
private readonly DesktopLayoutSettingsService _desktopLayoutSettingsService = new();
|
||||
private readonly LauncherSettingsService _launcherSettingsService = new();
|
||||
private readonly ComponentSettingsService _componentSettingsService = new();
|
||||
private readonly ISettingsFacadeService _settingsFacade = HostSettingsFacadeProvider.GetOrCreate();
|
||||
private readonly IGridSettingsService _gridSettingsService;
|
||||
private readonly IThemeAppearanceService _themeSettingsService;
|
||||
private readonly IWeatherSettingsService _weatherSettingsService;
|
||||
private readonly IRegionSettingsService _regionSettingsService;
|
||||
private readonly IUpdateSettingsService _updateSettingsService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IComponentLayoutStore _componentLayoutStore = ComponentDomainStorageProvider.Instance;
|
||||
private readonly IComponentStateStore _componentStateStore = ComponentDomainStorageProvider.Instance;
|
||||
private readonly IComponentInstanceSettingsStore _componentSettingsStore = new ComponentSettingsService();
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
private readonly TimeZoneService _timeZoneService = new();
|
||||
private readonly TimeZoneService _timeZoneService;
|
||||
private readonly WindowsStartupService _windowsStartupService = new();
|
||||
private readonly GitHubReleaseUpdateService _releaseUpdateService = new("wwiinnddyy", "LanMountainDesktop");
|
||||
private readonly IWeatherDataService _weatherDataService = new XiaomiWeatherService();
|
||||
private readonly IWeatherInfoService _weatherDataService;
|
||||
private readonly IRecommendationInfoService _recommendationInfoService = new RecommendationDataService();
|
||||
private readonly ICalculatorDataService _calculatorDataService = new CalculatorDataService();
|
||||
private readonly ComponentRegistry _componentRegistry;
|
||||
private readonly DesktopComponentRuntimeRegistry _componentRuntimeRegistry;
|
||||
private readonly IComponentLibraryService _componentLibraryService;
|
||||
private readonly IComponentLibraryWindowService _componentLibraryWindowService = new ComponentLibraryWindowService();
|
||||
private readonly FluentAvaloniaTheme? _fluentAvaloniaTheme;
|
||||
private readonly HashSet<string> _topStatusComponentIds = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly HashSet<TaskbarActionId> _pinnedTaskbarActions = [];
|
||||
@@ -171,71 +178,25 @@ public partial class MainWindow : Window
|
||||
{
|
||||
var pluginRuntimeService = (Application.Current as App)?.PluginRuntimeService;
|
||||
_componentRegistry = DesktopComponentRegistryFactory.Create(pluginRuntimeService);
|
||||
_settingsService = _settingsFacade.Settings;
|
||||
_gridSettingsService = _settingsFacade.Grid;
|
||||
_themeSettingsService = _settingsFacade.Theme;
|
||||
_weatherSettingsService = _settingsFacade.Weather;
|
||||
_regionSettingsService = _settingsFacade.Region;
|
||||
_updateSettingsService = _settingsFacade.Update;
|
||||
_timeZoneService = _regionSettingsService.GetTimeZoneService();
|
||||
_weatherDataService = _weatherSettingsService.GetWeatherInfoService();
|
||||
|
||||
InitializeComponent();
|
||||
InitializePluginSettingsNavigation();
|
||||
_componentRuntimeRegistry = DesktopComponentRegistryFactory.CreateRuntimeRegistry(
|
||||
_componentRegistry,
|
||||
pluginRuntimeService);
|
||||
_componentLibraryService = new ComponentLibraryService(_componentRegistry, _componentRuntimeRegistry);
|
||||
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
|
||||
AppSettingsService.SettingsSaved += OnExternalAppSettingsSaved;
|
||||
LauncherSettingsService.SettingsSaved += OnExternalLauncherSettingsSaved;
|
||||
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
|
||||
_settingsService.Changed += OnSettingsChanged;
|
||||
PropertyChanged += OnWindowPropertyChanged;
|
||||
InitializeDesktopSurfaceSwipeHandlers();
|
||||
InitializeDesktopComponentDragHandlers();
|
||||
|
||||
PickWallpaperButton.Click += OnPickWallpaperClick;
|
||||
ClearWallpaperButton.Click += OnClearWallpaperClick;
|
||||
WallpaperPlacementComboBox.SelectionChanged += OnWallpaperPlacementSelectionChanged;
|
||||
|
||||
GridSizeSlider.ValueChanged += OnGridSizeSliderChanged;
|
||||
GridSpacingPresetComboBox.SelectionChanged += OnGridSpacingPresetSelectionChanged;
|
||||
GridEdgeInsetSlider.ValueChanged += OnGridEdgeInsetSliderChanged;
|
||||
ApplyGridButton.Click += OnApplyGridSizeClick;
|
||||
|
||||
NightModeToggleSwitch.IsCheckedChanged += OnNightModeIsCheckedChanged;
|
||||
RecommendedColorButton1.Click += OnRecommendedColorClick;
|
||||
RecommendedColorButton2.Click += OnRecommendedColorClick;
|
||||
RecommendedColorButton3.Click += OnRecommendedColorClick;
|
||||
RecommendedColorButton4.Click += OnRecommendedColorClick;
|
||||
RecommendedColorButton5.Click += OnRecommendedColorClick;
|
||||
RecommendedColorButton6.Click += OnRecommendedColorClick;
|
||||
RefreshMonetColorsButton.Click += OnRefreshMonetColorsClick;
|
||||
MonetColorButton1.Click += OnMonetColorClick;
|
||||
MonetColorButton2.Click += OnMonetColorClick;
|
||||
MonetColorButton3.Click += OnMonetColorClick;
|
||||
MonetColorButton4.Click += OnMonetColorClick;
|
||||
MonetColorButton5.Click += OnMonetColorClick;
|
||||
MonetColorButton6.Click += OnMonetColorClick;
|
||||
|
||||
StatusBarClockToggleSwitch.IsCheckedChanged += OnStatusBarClockIsCheckedChanged;
|
||||
ClockFormatHMSSRadio.IsCheckedChanged += OnClockFormatChanged;
|
||||
ClockFormatHMRadio.IsCheckedChanged += OnClockFormatChanged;
|
||||
StatusBarSpacingModeComboBox.SelectionChanged += OnStatusBarSpacingModeChanged;
|
||||
StatusBarSpacingSlider.ValueChanged += OnStatusBarSpacingSliderChanged;
|
||||
|
||||
WeatherPreviewButton.Click += OnTestWeatherRequestClick;
|
||||
WeatherLocationModeComboBox.SelectionChanged += OnWeatherLocationModeSelectionChanged;
|
||||
WeatherLocationModeChipListBox.SelectionChanged += OnWeatherLocationModeChipSelectionChanged;
|
||||
WeatherAutoRefreshToggleSwitch.IsCheckedChanged += OnWeatherAutoRefreshToggled;
|
||||
WeatherSearchButton.Click += OnSearchWeatherCityClick;
|
||||
WeatherApplyCityButton.Click += OnApplyWeatherCitySelectionClick;
|
||||
WeatherApplyCoordinatesButton.Click += OnApplyWeatherCoordinatesClick;
|
||||
WeatherExcludedAlertsTextBox.LostFocus += OnWeatherExcludedAlertsLostFocus;
|
||||
WeatherIconPackComboBox.SelectionChanged += OnWeatherIconPackSelectionChanged;
|
||||
WeatherNoTlsToggleSwitch.IsCheckedChanged += OnWeatherNoTlsToggled;
|
||||
|
||||
LanguageComboBox.SelectionChanged += OnLanguageSelectionChanged;
|
||||
TimeZoneComboBox.SelectionChanged += OnTimeZoneSelectionChanged;
|
||||
|
||||
AutoCheckUpdatesToggleSwitch.IsCheckedChanged += OnAutoCheckUpdatesToggled;
|
||||
UpdateChannelChipListBox.SelectionChanged += OnUpdateChannelSelectionChanged;
|
||||
CheckForUpdatesButton.Click += OnCheckForUpdatesClick;
|
||||
DownloadAndInstallUpdateButton.Click += OnDownloadAndInstallUpdateClick;
|
||||
|
||||
AutoStartWithWindowsToggleSwitch.IsCheckedChanged += OnAutoStartWithWindowsToggled;
|
||||
AppRenderModeComboBox.SelectionChanged += OnAppRenderModeSelectionChanged;
|
||||
}
|
||||
|
||||
private void OnNightModeIsCheckedChanged(object? sender, RoutedEventArgs e)
|
||||
@@ -275,9 +236,9 @@ public partial class MainWindow : Window
|
||||
base.OnOpened(e);
|
||||
|
||||
_suppressSettingsPersistence = true;
|
||||
var snapshot = _appSettingsService.Load();
|
||||
var desktopLayoutSnapshot = _desktopLayoutSettingsService.Load();
|
||||
var launcherSnapshot = _launcherSettingsService.Load();
|
||||
var snapshot = _settingsService.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
|
||||
var desktopLayoutSnapshot = _componentLayoutStore.LoadLayout();
|
||||
var launcherSnapshot = _settingsService.LoadSnapshot<LauncherSettingsSnapshot>(SettingsScope.Launcher);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(snapshot.TimeZoneId))
|
||||
{
|
||||
@@ -289,47 +250,16 @@ public partial class MainWindow : Window
|
||||
MinShortSideCells,
|
||||
MaxShortSideCells);
|
||||
|
||||
_gridSpacingPreset = _gridLayoutService.NormalizeSpacingPreset(snapshot.GridSpacingPreset);
|
||||
_suppressGridSpacingEvents = true;
|
||||
GridSpacingPresetComboBox.SelectedIndex = string.Equals(_gridSpacingPreset, "Compact", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||||
_suppressGridSpacingEvents = false;
|
||||
_gridSpacingPreset = _gridSettingsService.NormalizeSpacingPreset(snapshot.GridSpacingPreset);
|
||||
|
||||
_desktopEdgeInsetPercent = Math.Clamp(snapshot.DesktopEdgeInsetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||
_suppressGridInsetEvents = true;
|
||||
GridEdgeInsetSlider.Value = _desktopEdgeInsetPercent;
|
||||
GridEdgeInsetNumberBox.Value = _desktopEdgeInsetPercent;
|
||||
_suppressGridInsetEvents = false;
|
||||
GridEdgeInsetNumberBox.ValueChanged += OnGridEdgeInsetNumberBoxChanged;
|
||||
|
||||
_statusBarSpacingMode = NormalizeStatusBarSpacingMode(snapshot.StatusBarSpacingMode);
|
||||
_statusBarCustomSpacingPercent = Math.Clamp(snapshot.StatusBarCustomSpacingPercent, 0, 30);
|
||||
_suppressStatusBarSpacingEvents = true;
|
||||
StatusBarSpacingModeComboBox.SelectedIndex = _statusBarSpacingMode switch
|
||||
{
|
||||
"Compact" => 0,
|
||||
"Custom" => 2,
|
||||
_ => 1
|
||||
};
|
||||
StatusBarSpacingSlider.Value = _statusBarCustomSpacingPercent;
|
||||
StatusBarSpacingNumberBox.Value = _statusBarCustomSpacingPercent;
|
||||
StatusBarSpacingCustomPanel.IsVisible = string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase);
|
||||
_suppressStatusBarSpacingEvents = false;
|
||||
StatusBarSpacingNumberBox.ValueChanged += OnStatusBarSpacingNumberBoxChanged;
|
||||
|
||||
GridSizeNumberBox.Value = _targetShortSideCells;
|
||||
GridSizeSlider.Value = _targetShortSideCells;
|
||||
GridSizeSlider.ValueChanged += OnGridSizeSliderChanged;
|
||||
GridSizeNumberBox.ValueChanged += OnGridSizeNumberBoxChanged;
|
||||
|
||||
RestoreSettingsTabSelection(snapshot);
|
||||
UpdateSettingsTabContent();
|
||||
|
||||
WallpaperPlacementComboBox.SelectedIndex = GetPlacementIndexFromSetting(snapshot.WallpaperPlacement);
|
||||
_defaultDesktopBackground = DesktopWallpaperLayer.Background;
|
||||
ApplyTaskbarSettings(snapshot);
|
||||
InitializeLocalization(snapshot.LanguageCode);
|
||||
InitializeWeatherSettings(snapshot);
|
||||
_ = _componentSettingsService.Load();
|
||||
InitializeAutoStartWithWindowsSetting(snapshot);
|
||||
InitializeAppRenderModeSetting(snapshot);
|
||||
InitializeUpdateSettings(snapshot);
|
||||
@@ -349,15 +279,8 @@ public partial class MainWindow : Window
|
||||
|
||||
_isNightMode = snapshot.IsNightMode ?? (CalculateCurrentBackgroundLuminance() < LightBackgroundLuminanceThreshold);
|
||||
ApplyNightModeState(_isNightMode, refreshPalettes: true);
|
||||
_suppressStatusBarToggleEvents = true;
|
||||
StatusBarClockToggleSwitch.IsChecked = _topStatusComponentIds.Contains(BuiltInComponentIds.Clock);
|
||||
_suppressStatusBarToggleEvents = false;
|
||||
ApplyLocalization();
|
||||
ThemeColorStatusTextBlock.Text = Lf("settings.color.theme_ready_format", "Theme color ready: {0}.", _selectedThemeColor);
|
||||
_settingsContentPanelTransform = SettingsContentPanel.RenderTransform as TranslateTransform;
|
||||
DesktopHost.SizeChanged += OnDesktopHostSizeChanged;
|
||||
WallpaperPreviewHost.SizeChanged += OnWallpaperPreviewHostSizeChanged;
|
||||
GridPreviewHost.SizeChanged += OnGridPreviewHostSizeChanged;
|
||||
RebuildDesktopGrid();
|
||||
LoadLauncherEntriesAsync();
|
||||
InitializeTimeZoneSettings();
|
||||
@@ -384,28 +307,15 @@ public partial class MainWindow : Window
|
||||
_wallpaperPreviewSnapshotBitmap = null;
|
||||
_libVlc?.Dispose();
|
||||
_libVlc = null;
|
||||
if (_weatherDataService is IDisposable weatherServiceDisposable)
|
||||
{
|
||||
weatherServiceDisposable.Dispose();
|
||||
}
|
||||
if (_recommendationInfoService is IDisposable recommendationServiceDisposable)
|
||||
{
|
||||
recommendationServiceDisposable.Dispose();
|
||||
}
|
||||
_releaseUpdateService.Dispose();
|
||||
_wallpaperBitmap?.Dispose();
|
||||
_wallpaperBitmap = null;
|
||||
AppSettingsService.SettingsSaved -= OnExternalAppSettingsSaved;
|
||||
LauncherSettingsService.SettingsSaved -= OnExternalLauncherSettingsSaved;
|
||||
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
|
||||
_settingsService.Changed -= OnSettingsChanged;
|
||||
PropertyChanged -= OnWindowPropertyChanged;
|
||||
DesktopHost.SizeChanged -= OnDesktopHostSizeChanged;
|
||||
WallpaperPreviewHost.SizeChanged -= OnWallpaperPreviewHostSizeChanged;
|
||||
GridPreviewHost.SizeChanged -= OnGridPreviewHostSizeChanged;
|
||||
GridSizeSlider.ValueChanged -= OnGridSizeSliderChanged;
|
||||
GridSizeNumberBox.ValueChanged -= OnGridSizeNumberBoxChanged;
|
||||
GridEdgeInsetNumberBox.ValueChanged -= OnGridEdgeInsetNumberBoxChanged;
|
||||
StatusBarSpacingNumberBox.ValueChanged -= OnStatusBarSpacingNumberBoxChanged;
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
@@ -434,6 +344,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnGridSizeSliderChanged(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (GridSizeSlider is null || GridSizeNumberBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sliderValue = (int)Math.Round(GridSizeSlider.Value);
|
||||
if (Math.Abs(GridSizeNumberBox.Value - sliderValue) > double.Epsilon)
|
||||
{
|
||||
@@ -444,6 +359,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnGridSizeNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
|
||||
{
|
||||
if (GridSizeSlider is null || GridSizeNumberBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var numberBoxValue = (int)Math.Round(GridSizeNumberBox.Value);
|
||||
if (Math.Abs(GridSizeSlider.Value - numberBoxValue) > double.Epsilon)
|
||||
{
|
||||
@@ -454,6 +374,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnGridEdgeInsetSliderChanged(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (GridEdgeInsetSlider is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suppressGridInsetEvents)
|
||||
{
|
||||
return;
|
||||
@@ -466,6 +391,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnGridEdgeInsetNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
|
||||
{
|
||||
if (GridEdgeInsetNumberBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suppressGridInsetEvents)
|
||||
{
|
||||
return;
|
||||
@@ -511,6 +441,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnStatusBarSpacingModeChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (StatusBarSpacingModeComboBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suppressStatusBarSpacingEvents)
|
||||
{
|
||||
return;
|
||||
@@ -529,6 +464,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnStatusBarSpacingSliderChanged(object? sender, RangeBaseValueChangedEventArgs e)
|
||||
{
|
||||
if (StatusBarSpacingSlider is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suppressStatusBarSpacingEvents)
|
||||
{
|
||||
return;
|
||||
@@ -549,6 +489,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnStatusBarSpacingNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
|
||||
{
|
||||
if (StatusBarSpacingNumberBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_suppressStatusBarSpacingEvents)
|
||||
{
|
||||
return;
|
||||
@@ -597,7 +542,8 @@ public partial class MainWindow : Window
|
||||
GridPreviewHost is null ||
|
||||
GridPreviewViewport is null ||
|
||||
GridPreviewGrid is null ||
|
||||
GridPreviewLinesCanvas is null)
|
||||
GridPreviewLinesCanvas is null ||
|
||||
GridSizeSlider is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -626,11 +572,11 @@ public partial class MainWindow : Window
|
||||
|
||||
var innerWidth = Math.Max(1, gridPreviewWidth - horizontalPadding);
|
||||
var innerHeight = Math.Max(1, gridPreviewHeight - verticalPadding);
|
||||
var preset = _gridLayoutService.NormalizeSpacingPreset(TryGetSelectedComboBoxTag(GridSpacingPresetComboBox) ?? _gridSpacingPreset);
|
||||
var gapRatio = _gridLayoutService.ResolveGapRatio(preset);
|
||||
var preset = _gridSettingsService.NormalizeSpacingPreset(TryGetSelectedComboBoxTag(GridSpacingPresetComboBox) ?? _gridSpacingPreset);
|
||||
var gapRatio = _gridSettingsService.ResolveGapRatio(preset);
|
||||
var pendingEdgeInsetPercent = ResolvePendingGridEdgeInsetPercent();
|
||||
var edgeInset = _gridLayoutService.CalculateEdgeInset(innerWidth, innerHeight, previewShortSideCells, pendingEdgeInsetPercent);
|
||||
var gridMetrics = _gridLayoutService.CalculateGridMetrics(innerWidth, innerHeight, previewShortSideCells, gapRatio, edgeInset);
|
||||
var edgeInset = _gridSettingsService.CalculateEdgeInset(innerWidth, innerHeight, previewShortSideCells, pendingEdgeInsetPercent);
|
||||
var gridMetrics = _gridSettingsService.CalculateGridMetrics(innerWidth, innerHeight, previewShortSideCells, gapRatio, edgeInset);
|
||||
if (gridMetrics.CellSize <= 0)
|
||||
{
|
||||
return;
|
||||
@@ -676,12 +622,15 @@ public partial class MainWindow : Window
|
||||
ApplyStatusBarComponentSpacingForPanel(GridPreviewTopStatusComponentsPanel, gridMetrics.CellSize);
|
||||
UpdateGridEdgeInsetComputedPxText(gridMetrics.CellSize);
|
||||
|
||||
GridInfoTextBlock.Text = Lf(
|
||||
"settings.grid.info_format",
|
||||
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||
gridMetrics.ColumnCount,
|
||||
gridMetrics.RowCount,
|
||||
gridMetrics.CellSize);
|
||||
if (GridInfoTextBlock is not null)
|
||||
{
|
||||
GridInfoTextBlock.Text = Lf(
|
||||
"settings.grid.info_format",
|
||||
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||
gridMetrics.ColumnCount,
|
||||
gridMetrics.RowCount,
|
||||
gridMetrics.CellSize);
|
||||
}
|
||||
|
||||
DrawGridPreviewLines(gridMetrics);
|
||||
}
|
||||
@@ -767,7 +716,12 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnApplyGridSizeClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_gridSpacingPreset = _gridLayoutService.NormalizeSpacingPreset(
|
||||
if (GridSizeNumberBox is null || GridSizeSlider is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_gridSpacingPreset = _gridSettingsService.NormalizeSpacingPreset(
|
||||
TryGetSelectedComboBoxTag(GridSpacingPresetComboBox) ?? _gridSpacingPreset);
|
||||
_desktopEdgeInsetPercent = ResolvePendingGridEdgeInsetPercent();
|
||||
|
||||
@@ -825,9 +779,9 @@ public partial class MainWindow : Window
|
||||
{
|
||||
var hostWidth = DesktopHost.Bounds.Width;
|
||||
var hostHeight = DesktopHost.Bounds.Height;
|
||||
var gapRatio = _gridLayoutService.ResolveGapRatio(_gridSpacingPreset);
|
||||
var edgeInset = _gridLayoutService.CalculateEdgeInset(hostWidth, hostHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||
var gridMetrics = _gridLayoutService.CalculateGridMetrics(hostWidth, hostHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||
var gapRatio = _gridSettingsService.ResolveGapRatio(_gridSpacingPreset);
|
||||
var edgeInset = _gridSettingsService.CalculateEdgeInset(hostWidth, hostHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||
var gridMetrics = _gridSettingsService.CalculateGridMetrics(hostWidth, hostHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||
if (gridMetrics.CellSize <= 0)
|
||||
{
|
||||
return;
|
||||
@@ -875,12 +829,15 @@ public partial class MainWindow : Window
|
||||
UpdateDesktopSurfaceLayout(gridMetrics);
|
||||
UpdateSettingsViewportInsets(gridMetrics.CellSize);
|
||||
|
||||
GridInfoTextBlock.Text = Lf(
|
||||
"settings.grid.info_format",
|
||||
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||
gridMetrics.ColumnCount,
|
||||
gridMetrics.RowCount,
|
||||
gridMetrics.CellSize);
|
||||
if (GridInfoTextBlock is not null)
|
||||
{
|
||||
GridInfoTextBlock.Text = Lf(
|
||||
"settings.grid.info_format",
|
||||
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
|
||||
gridMetrics.ColumnCount,
|
||||
gridMetrics.RowCount,
|
||||
gridMetrics.CellSize);
|
||||
}
|
||||
|
||||
UpdateWallpaperPreviewLayout();
|
||||
}
|
||||
@@ -930,6 +887,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private int ResolvePendingGridEdgeInsetPercent()
|
||||
{
|
||||
if (GridEdgeInsetNumberBox is null)
|
||||
{
|
||||
return _desktopEdgeInsetPercent;
|
||||
}
|
||||
|
||||
var pending = (int)Math.Round(GridEdgeInsetNumberBox.Value);
|
||||
return Math.Clamp(pending, MinEdgeInsetPercent, MaxEdgeInsetPercent);
|
||||
}
|
||||
@@ -1067,47 +1029,7 @@ public partial class MainWindow : Window
|
||||
|
||||
private void UpdateSettingsViewportInsets(double cellSize)
|
||||
{
|
||||
if (SettingsContentPanel is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var clampedCell = Math.Max(1, cellSize);
|
||||
var horizontalInset = Math.Clamp(clampedCell * 0.45, 12, 64);
|
||||
var verticalGap = Math.Clamp(clampedCell * 0.16, 6, 18);
|
||||
var edgeInset = Math.Max(0, _currentDesktopEdgeInset);
|
||||
|
||||
var taskbarCellHeight = Math.Clamp(clampedCell * 0.76, 36, 76);
|
||||
var taskbarPadding = Math.Clamp(taskbarCellHeight * 0.16, 6, 14);
|
||||
var taskbarVisualHeight = Math.Max(clampedCell, taskbarCellHeight + taskbarPadding * 2);
|
||||
if (BottomTaskbarContainer is not null && BottomTaskbarContainer.Bounds.Height > 1)
|
||||
{
|
||||
taskbarVisualHeight = Math.Max(taskbarVisualHeight, BottomTaskbarContainer.Bounds.Height);
|
||||
}
|
||||
|
||||
var statusBarVisualHeight = clampedCell;
|
||||
if (TopStatusBarHost is not null && TopStatusBarHost.Bounds.Height > 1)
|
||||
{
|
||||
statusBarVisualHeight = Math.Max(statusBarVisualHeight, TopStatusBarHost.Bounds.Height);
|
||||
}
|
||||
|
||||
var topInset = Math.Max(clampedCell + verticalGap, edgeInset + statusBarVisualHeight + verticalGap);
|
||||
var bottomInset = Math.Max(clampedCell + verticalGap, edgeInset + taskbarVisualHeight + verticalGap);
|
||||
|
||||
// Add extra safety margin so rounded panel corners never clip against viewport edges.
|
||||
var cornerSafetyMargin = Math.Clamp(clampedCell * 0.12, 4, 12);
|
||||
var inset = new Thickness(
|
||||
horizontalInset + cornerSafetyMargin,
|
||||
topInset + cornerSafetyMargin,
|
||||
horizontalInset + cornerSafetyMargin,
|
||||
bottomInset + cornerSafetyMargin);
|
||||
|
||||
// Keep panel stretched with explicit viewport insets so it never overlaps fixed chrome.
|
||||
SettingsContentPanel.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
SettingsContentPanel.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
SettingsContentPanel.Margin = inset;
|
||||
SettingsContentPanel.Width = double.NaN;
|
||||
SettingsContentPanel.Height = double.NaN;
|
||||
_ = cellSize;
|
||||
}
|
||||
|
||||
private void UpdateWallpaperPreviewLayout()
|
||||
@@ -1160,9 +1082,9 @@ public partial class MainWindow : Window
|
||||
|
||||
var innerWidth = Math.Max(1, previewWidth - horizontalPadding);
|
||||
var innerHeight = Math.Max(1, previewHeight - verticalPadding);
|
||||
var gapRatio = _gridLayoutService.ResolveGapRatio(_gridSpacingPreset);
|
||||
var edgeInset = _gridLayoutService.CalculateEdgeInset(innerWidth, innerHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||
var gridMetrics = _gridLayoutService.CalculateGridMetrics(innerWidth, innerHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||
var gapRatio = _gridSettingsService.ResolveGapRatio(_gridSpacingPreset);
|
||||
var edgeInset = _gridSettingsService.CalculateEdgeInset(innerWidth, innerHeight, _targetShortSideCells, _desktopEdgeInsetPercent);
|
||||
var gridMetrics = _gridSettingsService.CalculateGridMetrics(innerWidth, innerHeight, _targetShortSideCells, gapRatio, edgeInset);
|
||||
if (gridMetrics.CellSize <= 0)
|
||||
{
|
||||
return;
|
||||
@@ -1273,6 +1195,11 @@ public partial class MainWindow : Window
|
||||
|
||||
private void InitializeTimeZoneSettings()
|
||||
{
|
||||
if (TimeZoneComboBox is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate timezone dropdown items before selecting current timezone.
|
||||
_suppressTimeZoneSelectionEvents = true;
|
||||
TimeZoneComboBox.Items.Clear();
|
||||
@@ -1299,7 +1226,9 @@ public partial class MainWindow : Window
|
||||
|
||||
private void OnTimeZoneSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_suppressTimeZoneSelectionEvents || TimeZoneComboBox.SelectedItem is not ComboBoxItem item)
|
||||
if (TimeZoneComboBox is null ||
|
||||
_suppressTimeZoneSelectionEvents ||
|
||||
TimeZoneComboBox.SelectedItem is not ComboBoxItem item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="1000"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.AboutSettingsPage">
|
||||
|
||||
<StackPanel x:Name="AboutSettingsPanel" Spacing="20">
|
||||
<TextBlock x:Name="AboutPanelTitleTextBlock" FontSize="24" FontWeight="SemiBold" Foreground="{DynamicResource TextFillColorPrimaryBrush}" Text="About" />
|
||||
|
||||
<Border Background="{DynamicResource AdaptiveSurfaceRaisedBrush}" CornerRadius="{DynamicResource DesignCornerRadiusMd}" Padding="20">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="LanMountainDesktop" FontSize="20" FontWeight="SemiBold" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock Text="Modern desktop shell experience." FontSize="13" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<Separator Background="{DynamicResource AdaptiveButtonBorderBrush}" Margin="0,8" />
|
||||
<TextBlock x:Name="VersionTextBlock" Text="Version: 1.0.0" FontSize="13" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock x:Name="CodeNameTextBlock" Text="Code Name: Administrate" FontSize="13" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveAccentBrush}" />
|
||||
<TextBlock x:Name="FontInfoTextBlock" Text="Font: MiSans" FontSize="12" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="AboutStartupSettingsExpander"
|
||||
Header="Windows Startup"
|
||||
Description="Launch the app automatically when signing in to Windows."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="Window" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ToggleSwitch x:Name="AutoStartWithWindowsToggleSwitch" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="AboutRenderModeSettingsExpander"
|
||||
Header="Rendering Mode"
|
||||
Description="Choose the rendering backend. Restart the app after changing this option. Unsupported modes fall back to software."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="Window" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<StackPanel Spacing="4"
|
||||
Margin="0,4,0,0">
|
||||
<TextBlock x:Name="CurrentRenderBackendLabelTextBlock"
|
||||
Text="Current actual backend"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock x:Name="CurrentRenderBackendValueTextBlock"
|
||||
Text="Current backend: Software"
|
||||
FontSize="13"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
<TextBlock x:Name="CurrentRenderBackendImplementationTextBlock"
|
||||
Text="Runtime implementation is unavailable."
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="AppRenderModeComboBox"
|
||||
MinWidth="180"
|
||||
SelectedIndex="0"
|
||||
HorizontalAlignment="Right">
|
||||
<ComboBoxItem Content="Default" Tag="Default" />
|
||||
<ComboBoxItem Content="Software" Tag="Software" />
|
||||
<ComboBoxItem Content="angleEgl" Tag="AngleEgl" />
|
||||
<ComboBoxItem Content="WGL" Tag="Wgl" />
|
||||
<ComboBoxItem Content="Vulkan" Tag="Vulkan" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class AboutSettingsPage : UserControl
|
||||
{
|
||||
public AboutSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="960"
|
||||
d:DesignHeight="1400"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.AppearanceSettingsPage">
|
||||
<StackPanel MaxWidth="920"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="AppearancePageSubtitleTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Personalize wallpaper, desktop grid, and accent colors in one place." />
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="AppearanceWallpaperSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Wallpaper" />
|
||||
<TextBlock x:Name="AppearanceWallpaperSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Use lightweight thumbnails and asset controls instead of heavy live preview." />
|
||||
<ContentControl x:Name="WallpaperContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="AppearanceGridSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Grid" />
|
||||
<TextBlock x:Name="AppearanceGridSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Tune grid density, spacing, and safe edge inset for the desktop canvas." />
|
||||
<ContentControl x:Name="GridContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="AppearanceColorSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Color" />
|
||||
<TextBlock x:Name="AppearanceColorSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Choose theme mode and accent colors with Fluent-consistent swatches." />
|
||||
<ContentControl x:Name="ColorContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,11 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class AppearanceSettingsPage : UserControl
|
||||
{
|
||||
public AppearanceSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.ColorSettingsPage">
|
||||
<StackPanel x:Name="ColorSettingsPanel"
|
||||
MaxWidth="920"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="ColorPanelTitleTextBlock"
|
||||
IsVisible="False"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="Color" />
|
||||
|
||||
<TextBlock x:Name="ColorPanelSubtitleTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Adjust theme mode and accent colors. The desktop shell will reuse these colors consistently." />
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="ThemeModeSettingsExpander"
|
||||
Header="日夜模式"
|
||||
Description="切换应用的浅色或深色主题。">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<ic:SymbolIconSource Symbol="DarkTheme"
|
||||
IconVariant="Regular" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ToggleSwitch x:Name="NightModeToggleSwitch"
|
||||
OffContent="Day"
|
||||
OnContent="Night" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="ThemeColorSettingsExpander"
|
||||
Header="主题色"
|
||||
Description="选择应用的主题点缀色。">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<ic:SymbolIconSource Symbol="Color"
|
||||
IconVariant="Regular" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="RecommendedColorsLabelTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="Recommended Colors" />
|
||||
<WrapPanel ItemWidth="72"
|
||||
ItemHeight="56"
|
||||
Orientation="Horizontal">
|
||||
<Button x:Name="RecommendedColorButton1"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch1"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="RecommendedColorButton2"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch2"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="RecommendedColorButton3"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch3"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="RecommendedColorButton4"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch4"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="RecommendedColorButton5"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch5"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="RecommendedColorButton6"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="RecommendedColorSwatch6"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Spacing="12">
|
||||
<Grid ColumnDefinitions="*,Auto"
|
||||
ColumnSpacing="10">
|
||||
<TextBlock x:Name="SystemMonetColorsLabelTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="System Monet Colors" />
|
||||
<Button x:Name="RefreshMonetColorsButton"
|
||||
Grid.Column="1"
|
||||
Padding="10,6"
|
||||
Content="Refresh" />
|
||||
</Grid>
|
||||
|
||||
<WrapPanel ItemWidth="72"
|
||||
ItemHeight="56"
|
||||
Orientation="Horizontal">
|
||||
<Button x:Name="MonetColorButton1"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch1"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="MonetColorButton2"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch2"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="MonetColorButton3"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch3"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="MonetColorButton4"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch4"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="MonetColorButton5"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch5"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
<Button x:Name="MonetColorButton6"
|
||||
Width="68"
|
||||
Height="50"
|
||||
Padding="8">
|
||||
<Border x:Name="MonetColorSwatch6"
|
||||
Width="26"
|
||||
Height="26"
|
||||
CornerRadius="12"
|
||||
BorderThickness="0" />
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell"
|
||||
Padding="16,14">
|
||||
<TextBlock x:Name="ThemeColorStatusTextBlock"
|
||||
Foreground="{DynamicResource AdaptiveTextMutedBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Theme color is ready." />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class ColorSettingsPage : UserControl
|
||||
{
|
||||
public ColorSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="960"
|
||||
d:DesignHeight="1200"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.ComponentsSettingsPage">
|
||||
<StackPanel MaxWidth="920"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="ComponentsPageSubtitleTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Review available desktop components and configure the status bar area." />
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="ComponentsSummarySectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Component Library" />
|
||||
<TextBlock x:Name="ComponentsSummarySectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Built-in and plugin-contributed components available to the desktop editor." />
|
||||
<TextBlock x:Name="ComponentsSummaryTextBlock"
|
||||
TextWrapping="Wrap"
|
||||
Text="Loading component catalog..." />
|
||||
<StackPanel x:Name="ComponentCategoryItemsPanel"
|
||||
Spacing="4" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="ComponentsStatusBarSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Status Bar" />
|
||||
<TextBlock x:Name="ComponentsStatusBarSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Clock and status-bar component spacing are managed here." />
|
||||
<ContentControl x:Name="StatusBarContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,11 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class ComponentsSettingsPage : UserControl
|
||||
{
|
||||
public ComponentsSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="960"
|
||||
d:DesignHeight="1400"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.GeneralSettingsPage">
|
||||
<StackPanel MaxWidth="920"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="GeneralPageSubtitleTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Manage language, launcher, and weather behavior from the independent settings module." />
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="GeneralRegionSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Region" />
|
||||
<TextBlock x:Name="GeneralRegionSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Language and time zone settings affect the entire desktop shell." />
|
||||
<ContentControl x:Name="RegionContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="GeneralLauncherSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Launcher" />
|
||||
<TextBlock x:Name="GeneralLauncherSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Restore hidden entries and adjust how the app launcher behaves." />
|
||||
<ContentControl x:Name="LauncherContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-page-shell">
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock x:Name="GeneralWeatherSectionTitleTextBlock"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Text="Weather" />
|
||||
<TextBlock x:Name="GeneralWeatherSectionHintTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Configure shared weather source, location, and icon style for weather widgets." />
|
||||
<ContentControl x:Name="WeatherContentHost" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,11 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class GeneralSettingsPage : UserControl
|
||||
{
|
||||
public GeneralSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent"
|
||||
xmlns:comp="using:LanMountainDesktop.Views.Components"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.GridSettingsPage">
|
||||
<Grid x:Name="GridSettingsPanel"
|
||||
ColumnDefinitions="280, *"
|
||||
RowDefinitions="Auto, *">
|
||||
<TextBlock x:Name="GridPanelTitleTextBlock"
|
||||
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Margin="0,0,0,20"
|
||||
Text="璋冩暣缃戞牸甯冨眬" />
|
||||
|
||||
<!-- Left Column: Grid Preview -->
|
||||
<Border x:Name="GridPreviewHost"
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,20,0"
|
||||
Width="256"
|
||||
MaxWidth="256"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Left">
|
||||
<Border x:Name="GridPreviewFrame"
|
||||
HorizontalAlignment="Stretch"
|
||||
CornerRadius="22"
|
||||
Background="#FF1A1A1A"
|
||||
Padding="8">
|
||||
<Border x:Name="GridPreviewViewport"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="14"
|
||||
Background="#30111827">
|
||||
<Panel>
|
||||
<Canvas x:Name="GridPreviewLinesCanvas"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsHitTestVisible="False" />
|
||||
<Grid x:Name="GridPreviewGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Border x:Name="GridPreviewTopStatusBarHost"
|
||||
Grid.Row="0"
|
||||
Background="Transparent"
|
||||
Padding="2">
|
||||
<StackPanel x:Name="GridPreviewTopStatusComponentsPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border x:Name="GridPreviewBottomTaskbarContainer"
|
||||
Classes="glass-strong"
|
||||
Grid.Row="1"
|
||||
Margin="3"
|
||||
CornerRadius="16"
|
||||
Padding="2">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||
ColumnSpacing="3">
|
||||
<Border x:Name="GridPreviewTaskbarFixedActionsHost" Grid.Column="0">
|
||||
<StackPanel x:Name="GridPreviewBackButtonVisual" Orientation="Horizontal" Spacing="3">
|
||||
<fi:SymbolIcon Classes="icon-s" Symbol="Window" />
|
||||
<TextBlock x:Name="GridPreviewBackButtonTextBlock" Text="鍥炲埌Windows" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel x:Name="GridPreviewTaskbarDynamicActionsHost"
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="3" />
|
||||
<Border x:Name="GridPreviewTaskbarSettingsActionHost" Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" Spacing="3">
|
||||
<StackPanel x:Name="GridPreviewComponentLibraryVisual" IsVisible="False" Orientation="Horizontal" Spacing="3">
|
||||
<fi:FluentIcon x:Name="GridPreviewComponentLibraryIcon" Classes="icon-s" Icon="Apps" />
|
||||
<TextBlock x:Name="GridPreviewComponentLibraryTextBlock" Text="Widget library" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<fi:SymbolIcon x:Name="GridPreviewSettingsButtonIcon" Classes="icon-s" Symbol="Settings" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Panel>
|
||||
</Border>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- Right Column: Settings Content -->
|
||||
<StackPanel Grid.Row="1" Grid.Column="1"
|
||||
Margin="20,0,0,0"
|
||||
Spacing="16">
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="GridRowsSettingsExpander" Header="Rows" Description="Adjust the density of the grid">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12" Width="220">
|
||||
<Slider x:Name="GridSizeSlider"
|
||||
Grid.Column="0"
|
||||
Minimum="6"
|
||||
Maximum="96"
|
||||
TickFrequency="1"
|
||||
TickPlacement="None"
|
||||
Value="12" />
|
||||
<ui:NumberBox x:Name="GridSizeNumberBox"
|
||||
Grid.Column="1"
|
||||
Width="80"
|
||||
Minimum="6"
|
||||
Maximum="96"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Value="12" />
|
||||
</Grid>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="GridSpacingSettingsExpander" Header="Spacing" Description="Adjust the gap between cells">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="GridSpacingPresetComboBox"
|
||||
Width="120">
|
||||
<ComboBoxItem x:Name="GridSpacingRelaxedComboBoxItem" Tag="Relaxed" Content="Relaxed" />
|
||||
<ComboBoxItem x:Name="GridSpacingCompactComboBoxItem" Tag="Compact" Content="Compact" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="GridEdgeInsetSettingsExpander" Header="Screen Inset" Description="Adjust margins around the desktop">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12" Width="220">
|
||||
<Slider x:Name="GridEdgeInsetSlider"
|
||||
Grid.Column="0"
|
||||
Minimum="0"
|
||||
Maximum="30"
|
||||
TickFrequency="1"
|
||||
TickPlacement="None"
|
||||
Value="18" />
|
||||
<ui:NumberBox x:Name="GridEdgeInsetNumberBox"
|
||||
Grid.Column="1"
|
||||
Width="80"
|
||||
Minimum="0"
|
||||
Maximum="30"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Value="18" />
|
||||
</Grid>
|
||||
</ui:SettingsExpander.Footer>
|
||||
<ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<TextBlock x:Name="GridEdgeInsetComputedPxTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text=">= 0 px" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Button x:Name="ApplyGridButton"
|
||||
HorizontalAlignment="Stretch"
|
||||
Padding="0,10"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Content="搴旂敤" />
|
||||
|
||||
<TextBlock x:Name="GridInfoTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="Grid: - cols x - rows (1:1)" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class GridSettingsPage : UserControl
|
||||
{
|
||||
public GridSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="1000"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.LauncherSettingsPage">
|
||||
|
||||
<StackPanel x:Name="LauncherSettingsPanel" Spacing="16">
|
||||
<TextBlock x:Name="LauncherSettingsPanelTitleTextBlock"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="App Launcher" />
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="LauncherHiddenItemsSettingsExpander"
|
||||
Header="Hidden Items"
|
||||
Description="Review hidden launcher entries and show them again."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<ui:FontIconSource Glyph="" FontFamily="{StaticResource SymbolThemeFontFamily}" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock x:Name="LauncherHiddenItemsDescriptionTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="Right-click an icon in launcher to hide it. Hidden entries appear here." />
|
||||
<TextBlock x:Name="LauncherHiddenItemsEmptyTextBlock"
|
||||
IsVisible="False"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="No hidden items." />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class LauncherSettingsPage : UserControl
|
||||
{
|
||||
public LauncherSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.RegionSettingsPage">
|
||||
|
||||
<StackPanel x:Name="RegionSettingsPanel"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="RegionPanelTitleTextBlock"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="Region" />
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="LanguageSettingsExpander"
|
||||
Header="Language"
|
||||
Description="Select application language. Changes apply immediately.">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="Translate" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="LanguageComboBox"
|
||||
Width="220">
|
||||
<ComboBoxItem x:Name="LanguageChineseItem" Tag="zh-CN" Content="涓枃" />
|
||||
<ComboBoxItem x:Name="LanguageEnglishItem" Tag="en-US" Content="English" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="TimeZoneSettingsExpander"
|
||||
Header="Time Zone"
|
||||
Description="Select a time zone. Clock and calendar widgets will follow this zone.">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="Clock" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="TimeZoneComboBox"
|
||||
Width="280" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class RegionSettingsPage : UserControl
|
||||
{
|
||||
public RegionSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.StatusBarSettingsPage">
|
||||
<StackPanel x:Name="StatusBarSettingsPanel"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="StatusBarPanelTitleTextBlock"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="Status Bar" />
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="StatusBarClockSettingsExpander"
|
||||
Header="系统时钟"
|
||||
Description="在状态栏上显示时间。"
|
||||
IsExpanded="False">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ToggleSwitch x:Name="StatusBarClockToggleSwitch"
|
||||
OnContent="On"
|
||||
OffContent="Off" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
|
||||
<ui:SettingsExpanderItem Content="Display Format">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<RadioButton x:Name="ClockFormatHMSSRadio"
|
||||
Content="HH:mm:ss"
|
||||
GroupName="ClockFormat"
|
||||
Tag="Hms" />
|
||||
<RadioButton x:Name="ClockFormatHMRadio"
|
||||
Content="HH:mm"
|
||||
GroupName="ClockFormat"
|
||||
Tag="Hm" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="StatusBarSpacingSettingsExpander"
|
||||
Header="Component spacing"
|
||||
Description="Adjust spacing between status bar components."
|
||||
IsExpanded="False">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="StatusBarSpacingModeComboBox"
|
||||
Width="150">
|
||||
<ComboBoxItem x:Name="StatusBarSpacingModeCompactItem" Tag="Compact" Content="Compact" />
|
||||
<ComboBoxItem x:Name="StatusBarSpacingModeRelaxedItem" Tag="Relaxed" Content="Relaxed" />
|
||||
<ComboBoxItem x:Name="StatusBarSpacingModeCustomItem" Tag="Custom" Content="Custom" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
|
||||
<ui:SettingsExpanderItem x:Name="StatusBarSpacingCustomPanel"
|
||||
Content="Custom spacing"
|
||||
IsVisible="False">
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Slider x:Name="StatusBarSpacingSlider"
|
||||
Width="150"
|
||||
Minimum="0"
|
||||
Maximum="30"
|
||||
TickFrequency="1"
|
||||
Value="12" />
|
||||
<ui:NumberBox x:Name="StatusBarSpacingNumberBox"
|
||||
Width="80"
|
||||
Minimum="0"
|
||||
Maximum="30"
|
||||
Value="12" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<TextBlock x:Name="StatusBarSpacingComputedPxTextBlock"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text=">= 0 px" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class StatusBarSettingsPage : UserControl
|
||||
{
|
||||
public StatusBarSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="1000"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.UpdateSettingsPage">
|
||||
|
||||
<StackPanel x:Name="UpdateSettingsPanel"
|
||||
Spacing="16">
|
||||
<TextBlock x:Name="UpdatePanelTitleTextBlock"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="Update" />
|
||||
|
||||
<Border Background="{DynamicResource AdaptiveSurfaceRaisedBrush}"
|
||||
CornerRadius="{DynamicResource DesignCornerRadiusMd}"
|
||||
Padding="20">
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto" ColumnSpacing="12" RowSpacing="8">
|
||||
<TextBlock x:Name="UpdateCurrentVersionLabelTextBlock"
|
||||
Text="Current Version"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock x:Name="UpdateCurrentVersionValueTextBlock"
|
||||
Grid.Column="1"
|
||||
Text="-"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
|
||||
<TextBlock x:Name="UpdateLatestVersionLabelTextBlock"
|
||||
Grid.Row="1"
|
||||
Text="Latest Release"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock x:Name="UpdateLatestVersionValueTextBlock"
|
||||
Grid.Row="1" Grid.Column="1"
|
||||
Text="-"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
|
||||
<TextBlock x:Name="UpdatePublishedAtLabelTextBlock"
|
||||
Grid.Row="2"
|
||||
Text="Published At"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock x:Name="UpdatePublishedAtValueTextBlock"
|
||||
Grid.Row="2" Grid.Column="1"
|
||||
Text="-"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="UpdateOptionsSettingsExpander"
|
||||
Header="Update Options"
|
||||
Description="Configure update checks and release channel."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="Settings" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<StackPanel Spacing="10">
|
||||
<ToggleSwitch x:Name="AutoCheckUpdatesToggleSwitch"
|
||||
Content="Automatically check for updates on startup" />
|
||||
<TextBlock x:Name="UpdateChannelLabelTextBlock"
|
||||
Text="Update Channel"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<ListBox x:Name="UpdateChannelChipListBox"
|
||||
Classes="settings-chip-list">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBoxItem x:Name="UpdateChannelStableChipItem"
|
||||
Tag="Stable"
|
||||
Content="Stable" />
|
||||
<ListBoxItem x:Name="UpdateChannelPreviewChipItem"
|
||||
Tag="Preview"
|
||||
Content="Preview" />
|
||||
</ListBox>
|
||||
</StackPanel>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="UpdateActionsSettingsExpander"
|
||||
Header="Update Actions"
|
||||
Description="Check releases, download installer, and start update."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.IconSource>
|
||||
<fi:SymbolIconSource Symbol="ArrowSync" />
|
||||
</ui:SettingsExpander.IconSource>
|
||||
<ui:SettingsExpander.Footer>
|
||||
<StackPanel Spacing="10">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<Button x:Name="CheckForUpdatesButton"
|
||||
MinWidth="140"
|
||||
Content="Check for Updates" />
|
||||
<Button x:Name="DownloadAndInstallUpdateButton"
|
||||
MinWidth="180"
|
||||
Content="Download & Install" />
|
||||
</StackPanel>
|
||||
<ProgressBar x:Name="UpdateDownloadProgressBar"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Height="6"
|
||||
IsVisible="False" />
|
||||
<TextBlock x:Name="UpdateDownloadProgressTextBlock"
|
||||
Text="Download progress: -"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock x:Name="UpdateStatusTextBlock"
|
||||
Text="Ready to check for updates."
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class UpdateSettingsPage : UserControl
|
||||
{
|
||||
public UpdateSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia"
|
||||
xmlns:ic="using:FluentIcons.Avalonia.Fluent"
|
||||
xmlns:comp="using:LanMountainDesktop.Views.Components"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.WallpaperSettingsPage">
|
||||
<Grid x:Name="WallpaperSettingsPanel"
|
||||
ColumnDefinitions="280, *"
|
||||
RowDefinitions="Auto, *">
|
||||
<TextBlock x:Name="WallpaperPanelTitleTextBlock"
|
||||
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
FontSize="24"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Margin="0,0,0,20"
|
||||
Text="Personalize Wallpaper" />
|
||||
|
||||
<!-- Left Column: Monitor Preview -->
|
||||
<Border x:Name="WallpaperPreviewHost"
|
||||
Grid.Row="1" Grid.Column="0"
|
||||
Margin="0,0,20,0"
|
||||
Width="256"
|
||||
MaxWidth="256"
|
||||
VerticalAlignment="Top"
|
||||
HorizontalAlignment="Left">
|
||||
<!-- Monitor Frame (Bezel) -->
|
||||
<Border x:Name="WallpaperPreviewFrame"
|
||||
HorizontalAlignment="Stretch"
|
||||
CornerRadius="22"
|
||||
Background="#FF1A1A1A"
|
||||
Padding="8">
|
||||
<Border x:Name="WallpaperPreviewViewport"
|
||||
ClipToBounds="True"
|
||||
CornerRadius="14"
|
||||
Background="#30111827">
|
||||
<Grid>
|
||||
<Image x:Name="WallpaperPreviewVideoImage"
|
||||
IsVisible="False"
|
||||
IsHitTestVisible="False"
|
||||
Stretch="UniformToFill"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch" />
|
||||
|
||||
<Grid x:Name="WallpaperPreviewGrid"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Border x:Name="WallpaperPreviewTopStatusBarHost"
|
||||
Grid.Row="0"
|
||||
Background="Transparent"
|
||||
Padding="2">
|
||||
<StackPanel x:Name="WallpaperPreviewTopStatusComponentsPanel"
|
||||
Orientation="Horizontal"
|
||||
Spacing="3">
|
||||
<comp:ClockWidget x:Name="WallpaperPreviewClockWidget"
|
||||
IsVisible="False" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border x:Name="WallpaperPreviewBottomTaskbarContainer"
|
||||
Classes="glass-strong"
|
||||
Grid.Row="1"
|
||||
Margin="3"
|
||||
CornerRadius="16"
|
||||
Padding="2">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||
ColumnSpacing="3">
|
||||
<Border x:Name="WallpaperPreviewTaskbarFixedActionsHost" Grid.Column="0">
|
||||
<StackPanel x:Name="WallpaperPreviewBackButtonVisual" Orientation="Horizontal" Spacing="3">
|
||||
<fi:SymbolIcon Classes="icon-s" Symbol="Window" />
|
||||
<TextBlock x:Name="WallpaperPreviewBackButtonTextBlock" Text="Back to Windows" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel x:Name="WallpaperPreviewTaskbarDynamicActionsHost"
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="3" />
|
||||
<Border x:Name="WallpaperPreviewTaskbarSettingsActionHost" Grid.Column="2">
|
||||
<StackPanel Orientation="Horizontal" Spacing="3">
|
||||
<StackPanel x:Name="WallpaperPreviewComponentLibraryVisual" IsVisible="False" Orientation="Horizontal" Spacing="3">
|
||||
<fi:FluentIcon Classes="icon-s" Icon="Apps" />
|
||||
<TextBlock x:Name="WallpaperPreviewComponentLibraryTextBlock" Text="Widget library" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<fi:SymbolIcon x:Name="WallpaperPreviewSettingsButtonIcon" Classes="icon-s" Symbol="Settings" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- Right Column: Settings Content -->
|
||||
<StackPanel Grid.Row="1" Grid.Column="1"
|
||||
Margin="20,0,0,0"
|
||||
Spacing="16">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Preview status" FontSize="12" Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock x:Name="WallpaperPathTextBlock"
|
||||
FontSize="14"
|
||||
FontWeight="Medium"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="No file selected" />
|
||||
<TextBlock x:Name="WallpaperStatusTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource AdaptiveTextMutedBrush}"
|
||||
Text="Ready" />
|
||||
</StackPanel>
|
||||
|
||||
<Separator Background="{DynamicResource SurfaceStrokeColorDefaultBrush}" Height="1" Margin="0,8" />
|
||||
|
||||
<TextBlock Text="Choose image or video" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
|
||||
|
||||
<Grid ColumnDefinitions="*, *" ColumnSpacing="12">
|
||||
<Button x:Name="PickWallpaperButton"
|
||||
Grid.Column="0"
|
||||
Classes="accent"
|
||||
HorizontalAlignment="Stretch"
|
||||
Padding="0,10"
|
||||
Content="Browse" />
|
||||
<Button x:Name="ClearWallpaperButton"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Padding="0,10"
|
||||
Content="Reset" />
|
||||
</Grid>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WallpaperPlacementSettingsExpander"
|
||||
Header="Placement"
|
||||
Padding="12,8">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="WallpaperPlacementComboBox"
|
||||
Width="120">
|
||||
<ComboBoxItem Content="Fill" />
|
||||
<ComboBoxItem Content="Fit" />
|
||||
<ComboBoxItem Content="Stretch" />
|
||||
<ComboBoxItem Content="Center" />
|
||||
<ComboBoxItem Content="Tile" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class WallpaperSettingsPage : UserControl
|
||||
{
|
||||
public WallpaperSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||
mc:Ignorable="d" d:DesignWidth="860" d:DesignHeight="1200"
|
||||
x:Class="LanMountainDesktop.Views.SettingsPages.WeatherSettingsPage">
|
||||
|
||||
<UserControl.Styles>
|
||||
<Style Selector="StackPanel.weather-settings-root TextBlock.section-eyebrow">
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="StackPanel.weather-settings-root Border.preview-icon-shell">
|
||||
<Setter Property="Width" Value="62" />
|
||||
<Setter Property="Height" Value="62" />
|
||||
<Setter Property="CornerRadius" Value="18" />
|
||||
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
||||
<Setter Property="BorderThickness" Value="1" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveButtonBorderBrush}" />
|
||||
<Setter Property="Padding" Value="10" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="StackPanel.weather-settings-root Border.settings-note-shell">
|
||||
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
||||
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
|
||||
<Setter Property="Padding" Value="14,12" />
|
||||
</Style>
|
||||
|
||||
<Style Selector="StackPanel.weather-settings-root Border.settings-expander-shell">
|
||||
<Setter Property="Margin" Value="0" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<StackPanel x:Name="WeatherSettingsContentPanel"
|
||||
Classes="settings-animated-intro weather-settings-root"
|
||||
Margin="0,0,8,0"
|
||||
Spacing="12">
|
||||
<TextBlock x:Name="WeatherPanelTitleTextBlock"
|
||||
FontSize="28"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
|
||||
Text="Weather" />
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock x:Name="WeatherPreviewSectionTextBlock"
|
||||
Classes="section-eyebrow"
|
||||
Text="Weather Preview" />
|
||||
|
||||
<Border Classes="settings-expander-shell"
|
||||
Padding="18,16">
|
||||
<Grid RowDefinitions="Auto,Auto"
|
||||
RowSpacing="10">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto"
|
||||
ColumnSpacing="14">
|
||||
<Border Classes="preview-icon-shell">
|
||||
<Image x:Name="WeatherPreviewIconImage"
|
||||
Stretch="Uniform" />
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="3">
|
||||
<TextBlock x:Name="WeatherPreviewTemperatureTextBlock"
|
||||
FontSize="34"
|
||||
FontWeight="SemiBold"
|
||||
Text="--" />
|
||||
<TextBlock x:Name="WeatherPreviewUpdatedTextBlock"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="-" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8"
|
||||
VerticalAlignment="Center">
|
||||
<Button x:Name="WeatherPreviewButton"
|
||||
Padding="16,8"
|
||||
Content="Refresh" />
|
||||
<ui:ProgressRing x:Name="WeatherPreviewProgressRing"
|
||||
Width="20"
|
||||
Height="20"
|
||||
IsActive="True"
|
||||
IsVisible="False" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock x:Name="WeatherPreviewResultTextBlock"
|
||||
Grid.Row="1"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="Use refresh to verify your weather configuration." />
|
||||
</Grid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<Border Background="{DynamicResource SurfaceStrokeColorDefaultBrush}"
|
||||
Height="1" />
|
||||
|
||||
<TextBlock x:Name="WeatherSettingsSectionTextBlock"
|
||||
Classes="section-eyebrow"
|
||||
Text="Settings" />
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherLocationSettingsExpander"
|
||||
Header="Location Source"
|
||||
Description="Choose how weather widgets resolve location."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ListBox x:Name="WeatherLocationModeChipListBox"
|
||||
Classes="settings-chip-list"
|
||||
HorizontalAlignment="Right"
|
||||
SelectionMode="Single">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBoxItem x:Name="WeatherLocationModeCityChipItem"
|
||||
Tag="CitySearch"
|
||||
Content="City Search" />
|
||||
<ListBoxItem x:Name="WeatherLocationModeCoordinatesChipItem"
|
||||
Tag="Coordinates"
|
||||
Content="Coordinates" />
|
||||
</ListBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<Grid ColumnDefinitions="*,Auto"
|
||||
ColumnSpacing="18">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock x:Name="WeatherLocationSelectionTitleTextBlock"
|
||||
FontSize="17"
|
||||
FontWeight="SemiBold"
|
||||
Text="City Selection" />
|
||||
<TextBlock x:Name="WeatherLocationSelectionDescriptionTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Select the current city used for weather queries." />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
MaxWidth="420"
|
||||
HorizontalAlignment="Right"
|
||||
Spacing="4">
|
||||
<TextBlock x:Name="WeatherLocationValueTextBlock"
|
||||
FontSize="17"
|
||||
FontWeight="SemiBold"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap"
|
||||
Text="No location selected" />
|
||||
<TextBlock x:Name="WeatherLocationStatusTextBlock"
|
||||
FontSize="12"
|
||||
TextAlignment="Right"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<ui:SettingsExpanderItem.Footer>
|
||||
<ToggleSwitch x:Name="WeatherAutoRefreshToggleSwitch"
|
||||
Content="Auto refresh location on startup" />
|
||||
</ui:SettingsExpanderItem.Footer>
|
||||
</ui:SettingsExpanderItem>
|
||||
|
||||
<ComboBox x:Name="WeatherLocationModeComboBox"
|
||||
IsVisible="False">
|
||||
<ComboBoxItem x:Name="WeatherLocationModeCityItem"
|
||||
Tag="CitySearch"
|
||||
Content="City Search" />
|
||||
<ComboBoxItem x:Name="WeatherLocationModeCoordinatesItem"
|
||||
Tag="Coordinates"
|
||||
Content="Coordinates" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherCitySearchSettingsExpander"
|
||||
Header="City Search"
|
||||
Description="Search cities and apply one weather location."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<Button x:Name="WeatherApplyCityButton"
|
||||
Padding="14,8"
|
||||
Content="Apply City" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<StackPanel Spacing="12">
|
||||
<Grid ColumnDefinitions="*,Auto,Auto"
|
||||
ColumnSpacing="10">
|
||||
<TextBox x:Name="WeatherCitySearchTextBox"
|
||||
Watermark="e.g. Beijing" />
|
||||
<ui:ProgressRing x:Name="WeatherSearchProgressRing"
|
||||
Grid.Column="1"
|
||||
Width="22"
|
||||
Height="22"
|
||||
IsActive="True"
|
||||
IsVisible="False" />
|
||||
<Button x:Name="WeatherSearchButton"
|
||||
Grid.Column="2"
|
||||
Padding="14,8"
|
||||
Content="Search" />
|
||||
</Grid>
|
||||
|
||||
<ComboBox x:Name="WeatherCityResultsComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
MinWidth="320" />
|
||||
|
||||
<TextBlock x:Name="WeatherSearchStatusTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Search by city name and apply one location." />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherCoordinateSettingsExpander"
|
||||
Header="Coordinates"
|
||||
Description="Set latitude/longitude and optional key/name."
|
||||
IsVisible="False"
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<Button x:Name="WeatherApplyCoordinatesButton"
|
||||
Padding="14,8"
|
||||
Content="Apply Coordinates" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
|
||||
<ui:SettingsExpanderItem>
|
||||
<StackPanel Spacing="12">
|
||||
<Grid ColumnDefinitions="*,*"
|
||||
ColumnSpacing="10">
|
||||
<ui:NumberBox x:Name="WeatherLatitudeNumberBox"
|
||||
Grid.Column="0"
|
||||
Header="Latitude"
|
||||
Minimum="-90"
|
||||
Maximum="90"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
SmallChange="0.1"
|
||||
LargeChange="1"
|
||||
Value="39.9042" />
|
||||
<ui:NumberBox x:Name="WeatherLongitudeNumberBox"
|
||||
Grid.Column="1"
|
||||
Header="Longitude"
|
||||
Minimum="-180"
|
||||
Maximum="180"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
SmallChange="0.1"
|
||||
LargeChange="1"
|
||||
Value="116.4074" />
|
||||
</Grid>
|
||||
|
||||
<TextBox x:Name="WeatherLocationKeyTextBox"
|
||||
Watermark="Location key (optional)" />
|
||||
<TextBox x:Name="WeatherLocationNameTextBox"
|
||||
Watermark="Display name (optional)" />
|
||||
<TextBlock x:Name="WeatherCoordinateStatusTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherAlertFilterSettingsExpander"
|
||||
Header="Excluded Alerts"
|
||||
Description="Alerts containing these words will not be shown. One rule per line."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpanderItem>
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="20">
|
||||
<StackPanel Width="220"
|
||||
Spacing="4">
|
||||
<TextBlock x:Name="WeatherAlertListTitleTextBlock"
|
||||
FontSize="17"
|
||||
FontWeight="SemiBold"
|
||||
Text="Exclude List" />
|
||||
<TextBlock x:Name="WeatherAlertListDescriptionTextBlock"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="One exclusion rule per line." />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox x:Name="WeatherExcludedAlertsTextBox"
|
||||
Grid.Column="1"
|
||||
MinHeight="96"
|
||||
MaxHeight="220"
|
||||
HorizontalAlignment="Stretch"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="One keyword per line" />
|
||||
</Grid>
|
||||
</ui:SettingsExpanderItem>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherNoTlsSettingsExpander"
|
||||
Header="No TLS Weather Request"
|
||||
Description="Not recommended. Enable only for incompatible network environments."
|
||||
IsExpanded="True">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ToggleSwitch x:Name="WeatherNoTlsToggleSwitch"
|
||||
Content="Allow non-TLS request fallback" />
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settings-note-shell">
|
||||
<TextBlock x:Name="WeatherFooterHintTextBlock"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
||||
Text="Desktop weather widgets will reuse the location and alert exclusion settings configured here." />
|
||||
</Border>
|
||||
|
||||
<Grid IsVisible="False">
|
||||
<ui:SettingsExpander x:Name="WeatherPreviewSettingsExpander"
|
||||
Header="Weather Preview"
|
||||
Description="Refresh and verify current weather service status." />
|
||||
<fi:SymbolIcon x:Name="WeatherPreviewIconSymbol"
|
||||
Symbol="WeatherSunny"
|
||||
IconVariant="Regular" />
|
||||
<Border Classes="settings-expander-shell">
|
||||
<ui:SettingsExpander x:Name="WeatherIconPackSettingsExpander"
|
||||
Header="Weather Icon Style"
|
||||
Description="Choose Fluent Icon style for weather symbols.">
|
||||
<ui:SettingsExpander.Footer>
|
||||
<ComboBox x:Name="WeatherIconPackComboBox"
|
||||
Width="220">
|
||||
<ComboBoxItem x:Name="WeatherIconPackFluentRegularItem"
|
||||
Tag="FluentRegular"
|
||||
Content="Fluent Regular" />
|
||||
<ComboBoxItem x:Name="WeatherIconPackFluentFilledItem"
|
||||
Tag="FluentFilled"
|
||||
Content="Fluent Filled" />
|
||||
</ComboBox>
|
||||
</ui:SettingsExpander.Footer>
|
||||
</ui:SettingsExpander>
|
||||
</Border>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace LanMountainDesktop.Views.SettingsPages;
|
||||
|
||||
public partial class WeatherSettingsPage : UserControl
|
||||
{
|
||||
public WeatherSettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user