Files
LanMountainDesktop/LanMountainDesktop/Views/SettingsWindow.axaml.cs
lincube cdffaa16eb 0.5.8
插件市场
2026-03-10 09:55:49 +08:00

328 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using FluentAvalonia.UI.Controls;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models;
using LanMountainDesktop.Services;
using LanMountainDesktop.Theme;
using LanMountainDesktop.Views.Components;
using LibVLCSharp.Shared;
using Line = Avalonia.Controls.Shapes.Line;
namespace LanMountainDesktop.Views;
public partial class SettingsWindow : Window
{
private enum WallpaperPlacement
{
Fill,
Fit,
Stretch,
Center,
Tile
}
private enum WallpaperMediaType
{
None,
Image,
Video
}
private enum WeatherLocationMode
{
CitySearch,
Coordinates
}
private const int MinShortSideCells = 6;
private const int MaxShortSideCells = 96;
private const int MinEdgeInsetPercent = 0;
private const int MaxEdgeInsetPercent = 30;
private const int DefaultEdgeInsetPercent = 18;
private const double WallpaperPreviewMaxWidth = 520;
private const double LightBackgroundLuminanceThreshold = 0.57;
private const string TaskbarLayoutBottomFullRowMacStyle = "BottomFullRowMacStyle";
private static readonly HashSet<string> SupportedImageExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".png", ".jpg", ".jpeg", ".bmp", ".gif", ".webp"
};
private static readonly HashSet<string> SupportedVideoExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".mp4", ".mkv", ".webm", ".avi", ".mov", ".m4v"
};
private static readonly TaskbarActionId[] DefaultPinnedTaskbarActions =
[
TaskbarActionId.MinimizeToWindows,
TaskbarActionId.OpenSettings
];
private readonly record struct GridMetrics(
int ColumnCount,
int RowCount,
double CellSize,
double GapPx,
double EdgeInsetPx,
double GridWidthPx,
double GridHeightPx)
{
public double Pitch => CellSize + GapPx;
}
private readonly MonetColorService _monetColorService = new();
private readonly AppSettingsService _appSettingsService = new();
private readonly LauncherSettingsService _launcherSettingsService = new();
private readonly LocalizationService _localizationService = new();
private readonly TimeZoneService _timeZoneService = new();
private readonly WindowsStartupService _windowsStartupService = new();
private readonly GitHubReleaseUpdateService _releaseUpdateService = new("wwiinnddyy", "LanMountainDesktop");
private readonly IWeatherDataService _weatherDataService = new XiaomiWeatherService();
private readonly ComponentRegistry _componentRegistry;
private readonly WindowsStartMenuService _windowsStartMenuService = new();
private readonly LinuxDesktopEntryService _linuxDesktopEntryService = new();
private readonly FluentAvaloniaTheme? _fluentAvaloniaTheme;
private readonly HashSet<string> _topStatusComponentIds = new(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<TaskbarActionId> _pinnedTaskbarActions = [];
private readonly HashSet<string> _hiddenLauncherFolderPaths = new(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _hiddenLauncherAppPaths = new(StringComparer.OrdinalIgnoreCase);
private readonly Stack<StartMenuFolderNode> _launcherFolderStack = [];
private readonly Dictionary<string, Button> _settingsNavItems = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Button> _pluginSettingsNavItems = new(StringComparer.OrdinalIgnoreCase);
private StartMenuFolderNode _startMenuRoot = new("All Apps", string.Empty);
private byte[]? _launcherFolderIconPngBytes;
private Bitmap? _launcherFolderIconBitmap;
private int _targetShortSideCells;
private bool _isNightMode;
private bool _enableDynamicTaskbarActions;
private bool _suppressThemeToggleEvents;
private bool _suppressLanguageSelectionEvents;
private bool _suppressTimeZoneSelectionEvents;
private bool _suppressWeatherLocationEvents;
private bool _suppressSettingsPersistence;
private bool _suppressGridInsetEvents;
private bool _suppressStatusBarSpacingEvents;
private bool _suppressAutoStartToggleEvents;
private bool _suppressAppRenderModeSelectionEvents;
private bool _isUpdatingWallpaperPreviewLayout;
private IBrush? _defaultDesktopBackground;
private Bitmap? _wallpaperBitmap;
private WallpaperMediaType _wallpaperMediaType;
private string? _wallpaperVideoPath;
private MediaPlayer? _previewVideoWallpaperPlayer;
private Media? _previewVideoWallpaperMedia;
private LibVLC? _libVlc;
private string? _wallpaperPath;
private string _wallpaperStatus = "Current background uses solid color.";
private IReadOnlyList<Color> _recommendedColors = Array.Empty<Color>();
private IReadOnlyList<Color> _monetColors = Array.Empty<Color>();
private Color _selectedThemeColor = Color.Parse("#FF3B82F6");
private double _currentDesktopCellSize;
private string _gridSpacingPreset = "Relaxed";
private string _statusBarSpacingMode = "Relaxed";
private int _statusBarCustomSpacingPercent = 12;
private int _desktopEdgeInsetPercent = DefaultEdgeInsetPercent;
private string _selectedAppRenderMode = AppRenderingModeHelper.Default;
private string _runningAppRenderMode = AppRenderingModeHelper.Default;
private string _taskbarLayoutMode = TaskbarLayoutBottomFullRowMacStyle;
private string _languageCode = "zh-CN";
private WeatherLocationMode _weatherLocationMode = WeatherLocationMode.CitySearch;
private string _weatherLocationKey = string.Empty;
private string _weatherLocationName = string.Empty;
private double _weatherLatitude = 39.9042;
private double _weatherLongitude = 116.4074;
private bool _weatherAutoRefreshLocation;
private string _weatherExcludedAlertsRaw = string.Empty;
private string _weatherIconPackId = "FluentRegular";
private bool _weatherNoTlsRequests;
private bool _autoStartWithWindows;
private string _weatherSearchKeyword = string.Empty;
private string _selectedSettingsTabTag = "Wallpaper";
private bool _isWeatherSearchInProgress;
private bool _isWeatherPreviewInProgress;
public SettingsWindow()
{
_componentRegistry = DesktopComponentRegistryFactory.Create((Application.Current as App)?.PluginRuntimeService);
InitializeComponent();
InitializeSettingsNavigation();
InitializePluginSettingsNavigation();
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
RequestedThemeVariant = Application.Current?.RequestedThemeVariant ?? ThemeVariant.Default;
PendingRestartStateService.StateChanged += OnPendingRestartStateChanged;
HookEvents();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void HookEvents()
{
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;
Opened += OnWindowOpened;
}
private void OnNightModeIsCheckedChanged(object? sender, RoutedEventArgs e)
{
if (sender is not ToggleButton toggleButton)
{
return;
}
if (toggleButton.IsChecked == true)
{
OnNightModeChecked(sender, e);
return;
}
OnNightModeUnchecked(sender, e);
}
private void OnStatusBarClockIsCheckedChanged(object? sender, RoutedEventArgs e)
{
if (sender is not ToggleButton toggleButton)
{
return;
}
if (toggleButton.IsChecked == true)
{
OnStatusBarClockChecked(sender, e);
return;
}
OnStatusBarClockUnchecked(sender, e);
}
private void OnWindowOpened(object? sender, EventArgs e)
{
Opened -= OnWindowOpened;
_suppressSettingsPersistence = true;
var snapshot = _appSettingsService.Load();
var launcherSnapshot = _launcherSettingsService.Load();
_targetShortSideCells = Math.Clamp(
snapshot.GridShortSideCells > 0 ? snapshot.GridShortSideCells : CalculateDefaultShortSideCellCountFromDpi(),
MinShortSideCells,
MaxShortSideCells);
_gridSpacingPreset = NormalizeGridSpacingPreset(snapshot.GridSpacingPreset);
_desktopEdgeInsetPercent = Math.Clamp(snapshot.DesktopEdgeInsetPercent, MinEdgeInsetPercent, MaxEdgeInsetPercent);
_statusBarSpacingMode = NormalizeStatusBarSpacingMode(snapshot.StatusBarSpacingMode);
_statusBarCustomSpacingPercent = Math.Clamp(snapshot.StatusBarCustomSpacingPercent, 0, 30);
GridSizeNumberBox.Value = _targetShortSideCells;
GridSizeSlider.Value = _targetShortSideCells;
GridSpacingPresetComboBox.SelectedIndex = string.Equals(_gridSpacingPreset, "Compact", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
GridEdgeInsetSlider.Value = _desktopEdgeInsetPercent;
GridEdgeInsetNumberBox.Value = _desktopEdgeInsetPercent;
StatusBarSpacingModeComboBox.SelectedIndex = _statusBarSpacingMode switch
{
"Compact" => 0,
"Custom" => 2,
_ => 1
};
StatusBarSpacingSlider.Value = _statusBarCustomSpacingPercent;
StatusBarSpacingNumberBox.Value = _statusBarCustomSpacingPercent;
StatusBarSpacingCustomPanel.IsVisible = string.Equals(_statusBarSpacingMode, "Custom", StringComparison.OrdinalIgnoreCase);
GridEdgeInsetNumberBox.ValueChanged += OnGridEdgeInsetNumberBoxChanged;
StatusBarSpacingNumberBox.ValueChanged += OnStatusBarSpacingNumberBoxChanged;
ApplyTaskbarSettings(snapshot);
InitializeLocalization(snapshot.LanguageCode);
InitializeWeatherSettings(snapshot);
InitializeAutoStartWithWindowsSetting(snapshot);
InitializeAppRenderModeSetting(snapshot);
InitializeUpdateSettings(snapshot);
InitializeLauncherVisibilitySettings(launcherSnapshot);
InitializeSettingsIcons();
ApplyLocalization();
WallpaperPlacementComboBox.SelectedIndex = GetPlacementIndexFromSetting(snapshot.WallpaperPlacement);
TryRestoreWallpaper(snapshot.WallpaperPath);
RefreshColorPalettes();
if (TryParseColor(snapshot.ThemeColor, out var savedThemeColor))
{
_selectedThemeColor = savedThemeColor;
}
_isNightMode = snapshot.IsNightMode ?? (CalculateCurrentBackgroundLuminance() < LightBackgroundLuminanceThreshold);
ApplyNightModeState(_isNightMode, refreshPalettes: false);
EnsureSelectedThemeColor();
UpdateThemeColorSelectionState();
ThemeColorStatusTextBlock.Text = Lf("settings.color.theme_ready_format", "Theme color ready: {0}.", _selectedThemeColor);
_defaultDesktopBackground = DesktopWallpaperLayer.Background;
RestoreSettingsTabSelection(snapshot);
UpdateSettingsTabContent();
UpdateWallpaperDisplay();
UpdateWallpaperPreviewLayout();
UpdateGridPreviewLayout();
InitializeTimeZoneSettings();
_ = LoadLauncherEntriesAsync();
_suppressSettingsPersistence = false;
}
private void OnCloseWindowClick(object? sender, RoutedEventArgs e)
{
Close();
}
}