mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
settings_re5
This commit is contained in:
@@ -8,6 +8,7 @@ using Avalonia.Controls.ApplicationLifetimes;
|
|||||||
using Avalonia.Data.Core;
|
using Avalonia.Data.Core;
|
||||||
using Avalonia.Data.Core.Plugins;
|
using Avalonia.Data.Core.Plugins;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
@@ -17,6 +18,7 @@ using LanMountainDesktop.Models;
|
|||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
using LanMountainDesktop.Services.Settings;
|
using LanMountainDesktop.Services.Settings;
|
||||||
|
using LanMountainDesktop.Theme;
|
||||||
using LanMountainDesktop.ViewModels;
|
using LanMountainDesktop.ViewModels;
|
||||||
using LanMountainDesktop.Views;
|
using LanMountainDesktop.Views;
|
||||||
|
|
||||||
@@ -24,6 +26,7 @@ namespace LanMountainDesktop;
|
|||||||
|
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
|
private static readonly Color DefaultAccentColor = Color.Parse("#FF3B82F6");
|
||||||
private enum DesktopShellState
|
private enum DesktopShellState
|
||||||
{
|
{
|
||||||
ForegroundDesktop = 0,
|
ForegroundDesktop = 0,
|
||||||
@@ -75,13 +78,18 @@ public partial class App : Application
|
|||||||
PageId: pageTag));
|
PageId: pageTag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public App()
|
||||||
|
{
|
||||||
|
_settingsFacade.Settings.Changed += OnSettingsChanged;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
AppLogger.Info("App", "Initializing application resources.");
|
AppLogger.Info("App", "Initializing application resources.");
|
||||||
ConfigureWebViewUserDataFolder();
|
ConfigureWebViewUserDataFolder();
|
||||||
AvaloniaWebViewBuilder.Initialize(default);
|
AvaloniaWebViewBuilder.Initialize(default);
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
ApplyInitialThemeVariantFromSettings();
|
ApplyThemeFromSettings();
|
||||||
ApplyCurrentCultureFromSettings();
|
ApplyCurrentCultureFromSettings();
|
||||||
EnsureSettingsWindowService();
|
EnsureSettingsWindowService();
|
||||||
}
|
}
|
||||||
@@ -292,12 +300,13 @@ public partial class App : Application
|
|||||||
_settingsFacade);
|
_settingsFacade);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyInitialThemeVariantFromSettings()
|
private void ApplyThemeFromSettings()
|
||||||
{
|
{
|
||||||
var themeState = _settingsFacade.Theme.Get();
|
var themeState = _settingsFacade.Theme.Get();
|
||||||
RequestedThemeVariant = themeState.IsNightMode
|
RequestedThemeVariant = themeState.IsNightMode
|
||||||
? ThemeVariant.Dark
|
? ThemeVariant.Dark
|
||||||
: ThemeVariant.Light;
|
: ThemeVariant.Light;
|
||||||
|
ApplyAdaptiveThemeResources(themeState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyCurrentCultureFromSettings()
|
private void ApplyCurrentCultureFromSettings()
|
||||||
@@ -424,7 +433,7 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
ApplyInitialThemeVariantFromSettings();
|
ApplyThemeFromSettings();
|
||||||
ApplyCurrentCultureFromSettings();
|
ApplyCurrentCultureFromSettings();
|
||||||
if (_trayIcons is not null)
|
if (_trayIcons is not null)
|
||||||
{
|
{
|
||||||
@@ -433,6 +442,71 @@ public partial class App : Application
|
|||||||
}, DispatcherPriority.Background);
|
}, DispatcherPriority.Background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSettingsChanged(object? sender, SettingsChangedEvent e)
|
||||||
|
{
|
||||||
|
_ = sender;
|
||||||
|
|
||||||
|
if (e.Scope != SettingsScope.App)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
var changedKeys = e.ChangedKeys?.ToArray();
|
||||||
|
var refreshAll = changedKeys is null || changedKeys.Length == 0;
|
||||||
|
var themeChanged =
|
||||||
|
refreshAll ||
|
||||||
|
changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) ||
|
||||||
|
changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase);
|
||||||
|
var languageChanged =
|
||||||
|
refreshAll ||
|
||||||
|
changedKeys.Contains(nameof(AppSettingsSnapshot.LanguageCode), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (themeChanged)
|
||||||
|
{
|
||||||
|
ApplyThemeFromSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (languageChanged)
|
||||||
|
{
|
||||||
|
ApplyCurrentCultureFromSettings();
|
||||||
|
if (_trayIcons is not null)
|
||||||
|
{
|
||||||
|
InitializeTrayIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, DispatcherPriority.Background);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyAdaptiveThemeResources(ThemeAppearanceSettingsState themeState)
|
||||||
|
{
|
||||||
|
var accentColor = TryParseThemeColor(themeState.ThemeColor);
|
||||||
|
var context = new ThemeColorContext(
|
||||||
|
accentColor,
|
||||||
|
IsLightBackground: !themeState.IsNightMode,
|
||||||
|
IsLightNavBackground: !themeState.IsNightMode,
|
||||||
|
IsNightMode: themeState.IsNightMode);
|
||||||
|
ThemeColorSystemService.ApplyThemeResources(Resources, context);
|
||||||
|
GlassEffectService.ApplyGlassResources(Resources, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color TryParseThemeColor(string? colorText)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(colorText))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Color.Parse(colorText);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultAccentColor;
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterUiUnhandledExceptionGuard()
|
private void RegisterUiUnhandledExceptionGuard()
|
||||||
{
|
{
|
||||||
if (_uiUnhandledExceptionHooked)
|
if (_uiUnhandledExceptionHooked)
|
||||||
@@ -479,6 +553,7 @@ public partial class App : Application
|
|||||||
|
|
||||||
_exitCleanupCompleted = true;
|
_exitCleanupCompleted = true;
|
||||||
AppSettingsService.SettingsSaved -= OnAppSettingsSaved;
|
AppSettingsService.SettingsSaved -= OnAppSettingsSaved;
|
||||||
|
_settingsFacade.Settings.Changed -= OnSettingsChanged;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
19
LanMountainDesktop/Controls/IconText.axaml
Normal file
19
LanMountainDesktop/Controls/IconText.axaml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia"
|
||||||
|
x:Class="LanMountainDesktop.Controls.IconText">
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Spacing="8"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<fi:FluentIcon x:Name="IconElement"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock x:Name="TextElement"
|
||||||
|
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="Medium"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
52
LanMountainDesktop/Controls/IconText.axaml.cs
Normal file
52
LanMountainDesktop/Controls/IconText.axaml.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using FluentIcons.Avalonia;
|
||||||
|
using FluentIcons.Common;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Controls;
|
||||||
|
|
||||||
|
public partial class IconText : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<Icon> IconProperty =
|
||||||
|
AvaloniaProperty.Register<IconText, Icon>(nameof(Icon), Icon.Info);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string> TextProperty =
|
||||||
|
AvaloniaProperty.Register<IconText, string>(nameof(Text), string.Empty);
|
||||||
|
|
||||||
|
public IconText()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Icon Icon
|
||||||
|
{
|
||||||
|
get => GetValue(IconProperty);
|
||||||
|
set => SetValue(IconProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get => GetValue(TextProperty);
|
||||||
|
set => SetValue(TextProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
|
{
|
||||||
|
base.OnPropertyChanged(change);
|
||||||
|
|
||||||
|
if (change.Property == IconProperty)
|
||||||
|
{
|
||||||
|
if (IconElement is not null)
|
||||||
|
{
|
||||||
|
IconElement.Icon = change.GetNewValue<Icon>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (change.Property == TextProperty)
|
||||||
|
{
|
||||||
|
if (TextElement is not null)
|
||||||
|
{
|
||||||
|
TextElement.Text = change.GetNewValue<string?>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,6 +102,7 @@ public partial class SettingsOptionCard : UserControl
|
|||||||
return iconKey?.Trim() switch
|
return iconKey?.Trim() switch
|
||||||
{
|
{
|
||||||
"DesignIdeas" => Symbol.Color,
|
"DesignIdeas" => Symbol.Color,
|
||||||
|
"Image" => Symbol.Image,
|
||||||
"GridDots" => Symbol.GridDots,
|
"GridDots" => Symbol.GridDots,
|
||||||
"PuzzlePiece" => Symbol.PuzzlePiece,
|
"PuzzlePiece" => Symbol.PuzzlePiece,
|
||||||
"Info" => Symbol.Info,
|
"Info" => Symbol.Info,
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ public partial class SettingsSectionCard : UserControl
|
|||||||
return iconKey?.Trim() switch
|
return iconKey?.Trim() switch
|
||||||
{
|
{
|
||||||
"DesignIdeas" => Symbol.Color,
|
"DesignIdeas" => Symbol.Color,
|
||||||
|
"Image" => Symbol.Image,
|
||||||
"GridDots" => Symbol.GridDots,
|
"GridDots" => Symbol.GridDots,
|
||||||
"PuzzlePiece" => Symbol.PuzzlePiece,
|
"PuzzlePiece" => Symbol.PuzzlePiece,
|
||||||
"Info" => Symbol.Info,
|
"Info" => Symbol.Info,
|
||||||
|
|||||||
@@ -232,7 +232,7 @@
|
|||||||
"settings.general.preview_date_label": "Date",
|
"settings.general.preview_date_label": "Date",
|
||||||
"settings.general.render_mode_restart_message": "Rendering mode changes require restarting the app.",
|
"settings.general.render_mode_restart_message": "Rendering mode changes require restarting the app.",
|
||||||
"settings.appearance.title": "Appearance",
|
"settings.appearance.title": "Appearance",
|
||||||
"settings.appearance.description": "Adjust theme, wallpaper, and status bar presentation.",
|
"settings.appearance.description": "Adjust theme and status bar presentation.",
|
||||||
"settings.appearance.theme_header": "Theme",
|
"settings.appearance.theme_header": "Theme",
|
||||||
"settings.color.enable_night_mode_toggle": "Enable night mode",
|
"settings.color.enable_night_mode_toggle": "Enable night mode",
|
||||||
"settings.color.use_system_chrome_toggle": "Use system window chrome",
|
"settings.color.use_system_chrome_toggle": "Use system window chrome",
|
||||||
@@ -802,4 +802,3 @@
|
|||||||
"single_instance.notice.button": "OK"
|
"single_instance.notice.button": "OK"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -232,7 +232,7 @@
|
|||||||
"settings.general.preview_date_label": "日期",
|
"settings.general.preview_date_label": "日期",
|
||||||
"settings.general.render_mode_restart_message": "渲染模式变更需要重启应用。",
|
"settings.general.render_mode_restart_message": "渲染模式变更需要重启应用。",
|
||||||
"settings.appearance.title": "外观",
|
"settings.appearance.title": "外观",
|
||||||
"settings.appearance.description": "切换主题、壁纸和状态栏展示。",
|
"settings.appearance.description": "切换主题与状态栏展示。",
|
||||||
"settings.appearance.theme_header": "主题",
|
"settings.appearance.theme_header": "主题",
|
||||||
"settings.color.enable_night_mode_toggle": "启用夜间模式",
|
"settings.color.enable_night_mode_toggle": "启用夜间模式",
|
||||||
"settings.color.use_system_chrome_toggle": "使用系统窗口标题栏",
|
"settings.color.use_system_chrome_toggle": "使用系统窗口标题栏",
|
||||||
@@ -802,4 +802,3 @@
|
|||||||
"single_instance.notice.button": "确定"
|
"single_instance.notice.button": "确定"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
using LanMountainDesktop.Theme;
|
||||||
using LanMountainDesktop.ViewModels;
|
using LanMountainDesktop.ViewModels;
|
||||||
using LanMountainDesktop.Views;
|
using LanMountainDesktop.Views;
|
||||||
|
|
||||||
@@ -50,6 +52,7 @@ public interface ISettingsWindowService
|
|||||||
|
|
||||||
internal sealed class SettingsWindowService : ISettingsWindowService
|
internal sealed class SettingsWindowService : ISettingsWindowService
|
||||||
{
|
{
|
||||||
|
private static readonly Color DefaultAccentColor = Color.Parse("#FF3B82F6");
|
||||||
private readonly ISettingsPageRegistry _pageRegistry;
|
private readonly ISettingsPageRegistry _pageRegistry;
|
||||||
private readonly IHostApplicationLifecycle _hostApplicationLifecycle;
|
private readonly IHostApplicationLifecycle _hostApplicationLifecycle;
|
||||||
private readonly ISettingsFacadeService _settingsFacade;
|
private readonly ISettingsFacadeService _settingsFacade;
|
||||||
@@ -85,7 +88,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService
|
|||||||
_window ??= CreateWindow();
|
_window ??= CreateWindow();
|
||||||
var themeState = _settingsFacade.Theme.Get();
|
var themeState = _settingsFacade.Theme.Get();
|
||||||
_window.ApplyChromeMode(themeState.UseSystemChrome);
|
_window.ApplyChromeMode(themeState.UseSystemChrome);
|
||||||
ApplyTheme(_window, themeState.IsNightMode);
|
ApplyTheme(_window, themeState);
|
||||||
_window.ReloadPages(request.PageId);
|
_window.ReloadPages(request.PageId);
|
||||||
PositionWindow(_window, request);
|
PositionWindow(_window, request);
|
||||||
|
|
||||||
@@ -140,7 +143,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService
|
|||||||
_pageRegistry,
|
_pageRegistry,
|
||||||
_hostApplicationLifecycle,
|
_hostApplicationLifecycle,
|
||||||
useSystemChrome);
|
useSystemChrome);
|
||||||
ApplyTheme(window, themeState.IsNightMode);
|
ApplyTheme(window, themeState);
|
||||||
window.ShowInTaskbar = false;
|
window.ShowInTaskbar = false;
|
||||||
window.Closed += (_, _) =>
|
window.Closed += (_, _) =>
|
||||||
{
|
{
|
||||||
@@ -277,6 +280,7 @@ internal sealed class SettingsWindowService : ISettingsWindowService
|
|||||||
var themeChanged =
|
var themeChanged =
|
||||||
refreshAll ||
|
refreshAll ||
|
||||||
changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) ||
|
changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) ||
|
||||||
|
changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase) ||
|
||||||
changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase);
|
changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (languageChanged)
|
if (languageChanged)
|
||||||
@@ -292,15 +296,40 @@ internal sealed class SettingsWindowService : ISettingsWindowService
|
|||||||
{
|
{
|
||||||
var themeState = _settingsFacade.Theme.Get();
|
var themeState = _settingsFacade.Theme.Get();
|
||||||
_window.ApplyChromeMode(themeState.UseSystemChrome);
|
_window.ApplyChromeMode(themeState.UseSystemChrome);
|
||||||
ApplyTheme(_window, themeState.IsNightMode);
|
ApplyTheme(_window, themeState);
|
||||||
}
|
}
|
||||||
}, DispatcherPriority.Background);
|
}, DispatcherPriority.Background);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApplyTheme(SettingsWindow window, bool isNightMode)
|
private static void ApplyTheme(SettingsWindow window, ThemeAppearanceSettingsState themeState)
|
||||||
{
|
{
|
||||||
window.RequestedThemeVariant = isNightMode
|
window.RequestedThemeVariant = themeState.IsNightMode
|
||||||
? ThemeVariant.Dark
|
? ThemeVariant.Dark
|
||||||
: ThemeVariant.Light;
|
: ThemeVariant.Light;
|
||||||
|
|
||||||
|
var accentColor = TryParseThemeColor(themeState.ThemeColor);
|
||||||
|
var context = new ThemeColorContext(
|
||||||
|
accentColor,
|
||||||
|
IsLightBackground: !themeState.IsNightMode,
|
||||||
|
IsLightNavBackground: !themeState.IsNightMode,
|
||||||
|
IsNightMode: themeState.IsNightMode);
|
||||||
|
ThemeColorSystemService.ApplyThemeResources(window.Resources, context);
|
||||||
|
GlassEffectService.ApplyGlassResources(window.Resources, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color TryParseThemeColor(string? colorText)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(colorText))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Color.Parse(colorText);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultAccentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,11 +107,21 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector=".settings-scope ComboBoxItem">
|
<Style Selector=".settings-scope ComboBoxItem">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
|
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusXs}" />
|
||||||
<Setter Property="Padding" Value="10,6" />
|
<Setter Property="Padding" Value="10,6" />
|
||||||
<Setter Property="Margin" Value="4,2" />
|
<Setter Property="Margin" Value="4,2" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector=".settings-scope ComboBoxItem:pointerover">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AdaptiveButtonHoverBackgroundBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector=".settings-scope ComboBoxItem:selected">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemSelectedBackgroundBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style Selector=".settings-scope ui|NumberBox">
|
<Style Selector=".settings-scope ui|NumberBox">
|
||||||
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
|
<Setter Property="CornerRadius" Value="{DynamicResource DesignCornerRadiusSm}" />
|
||||||
<Setter Property="MinHeight" Value="34" />
|
<Setter Property="MinHeight" Value="34" />
|
||||||
@@ -139,6 +149,10 @@
|
|||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemSelectedBackgroundBrush}" />
|
<Setter Property="Background" Value="{DynamicResource AdaptiveNavItemSelectedBackgroundBrush}" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector=".settings-scope ui|NavigationView, .settings-scope ui|NavigationViewItem, .settings-scope ui|SettingsExpander, .settings-scope ui|InfoBar, .settings-scope ListBoxItem">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Button.swatch-button">
|
<Style Selector="Button.swatch-button">
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Setter Property="CornerRadius" Value="16" />
|
<Setter Property="CornerRadius" Value="16" />
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
<Style Selector="StackPanel.settings-page-container">
|
<Style Selector="StackPanel.settings-page-container">
|
||||||
<Setter Property="Spacing" Value="0" />
|
<Setter Property="Spacing" Value="0" />
|
||||||
<Setter Property="Margin" Value="12,14,28,32" />
|
<Setter Property="Margin" Value="0,12,0,24" />
|
||||||
<Setter Property="MaxWidth" Value="900" />
|
<Setter Property="Width" Value="{DynamicResource SettingsPageContentWidth}" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="TextBlock.settings-section-title">
|
<Style Selector="TextBlock.settings-section-title">
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
<Setter Property="FontSize" Value="14" />
|
<Setter Property="FontSize" Value="14" />
|
||||||
<Setter Property="Opacity" Value="0.76" />
|
<Setter Property="Opacity" Value="0.76" />
|
||||||
<Setter Property="TextWrapping" Value="Wrap" />
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
<Setter Property="MaxWidth" Value="760" />
|
|
||||||
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
<Setter Property="Margin" Value="0,0,0,22" />
|
<Setter Property="Margin" Value="0,0,0,22" />
|
||||||
</Style>
|
</Style>
|
||||||
@@ -33,6 +32,7 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.settings-section-card, Border.settings-option-card, Border.settings-list-item">
|
<Style Selector="Border.settings-section-card, Border.settings-option-card, Border.settings-list-item">
|
||||||
|
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
||||||
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
|
<Setter Property="BorderBrush" Value="{DynamicResource AdaptiveGlassPanelBorderBrush}" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="Transitions">
|
<Setter Property="Transitions">
|
||||||
@@ -48,7 +48,6 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.settings-section-card">
|
<Style Selector="Border.settings-section-card">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
|
||||||
<Setter Property="CornerRadius" Value="18" />
|
<Setter Property="CornerRadius" Value="18" />
|
||||||
<Setter Property="Padding" Value="20" />
|
<Setter Property="Padding" Value="20" />
|
||||||
<Setter Property="Margin" Value="0,0,0,14" />
|
<Setter Property="Margin" Value="0,0,0,14" />
|
||||||
@@ -56,7 +55,6 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.settings-option-card">
|
<Style Selector="Border.settings-option-card">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
|
||||||
<Setter Property="CornerRadius" Value="14" />
|
<Setter Property="CornerRadius" Value="14" />
|
||||||
<Setter Property="Padding" Value="16" />
|
<Setter Property="Padding" Value="16" />
|
||||||
<Setter Property="Margin" Value="0,0,0,12" />
|
<Setter Property="Margin" Value="0,0,0,12" />
|
||||||
@@ -64,7 +62,6 @@
|
|||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="Border.settings-list-item">
|
<Style Selector="Border.settings-list-item">
|
||||||
<Setter Property="Background" Value="{DynamicResource AdaptiveSurfaceRaisedBrush}" />
|
|
||||||
<Setter Property="CornerRadius" Value="14" />
|
<Setter Property="CornerRadius" Value="14" />
|
||||||
<Setter Property="Padding" Value="16" />
|
<Setter Property="Padding" Value="16" />
|
||||||
<Setter Property="Margin" Value="0,0,0,10" />
|
<Setter Property="Margin" Value="0,0,0,10" />
|
||||||
@@ -121,7 +118,6 @@
|
|||||||
<Setter Property="Opacity" Value="0.68" />
|
<Setter Property="Opacity" Value="0.68" />
|
||||||
<Setter Property="TextWrapping" Value="Wrap" />
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
<Setter Property="Foreground" Value="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||||
<Setter Property="MaxWidth" Value="620" />
|
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<Style Selector="StackPanel.settings-item">
|
<Style Selector="StackPanel.settings-item">
|
||||||
@@ -137,6 +133,11 @@
|
|||||||
<Setter Property="ColumnSpacing" Value="12" />
|
<Setter Property="ColumnSpacing" Value="12" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="ui|SettingsExpander">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||||
|
<Setter Property="MinWidth" Value="0" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
<Style Selector="ui|SettingsExpander.settings-expander-card">
|
<Style Selector="ui|SettingsExpander.settings-expander-card">
|
||||||
<Setter Property="Margin" Value="0,0,0,14" />
|
<Setter Property="Margin" Value="0,0,0,14" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ public sealed partial class SettingsWindowViewModel : ViewModelBase
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _currentPageTitle = string.Empty;
|
private string _currentPageTitle = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isPageTitleVisible = true;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string? _currentPageDescription;
|
private string? _currentPageDescription;
|
||||||
|
|
||||||
@@ -636,7 +639,7 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
|
|||||||
private void RefreshLocalizedText()
|
private void RefreshLocalizedText()
|
||||||
{
|
{
|
||||||
PageTitle = L("settings.appearance.title", "Appearance");
|
PageTitle = L("settings.appearance.title", "Appearance");
|
||||||
PageDescription = L("settings.appearance.description", "Theme, wallpaper, and status bar presentation.");
|
PageDescription = L("settings.appearance.description", "Theme and status bar presentation.");
|
||||||
ThemeHeader = L("settings.appearance.theme_header", "Theme");
|
ThemeHeader = L("settings.appearance.theme_header", "Theme");
|
||||||
NightModeLabel = L("settings.color.enable_night_mode_toggle", "Enable night mode");
|
NightModeLabel = L("settings.color.enable_night_mode_toggle", "Enable night mode");
|
||||||
UseSystemChromeLabel = L("settings.color.use_system_chrome_toggle", "Use system window chrome");
|
UseSystemChromeLabel = L("settings.color.use_system_chrome_toggle", "Use system window chrome");
|
||||||
|
|||||||
127
LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs
Normal file
127
LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LanMountainDesktop.Services;
|
||||||
|
using LanMountainDesktop.Services.Settings;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.ViewModels;
|
||||||
|
|
||||||
|
public sealed partial class WallpaperSettingsPageViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly ISettingsFacadeService _settingsFacade;
|
||||||
|
private readonly LocalizationService _localizationService = new();
|
||||||
|
private readonly string _languageCode;
|
||||||
|
private bool _isInitializing;
|
||||||
|
|
||||||
|
public WallpaperSettingsPageViewModel(ISettingsFacadeService settingsFacade)
|
||||||
|
{
|
||||||
|
_settingsFacade = settingsFacade;
|
||||||
|
_languageCode = _localizationService.NormalizeLanguageCode(_settingsFacade.Region.Get().LanguageCode);
|
||||||
|
WallpaperPlacements = CreateWallpaperPlacements();
|
||||||
|
RefreshLocalizedText();
|
||||||
|
|
||||||
|
_isInitializing = true;
|
||||||
|
Load();
|
||||||
|
_isInitializing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<SelectionOption> WallpaperPlacements { get; }
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _wallpaperPath = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private SelectionOption _selectedWallpaperPlacement = new("Fill", "Fill");
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _wallpaperHeader = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _wallpaperPathLabel = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _wallpaperPlacementLabel = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _wallpaperPlacementDescription = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _importWallpaperButtonText = string.Empty;
|
||||||
|
|
||||||
|
[CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
|
||||||
|
private string _filePickerTitle = string.Empty;
|
||||||
|
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
var wallpaper = _settingsFacade.Wallpaper.Get();
|
||||||
|
WallpaperPath = wallpaper.WallpaperPath ?? string.Empty;
|
||||||
|
var wallpaperPlacement = string.IsNullOrWhiteSpace(wallpaper.Placement)
|
||||||
|
? "Fill"
|
||||||
|
: wallpaper.Placement;
|
||||||
|
SelectedWallpaperPlacement = WallpaperPlacements.FirstOrDefault(option =>
|
||||||
|
string.Equals(option.Value, wallpaperPlacement, StringComparison.OrdinalIgnoreCase))
|
||||||
|
?? WallpaperPlacements[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ImportWallpaperAsync(string sourcePath)
|
||||||
|
{
|
||||||
|
var importedPath = await _settingsFacade.WallpaperMedia.ImportAssetAsync(sourcePath);
|
||||||
|
if (!string.IsNullOrWhiteSpace(importedPath))
|
||||||
|
{
|
||||||
|
WallpaperPath = importedPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnWallpaperPathChanged(string value)
|
||||||
|
{
|
||||||
|
if (_isInitializing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveWallpaper();
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedWallpaperPlacementChanged(SelectionOption value)
|
||||||
|
{
|
||||||
|
if (_isInitializing || value is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveWallpaper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveWallpaper()
|
||||||
|
{
|
||||||
|
_settingsFacade.Wallpaper.Save(new WallpaperSettingsState(
|
||||||
|
string.IsNullOrWhiteSpace(WallpaperPath) ? null : WallpaperPath,
|
||||||
|
SelectedWallpaperPlacement.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<SelectionOption> CreateWallpaperPlacements()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
[
|
||||||
|
new SelectionOption("Fill", L("settings.wallpaper.placement.fill", "Fill")),
|
||||||
|
new SelectionOption("Fit", L("settings.wallpaper.placement.fit", "Fit")),
|
||||||
|
new SelectionOption("Stretch", L("settings.wallpaper.placement.stretch", "Stretch")),
|
||||||
|
new SelectionOption("Center", L("settings.wallpaper.placement.center", "Center")),
|
||||||
|
new SelectionOption("Tile", L("settings.wallpaper.placement.tile", "Tile"))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshLocalizedText()
|
||||||
|
{
|
||||||
|
WallpaperHeader = L("settings.wallpaper.title", "Wallpaper");
|
||||||
|
WallpaperPathLabel = L("settings.wallpaper.current_label", "Current Wallpaper");
|
||||||
|
WallpaperPlacementLabel = L("settings.wallpaper.placement_label", "Placement");
|
||||||
|
WallpaperPlacementDescription = L("settings.wallpaper.placement_desc", "Adjust how the image fills the desktop.");
|
||||||
|
ImportWallpaperButtonText = L("settings.wallpaper.pick_button", "Import Wallpaper");
|
||||||
|
FilePickerTitle = L("filepicker.title", "Select wallpaper");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string L(string key, string fallback)
|
||||||
|
=> _localizationService.GetString(_languageCode, key, fallback);
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ using FluentAvalonia.UI.Controls;
|
|||||||
using LanMountainDesktop.Models;
|
using LanMountainDesktop.Models;
|
||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
|
using LanMountainDesktop.Theme;
|
||||||
using LanMountainDesktop.Views.Components;
|
using LanMountainDesktop.Views.Components;
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views;
|
namespace LanMountainDesktop.Views;
|
||||||
@@ -193,6 +194,30 @@ public partial class MainWindow
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ThemeColorContext BuildAdaptiveThemeContext()
|
||||||
|
{
|
||||||
|
return new ThemeColorContext(
|
||||||
|
_selectedThemeColor,
|
||||||
|
IsLightBackground: !_isNightMode,
|
||||||
|
IsLightNavBackground: !_isNightMode,
|
||||||
|
IsNightMode: _isNightMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyAdaptiveThemeResources()
|
||||||
|
{
|
||||||
|
var context = BuildAdaptiveThemeContext();
|
||||||
|
ThemeColorSystemService.ApplyThemeResources(Resources, context);
|
||||||
|
GlassEffectService.ApplyGlassResources(Resources, context);
|
||||||
|
|
||||||
|
if (Application.Current?.Resources is { } applicationResources)
|
||||||
|
{
|
||||||
|
ThemeColorSystemService.ApplyThemeResources(applicationResources, context);
|
||||||
|
GlassEffectService.ApplyGlassResources(applicationResources, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
_defaultDesktopBackground = GetThemeBrush("AdaptiveSurfaceBaseBrush");
|
||||||
|
}
|
||||||
|
|
||||||
private void TryRestoreWallpaper(string? savedWallpaperPath)
|
private void TryRestoreWallpaper(string? savedWallpaperPath)
|
||||||
{
|
{
|
||||||
_wallpaperPath = string.IsNullOrWhiteSpace(savedWallpaperPath) ? null : savedWallpaperPath;
|
_wallpaperPath = string.IsNullOrWhiteSpace(savedWallpaperPath) ? null : savedWallpaperPath;
|
||||||
@@ -285,6 +310,9 @@ public partial class MainWindow
|
|||||||
Application.Current.RequestedThemeVariant = requestedThemeVariant;
|
Application.Current.RequestedThemeVariant = requestedThemeVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyAdaptiveThemeResources();
|
||||||
|
ApplyWallpaperBrush();
|
||||||
|
|
||||||
if (!refreshPalettes)
|
if (!refreshPalettes)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -25,43 +25,6 @@
|
|||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
<vm:MainWindowViewModel />
|
<vm:MainWindowViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
|
||||||
<Window.Resources>
|
|
||||||
<SolidColorBrush x:Key="AdaptivePrimaryBrush" Color="#FF1D4ED8" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveSecondaryBrush" Color="#FF1E40AF" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveTextPrimaryBrush" Color="#FFF8FAFC" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveTextSecondaryBrush" Color="#FFE2E8F0" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveTextMutedBrush" Color="#FF94A3B8" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveTextAccentBrush" Color="#FF93C5FD" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveAccentBrush" Color="#FF3B82F6" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveOnAccentBrush" Color="#FFFFFFFF" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveSurfaceBaseBrush" Color="#FF020617" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveSurfaceRaisedBrush" Color="#FF1E293B" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveSurfaceOverlayBrush" Color="#CC0F172A" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveButtonBackgroundBrush" Color="#66334155" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveButtonBorderBrush" Color="#80E2E8F0" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveButtonHoverBackgroundBrush" Color="#88475A74" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveButtonPressedBackgroundBrush" Color="#AA2A3B55" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveGlassPanelBackgroundBrush" Color="#70233448" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveGlassPanelBorderBrush" Color="#70475569" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveGlassStrongBackgroundBrush" Color="#A01E293B" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveGlassStrongBorderBrush" Color="#80475569" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveGlassOverlayBackgroundBrush" Color="#9A0F172A" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavTextBrush" Color="#FFF8FAFC" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavSelectedTextBrush" Color="#FFFFFFFF" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavSelectionIndicatorBrush" Color="#FF93C5FD" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavItemBackgroundBrush" Color="#220F172A" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavItemHoverBackgroundBrush" Color="#40334155" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveNavItemSelectedBackgroundBrush" Color="#CC1D4ED8" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveToggleOnBrush" Color="#FF3B82F6" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveToggleOffBrush" Color="#66475569" />
|
|
||||||
<SolidColorBrush x:Key="AdaptiveToggleBorderBrush" Color="#80E2E8F0" />
|
|
||||||
<x:Double x:Key="AdaptiveGlassPanelBlurRadius">22</x:Double>
|
|
||||||
<x:Double x:Key="AdaptiveGlassStrongBlurRadius">28</x:Double>
|
|
||||||
<x:Double x:Key="AdaptiveGlassPanelOpacity">0.92</x:Double>
|
|
||||||
<x:Double x:Key="AdaptiveGlassStrongOpacity">0.95</x:Double>
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid x:Name="DesktopPage"
|
<Grid x:Name="DesktopPage"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
|
|||||||
@@ -3,78 +3,102 @@
|
|||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
xmlns:controls="using:LanMountainDesktop.Controls"
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsPages.AboutSettingsPage"
|
x:Class="LanMountainDesktop.Views.SettingsPages.AboutSettingsPage"
|
||||||
x:DataType="vm:AboutSettingsPageViewModel">
|
x:DataType="vm:AboutSettingsPageViewModel">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Classes="settings-page-container">
|
<StackPanel Classes="settings-page-container">
|
||||||
<TextBlock Classes="settings-section-title"
|
|
||||||
Text="{Binding PageTitle}" />
|
|
||||||
<TextBlock Classes="settings-section-description"
|
|
||||||
Text="{Binding PageDescription}" />
|
|
||||||
|
|
||||||
<controls:SettingsSectionCard IconKey="Info"
|
<!-- 应用信息分组 -->
|
||||||
Title="{Binding AppInfoHeader}">
|
<controls:IconText Icon="Info"
|
||||||
<controls:SettingsSectionCard.CardContent>
|
Text="{Binding AppInfoHeader}"
|
||||||
<StackPanel Spacing="14">
|
Margin="0,0,0,4" />
|
||||||
<StackPanel Classes="settings-item">
|
|
||||||
<TextBlock Classes="settings-item-label"
|
|
||||||
Text="{Binding VersionLabel}" />
|
|
||||||
<TextBlock Opacity="0.82"
|
|
||||||
Text="{Binding VersionText}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<StackPanel Classes="settings-item">
|
<ui:SettingsExpander Header="{Binding AppInfoHeader}">
|
||||||
<TextBlock Classes="settings-item-label"
|
<ui:SettingsExpander.IconSource>
|
||||||
Text="{Binding RenderBackendLabel}" />
|
<fi:SymbolIconSource Symbol="Info" />
|
||||||
<TextBlock Opacity="0.82"
|
</ui:SettingsExpander.IconSource>
|
||||||
Text="{Binding RenderBackendText}" />
|
<ui:SettingsExpanderItem>
|
||||||
</StackPanel>
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
</StackPanel>
|
<TextBlock Text="{Binding VersionLabel}"
|
||||||
</controls:SettingsSectionCard.CardContent>
|
VerticalAlignment="Center" />
|
||||||
</controls:SettingsSectionCard>
|
<TextBlock Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Opacity="0.82"
|
||||||
|
Text="{Binding VersionText}" />
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Text="{Binding RenderBackendLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Opacity="0.82"
|
||||||
|
Text="{Binding RenderBackendText}" />
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<ui:SettingsExpander Classes="settings-expander-card"
|
<Separator Classes="settings-separator" />
|
||||||
Header="{Binding UpdateHeader}"
|
|
||||||
|
<!-- 更新设置分组 -->
|
||||||
|
<controls:IconText Icon="ArrowSync"
|
||||||
|
Text="{Binding UpdateHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding UpdateHeader}"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
<StackPanel Spacing="12">
|
<ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsOptionCard IconKey="ArrowSync"
|
<fi:SymbolIconSource Symbol="ArrowSync" />
|
||||||
Title="{Binding AutoCheckUpdatesLabel}">
|
</ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsOptionCard.ActionContent>
|
<ui:SettingsExpanderItem>
|
||||||
<ToggleSwitch IsChecked="{Binding AutoCheckUpdates}" />
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
</controls:SettingsOptionCard.ActionContent>
|
<TextBlock Text="{Binding AutoCheckUpdatesLabel}"
|
||||||
</controls:SettingsOptionCard>
|
VerticalAlignment="Center" />
|
||||||
|
<ToggleSwitch Grid.Column="1"
|
||||||
<controls:SettingsOptionCard IconKey="ArrowSync"
|
HorizontalAlignment="Right"
|
||||||
Title="{Binding IncludePrereleaseUpdatesLabel}">
|
IsChecked="{Binding AutoCheckUpdates}" />
|
||||||
<controls:SettingsOptionCard.ActionContent>
|
</Grid>
|
||||||
<ToggleSwitch IsChecked="{Binding IncludePrereleaseUpdates}" />
|
</ui:SettingsExpanderItem>
|
||||||
</controls:SettingsOptionCard.ActionContent>
|
<ui:SettingsExpanderItem>
|
||||||
</controls:SettingsOptionCard>
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Text="{Binding IncludePrereleaseUpdatesLabel}"
|
||||||
<controls:SettingsOptionCard IconKey="ArrowSync"
|
VerticalAlignment="Center" />
|
||||||
Title="{Binding UpdateChannelLabel}">
|
<ToggleSwitch Grid.Column="1"
|
||||||
<controls:SettingsOptionCard.DetailsContent>
|
HorizontalAlignment="Right"
|
||||||
<ComboBox ItemsSource="{Binding UpdateChannels}"
|
IsChecked="{Binding IncludePrereleaseUpdates}" />
|
||||||
SelectedItem="{Binding SelectedUpdateChannel}">
|
</Grid>
|
||||||
<ComboBox.ItemTemplate>
|
</ui:SettingsExpanderItem>
|
||||||
<DataTemplate x:DataType="vm:SelectionOption">
|
<ui:SettingsExpanderItem>
|
||||||
<TextBlock Text="{Binding Label}" />
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
</DataTemplate>
|
<TextBlock Text="{Binding UpdateChannelLabel}"
|
||||||
</ComboBox.ItemTemplate>
|
VerticalAlignment="Center" />
|
||||||
</ComboBox>
|
<ComboBox Grid.Column="1"
|
||||||
</controls:SettingsOptionCard.DetailsContent>
|
MinWidth="160"
|
||||||
</controls:SettingsOptionCard>
|
HorizontalAlignment="Right"
|
||||||
|
ItemsSource="{Binding UpdateChannels}"
|
||||||
<StackPanel Orientation="Horizontal"
|
SelectedItem="{Binding SelectedUpdateChannel}">
|
||||||
Spacing="12">
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<StackPanel Spacing="8">
|
||||||
<Button Command="{Binding CheckForUpdatesCommand}"
|
<Button Command="{Binding CheckForUpdatesCommand}"
|
||||||
Content="{Binding CheckForUpdatesButtonText}" />
|
Content="{Binding CheckForUpdatesButtonText}" />
|
||||||
<TextBlock VerticalAlignment="Center"
|
<TextBlock Opacity="0.76"
|
||||||
Opacity="0.76"
|
FontSize="12"
|
||||||
|
TextWrapping="Wrap"
|
||||||
Text="{Binding UpdateStatus}" />
|
Text="{Binding UpdateStatus}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -2,96 +2,79 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
xmlns:controls="using:LanMountainDesktop.Controls"
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsPages.AppearanceSettingsPage"
|
x:Class="LanMountainDesktop.Views.SettingsPages.AppearanceSettingsPage"
|
||||||
x:DataType="vm:AppearanceSettingsPageViewModel">
|
x:DataType="vm:AppearanceSettingsPageViewModel">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Classes="settings-page-container">
|
<StackPanel Classes="settings-page-container">
|
||||||
<TextBlock Classes="settings-section-title"
|
|
||||||
Text="{Binding PageTitle}" />
|
|
||||||
<TextBlock Classes="settings-section-description"
|
|
||||||
Text="{Binding PageDescription}" />
|
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="DesignIdeas"
|
<controls:IconText Icon="Color"
|
||||||
Title="{Binding NightModeLabel}">
|
Text="{Binding ThemeHeader}"
|
||||||
<controls:SettingsOptionCard.ActionContent>
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding NightModeLabel}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="WeatherMoon" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
<ToggleSwitch IsChecked="{Binding IsNightMode}" />
|
<ToggleSwitch IsChecked="{Binding IsNightMode}" />
|
||||||
</controls:SettingsOptionCard.ActionContent>
|
</ui:SettingsExpander.Footer>
|
||||||
</controls:SettingsOptionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="DesignIdeas"
|
<ui:SettingsExpander Header="{Binding UseSystemChromeLabel}">
|
||||||
Title="{Binding UseSystemChromeLabel}">
|
<ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsOptionCard.ActionContent>
|
<fi:SymbolIconSource Symbol="Window" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
<ToggleSwitch IsChecked="{Binding UseSystemChrome}" />
|
<ToggleSwitch IsChecked="{Binding UseSystemChrome}" />
|
||||||
</controls:SettingsOptionCard.ActionContent>
|
</ui:SettingsExpander.Footer>
|
||||||
</controls:SettingsOptionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="DesignIdeas"
|
<ui:SettingsExpander Header="{Binding ThemeColorLabel}">
|
||||||
Title="{Binding ThemeColorLabel}">
|
<ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsOptionCard.DetailsContent>
|
<fi:SymbolIconSource Symbol="Color" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
<TextBox Watermark="#AABBCC"
|
<TextBox Watermark="#AABBCC"
|
||||||
|
MinWidth="160"
|
||||||
Text="{Binding ThemeColor}" />
|
Text="{Binding ThemeColor}" />
|
||||||
</controls:SettingsOptionCard.DetailsContent>
|
</ui:SettingsExpander.Footer>
|
||||||
</controls:SettingsOptionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsSectionCard IconKey="DesignIdeas"
|
<Separator Classes="settings-separator" />
|
||||||
Title="{Binding WallpaperHeader}">
|
|
||||||
<controls:SettingsSectionCard.CardContent>
|
|
||||||
<StackPanel Spacing="14">
|
|
||||||
<StackPanel Classes="settings-item">
|
|
||||||
<TextBlock Classes="settings-item-label"
|
|
||||||
Text="{Binding WallpaperPathLabel}" />
|
|
||||||
<TextBox IsReadOnly="True"
|
|
||||||
Text="{Binding WallpaperPath}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<StackPanel Classes="settings-item">
|
<controls:IconText Icon="Clock"
|
||||||
<TextBlock Classes="settings-item-label"
|
Text="{Binding ClockHeader}"
|
||||||
Text="{Binding WallpaperPlacementLabel}" />
|
Margin="0,0,0,4" />
|
||||||
<ComboBox ItemsSource="{Binding WallpaperPlacements}"
|
|
||||||
SelectedItem="{Binding SelectedWallpaperPlacement}">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:SelectionOption">
|
|
||||||
<TextBlock Text="{Binding Label}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ComboBox.ItemTemplate>
|
|
||||||
</ComboBox>
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Button HorizontalAlignment="Left"
|
<ui:SettingsExpander Header="{Binding ClockHeader}"
|
||||||
Click="OnBrowseWallpaperClick"
|
Description="{Binding ClockDescription}">
|
||||||
Content="{Binding ImportWallpaperButtonText}" />
|
<ui:SettingsExpander.IconSource>
|
||||||
</StackPanel>
|
<fi:SymbolIconSource Symbol="Clock" />
|
||||||
</controls:SettingsSectionCard.CardContent>
|
</ui:SettingsExpander.IconSource>
|
||||||
</controls:SettingsSectionCard>
|
<ui:SettingsExpander.Footer>
|
||||||
|
<ToggleSwitch IsChecked="{Binding ShowClock}" />
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Text="{Binding ClockFormatLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
MinWidth="160"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
ItemsSource="{Binding ClockFormats}"
|
||||||
|
SelectedItem="{Binding SelectedClockFormat}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsSectionCard IconKey="DesignIdeas"
|
|
||||||
Title="{Binding ClockHeader}"
|
|
||||||
Description="{Binding ClockDescription}">
|
|
||||||
<controls:SettingsSectionCard.CardContent>
|
|
||||||
<StackPanel Spacing="14">
|
|
||||||
<controls:SettingsOptionCard IconKey="DesignIdeas"
|
|
||||||
Title="{Binding ClockHeader}">
|
|
||||||
<controls:SettingsOptionCard.ActionContent>
|
|
||||||
<ToggleSwitch IsChecked="{Binding ShowClock}" />
|
|
||||||
</controls:SettingsOptionCard.ActionContent>
|
|
||||||
</controls:SettingsOptionCard>
|
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="DesignIdeas"
|
|
||||||
Title="{Binding ClockFormatLabel}">
|
|
||||||
<controls:SettingsOptionCard.DetailsContent>
|
|
||||||
<ComboBox ItemsSource="{Binding ClockFormats}"
|
|
||||||
SelectedItem="{Binding SelectedClockFormat}">
|
|
||||||
<ComboBox.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:SelectionOption">
|
|
||||||
<TextBlock Text="{Binding Label}" />
|
|
||||||
</DataTemplate>
|
|
||||||
</ComboBox.ItemTemplate>
|
|
||||||
</ComboBox>
|
|
||||||
</controls:SettingsOptionCard.DetailsContent>
|
|
||||||
</controls:SettingsOptionCard>
|
|
||||||
</StackPanel>
|
|
||||||
</controls:SettingsSectionCard.CardContent>
|
|
||||||
</controls:SettingsSectionCard>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
|
||||||
using Avalonia.Platform.Storage;
|
|
||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services.Settings;
|
using LanMountainDesktop.Services.Settings;
|
||||||
using LanMountainDesktop.ViewModels;
|
using LanMountainDesktop.ViewModels;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace LanMountainDesktop.Views.SettingsPages;
|
namespace LanMountainDesktop.Views.SettingsPages;
|
||||||
|
|
||||||
@@ -30,25 +27,4 @@ public partial class AppearanceSettingsPage : SettingsPageBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AppearanceSettingsPageViewModel ViewModel { get; }
|
public AppearanceSettingsPageViewModel ViewModel { get; }
|
||||||
|
|
||||||
private async void OnBrowseWallpaperClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (TopLevel.GetTopLevel(this)?.StorageProvider is not { } storageProvider)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
|
||||||
{
|
|
||||||
Title = ViewModel.FilePickerTitle,
|
|
||||||
AllowMultiple = false
|
|
||||||
});
|
|
||||||
|
|
||||||
var file = files.FirstOrDefault();
|
|
||||||
var localPath = file?.TryGetLocalPath();
|
|
||||||
if (!string.IsNullOrWhiteSpace(localPath))
|
|
||||||
{
|
|
||||||
await ViewModel.ImportWallpaperAsync(localPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,56 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsPages.ComponentsSettingsPage"
|
x:Class="LanMountainDesktop.Views.SettingsPages.ComponentsSettingsPage"
|
||||||
x:DataType="vm:ComponentsSettingsPageViewModel">
|
x:DataType="vm:ComponentsSettingsPageViewModel">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Classes="settings-page-container">
|
<StackPanel Classes="settings-page-container">
|
||||||
<TextBlock Classes="settings-section-title"
|
|
||||||
Text="{Binding PageTitle}" />
|
|
||||||
<TextBlock Classes="settings-section-description"
|
|
||||||
Text="{Binding PageDescription}" />
|
|
||||||
|
|
||||||
<ui:SettingsExpander Classes="settings-expander-card"
|
<!-- 网格布局设置分组 -->
|
||||||
Header="{Binding GridHeader}"
|
<controls:IconText Icon="GridDots"
|
||||||
|
Text="{Binding GridHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding GridHeader}"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
<StackPanel Spacing="14">
|
<ui:SettingsExpander.IconSource>
|
||||||
<StackPanel Classes="settings-item">
|
<fi:SymbolIconSource Symbol="GridDots" />
|
||||||
<TextBlock Classes="settings-item-label"
|
</ui:SettingsExpander.IconSource>
|
||||||
Text="{Binding ShortSideCellsLabel}" />
|
<ui:SettingsExpanderItem>
|
||||||
<NumericUpDown Minimum="6"
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
|
<TextBlock Text="{Binding ShortSideCellsLabel}"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<NumericUpDown Grid.Column="1"
|
||||||
|
MinWidth="120"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Minimum="6"
|
||||||
Maximum="96"
|
Maximum="96"
|
||||||
Value="{Binding ShortSideCells}" />
|
Value="{Binding ShortSideCells}" />
|
||||||
</StackPanel>
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
<StackPanel Classes="settings-item">
|
<ui:SettingsExpanderItem>
|
||||||
<TextBlock Classes="settings-item-label"
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
Text="{Binding EdgeInsetPercentLabel}" />
|
<TextBlock Text="{Binding EdgeInsetPercentLabel}"
|
||||||
<NumericUpDown Minimum="0"
|
VerticalAlignment="Center" />
|
||||||
|
<NumericUpDown Grid.Column="1"
|
||||||
|
MinWidth="120"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Minimum="0"
|
||||||
Maximum="30"
|
Maximum="30"
|
||||||
Value="{Binding EdgeInsetPercent}" />
|
Value="{Binding EdgeInsetPercent}" />
|
||||||
</StackPanel>
|
</Grid>
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
<StackPanel Classes="settings-item">
|
<ui:SettingsExpanderItem>
|
||||||
<TextBlock Classes="settings-item-label"
|
<Grid ColumnDefinitions="Auto,*">
|
||||||
Text="{Binding SpacingPresetLabel}" />
|
<TextBlock Text="{Binding SpacingPresetLabel}"
|
||||||
<ComboBox ItemsSource="{Binding SpacingPresets}"
|
VerticalAlignment="Center" />
|
||||||
|
<ComboBox Grid.Column="1"
|
||||||
|
MinWidth="160"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
ItemsSource="{Binding SpacingPresets}"
|
||||||
SelectedItem="{Binding SelectedSpacingPreset}">
|
SelectedItem="{Binding SelectedSpacingPreset}">
|
||||||
<ComboBox.ItemTemplate>
|
<ComboBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:SelectionOption">
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
@@ -42,9 +58,10 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</StackPanel>
|
</Grid>
|
||||||
</StackPanel>
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -3,18 +3,22 @@
|
|||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
xmlns:controls="using:LanMountainDesktop.Controls"
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsPages.GeneralSettingsPage"
|
x:Class="LanMountainDesktop.Views.SettingsPages.GeneralSettingsPage"
|
||||||
x:DataType="vm:GeneralSettingsPageViewModel">
|
x:DataType="vm:GeneralSettingsPageViewModel">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Classes="settings-page-container">
|
<StackPanel Classes="settings-page-container">
|
||||||
<TextBlock Classes="settings-section-title"
|
|
||||||
Text="{Binding PageTitle}" />
|
|
||||||
<TextBlock Classes="settings-section-description"
|
|
||||||
Text="{Binding PageDescription}" />
|
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="Settings"
|
<!-- 区域设置分组 -->
|
||||||
Title="{Binding LanguageHeader}">
|
<controls:IconText Icon="Globe"
|
||||||
<controls:SettingsOptionCard.DetailsContent>
|
Text="{Binding BasicHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding LanguageHeader}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="Settings" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
<ComboBox MinWidth="240"
|
<ComboBox MinWidth="240"
|
||||||
ItemsSource="{Binding Languages}"
|
ItemsSource="{Binding Languages}"
|
||||||
SelectedItem="{Binding SelectedLanguage}">
|
SelectedItem="{Binding SelectedLanguage}">
|
||||||
@@ -24,13 +28,15 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</controls:SettingsOptionCard.DetailsContent>
|
</ui:SettingsExpander.Footer>
|
||||||
</controls:SettingsOptionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsOptionCard IconKey="Settings"
|
<ui:SettingsExpander Header="{Binding TimeZoneHeader}"
|
||||||
Title="{Binding TimeZoneHeader}"
|
Description="{Binding TimeZoneDescription}">
|
||||||
Description="{Binding TimeZoneDescription}">
|
<ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsOptionCard.DetailsContent>
|
<fi:SymbolIconSource Symbol="Clock" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
<ComboBox MinWidth="280"
|
<ComboBox MinWidth="280"
|
||||||
ItemsSource="{Binding TimeZones}"
|
ItemsSource="{Binding TimeZones}"
|
||||||
SelectedItem="{Binding SelectedTimeZone}">
|
SelectedItem="{Binding SelectedTimeZone}">
|
||||||
@@ -40,12 +46,14 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</controls:SettingsOptionCard.DetailsContent>
|
</ui:SettingsExpander.Footer>
|
||||||
</controls:SettingsOptionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<controls:SettingsSectionCard IconKey="Info"
|
<ui:SettingsExpander Header="{Binding PreviewHeader}">
|
||||||
Title="{Binding PreviewHeader}">
|
<ui:SettingsExpander.IconSource>
|
||||||
<controls:SettingsSectionCard.CardContent>
|
<fi:SymbolIconSource Symbol="Calendar" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
<Grid ColumnDefinitions="Auto,*"
|
<Grid ColumnDefinitions="Auto,*"
|
||||||
ColumnSpacing="16"
|
ColumnSpacing="16"
|
||||||
RowDefinitions="Auto,Auto"
|
RowDefinitions="Auto,Auto"
|
||||||
@@ -63,13 +71,22 @@
|
|||||||
Opacity="0.82"
|
Opacity="0.82"
|
||||||
Text="{Binding PreviewDateText}" />
|
Text="{Binding PreviewDateText}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</controls:SettingsSectionCard.CardContent>
|
</ui:SettingsExpanderItem>
|
||||||
</controls:SettingsSectionCard>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<ui:SettingsExpander Classes="settings-expander-card"
|
<Separator Classes="settings-separator" />
|
||||||
Header="{Binding RuntimeHeader}"
|
|
||||||
|
<!-- 运行时设置分组 -->
|
||||||
|
<controls:IconText Icon="DeveloperBoard"
|
||||||
|
Text="{Binding RuntimeHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding RenderModeHeader}"
|
||||||
Description="{Binding RuntimeDescription}"
|
Description="{Binding RuntimeDescription}"
|
||||||
IsExpanded="True">
|
IsExpanded="True">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="DeveloperBoard" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
<ui:SettingsExpander.Footer>
|
<ui:SettingsExpander.Footer>
|
||||||
<ComboBox MinWidth="240"
|
<ComboBox MinWidth="240"
|
||||||
ItemsSource="{Binding RenderModes}"
|
ItemsSource="{Binding RenderModes}"
|
||||||
@@ -81,7 +98,14 @@
|
|||||||
</ComboBox.ItemTemplate>
|
</ComboBox.ItemTemplate>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</ui:SettingsExpander.Footer>
|
</ui:SettingsExpander.Footer>
|
||||||
|
<ui:SettingsExpanderItem>
|
||||||
|
<TextBlock Text="{Binding RenderModeRestartMessage}"
|
||||||
|
Opacity="0.7"
|
||||||
|
FontSize="12"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
</ui:SettingsExpanderItem>
|
||||||
</ui:SettingsExpander>
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,87 +1,92 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
x:Class="LanMountainDesktop.Views.SettingsPages.PluginsSettingsPage"
|
x:Class="LanMountainDesktop.Views.SettingsPages.PluginsSettingsPage"
|
||||||
x:Name="Root"
|
x:Name="Root"
|
||||||
x:DataType="vm:PluginsSettingsPageViewModel">
|
x:DataType="vm:PluginsSettingsPageViewModel">
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Classes="settings-page-container">
|
<StackPanel Classes="settings-page-container">
|
||||||
<TextBlock Classes="settings-section-title"
|
|
||||||
Text="{Binding PageTitle}" />
|
|
||||||
<TextBlock Classes="settings-section-description"
|
|
||||||
Text="{Binding PageDescription}" />
|
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal"
|
<!-- 刷新按钮和状态 -->
|
||||||
Spacing="12"
|
<ui:SettingsExpander Header="{Binding RefreshButtonText}"
|
||||||
Margin="0,0,0,18">
|
Description="{Binding StatusMessage}">
|
||||||
<Button Command="{Binding RefreshCommand}"
|
<ui:SettingsExpander.IconSource>
|
||||||
Content="{Binding RefreshButtonText}" />
|
<fi:SymbolIconSource Symbol="ArrowSync" />
|
||||||
<TextBlock VerticalAlignment="Center"
|
</ui:SettingsExpander.IconSource>
|
||||||
Opacity="0.76"
|
<ui:SettingsExpander.Footer>
|
||||||
Text="{Binding StatusMessage}" />
|
<Button Command="{Binding RefreshCommand}"
|
||||||
</StackPanel>
|
Content="{Binding RefreshButtonText}" />
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
<TextBlock Classes="settings-subsection-title"
|
<Separator Classes="settings-separator" />
|
||||||
Text="{Binding InstalledHeader}" />
|
|
||||||
|
<!-- 已安装插件分组 -->
|
||||||
|
<controls:IconText Icon="PuzzleCube"
|
||||||
|
Text="{Binding InstalledHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding InstalledPlugins}">
|
<ItemsControl ItemsSource="{Binding InstalledPlugins}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:InstalledPluginItemViewModel">
|
<DataTemplate x:DataType="vm:InstalledPluginItemViewModel">
|
||||||
<Border Classes="settings-list-item">
|
<ui:SettingsExpander>
|
||||||
<Grid Classes="settings-list-actions"
|
<ui:SettingsExpander.IconSource>
|
||||||
ColumnDefinitions="*,Auto,Auto">
|
<fi:SymbolIconSource Symbol="PuzzleCube" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontWeight="SemiBold"
|
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
|
||||||
Text="{Binding Name}" />
|
<TextBlock Opacity="0.76" FontSize="12" Text="{Binding Description}" />
|
||||||
<TextBlock Opacity="0.76"
|
|
||||||
FontSize="12"
|
|
||||||
Text="{Binding Description}" />
|
|
||||||
<TextBlock Opacity="0.58"
|
|
||||||
FontSize="11"
|
|
||||||
Text="{Binding Version}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ToggleSwitch Grid.Column="1"
|
</ui:SettingsExpander.Header>
|
||||||
IsChecked="{Binding IsEnabled}"
|
<ui:SettingsExpander.Footer>
|
||||||
Command="{Binding #Root.DataContext.TogglePluginCommand}"
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
CommandParameter="{Binding}" />
|
<ToggleSwitch IsChecked="{Binding IsEnabled}"
|
||||||
<Button Grid.Column="2"
|
Command="{Binding #Root.DataContext.TogglePluginCommand}"
|
||||||
Command="{Binding #Root.DataContext.DeletePluginCommand}"
|
CommandParameter="{Binding}" />
|
||||||
CommandParameter="{Binding}"
|
<Button Command="{Binding #Root.DataContext.DeletePluginCommand}"
|
||||||
Content="{Binding #Root.DataContext.DeleteButtonText}" />
|
CommandParameter="{Binding}"
|
||||||
</Grid>
|
Content="{Binding #Root.DataContext.DeleteButtonText}" />
|
||||||
</Border>
|
</StackPanel>
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
</ui:SettingsExpander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
<TextBlock Classes="settings-subsection-title"
|
<Separator Classes="settings-separator" />
|
||||||
Text="{Binding MarketplaceHeader}" />
|
|
||||||
|
<!-- 市场插件分组 -->
|
||||||
|
<controls:IconText Icon="ShoppingBag"
|
||||||
|
Text="{Binding MarketplaceHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
<ItemsControl ItemsSource="{Binding MarketPlugins}">
|
<ItemsControl ItemsSource="{Binding MarketPlugins}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:PluginMarketItemViewModel">
|
<DataTemplate x:DataType="vm:PluginMarketItemViewModel">
|
||||||
<Border Classes="settings-list-item">
|
<ui:SettingsExpander>
|
||||||
<Grid Classes="settings-list-actions"
|
<ui:SettingsExpander.IconSource>
|
||||||
ColumnDefinitions="*,Auto">
|
<fi:SymbolIconSource Symbol="ShoppingBag" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Header>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock FontWeight="SemiBold"
|
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
|
||||||
Text="{Binding Name}" />
|
<TextBlock Opacity="0.76" FontSize="12" Text="{Binding Description}" />
|
||||||
<TextBlock Opacity="0.76"
|
|
||||||
FontSize="12"
|
|
||||||
Text="{Binding Description}" />
|
|
||||||
<TextBlock Opacity="0.58"
|
|
||||||
FontSize="11"
|
|
||||||
Text="{Binding Version}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Button Grid.Column="1"
|
</ui:SettingsExpander.Header>
|
||||||
Command="{Binding #Root.DataContext.InstallPluginCommand}"
|
<ui:SettingsExpander.Footer>
|
||||||
|
<Button Command="{Binding #Root.DataContext.InstallPluginCommand}"
|
||||||
CommandParameter="{Binding}"
|
CommandParameter="{Binding}"
|
||||||
Content="{Binding #Root.DataContext.InstallButtonText}" />
|
Content="{Binding #Root.DataContext.InstallButtonText}" />
|
||||||
</Grid>
|
</ui:SettingsExpander.Footer>
|
||||||
</Border>
|
</ui:SettingsExpander>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:LanMountainDesktop.ViewModels"
|
||||||
|
xmlns:controls="using:LanMountainDesktop.Controls"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:fi="using:FluentIcons.Avalonia.Fluent"
|
||||||
|
x:Class="LanMountainDesktop.Views.SettingsPages.WallpaperSettingsPage"
|
||||||
|
x:DataType="vm:WallpaperSettingsPageViewModel">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Classes="settings-page-container">
|
||||||
|
|
||||||
|
<controls:IconText Icon="Image"
|
||||||
|
Text="{Binding WallpaperHeader}"
|
||||||
|
Margin="0,0,0,4" />
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding WallpaperPathLabel}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="Image" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBox IsReadOnly="True"
|
||||||
|
MinWidth="200"
|
||||||
|
Text="{Binding WallpaperPath}" />
|
||||||
|
<Button Click="OnBrowseWallpaperClick"
|
||||||
|
Content="{Binding ImportWallpaperButtonText}" />
|
||||||
|
</StackPanel>
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
<ui:SettingsExpander Header="{Binding WallpaperPlacementLabel}"
|
||||||
|
Description="{Binding WallpaperPlacementDescription}">
|
||||||
|
<ui:SettingsExpander.IconSource>
|
||||||
|
<fi:SymbolIconSource Symbol="Maximize" />
|
||||||
|
</ui:SettingsExpander.IconSource>
|
||||||
|
<ui:SettingsExpander.Footer>
|
||||||
|
<ComboBox MinWidth="160"
|
||||||
|
ItemsSource="{Binding WallpaperPlacements}"
|
||||||
|
SelectedItem="{Binding SelectedWallpaperPlacement}">
|
||||||
|
<ComboBox.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:SelectionOption">
|
||||||
|
<TextBlock Text="{Binding Label}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ComboBox.ItemTemplate>
|
||||||
|
</ComboBox>
|
||||||
|
</ui:SettingsExpander.Footer>
|
||||||
|
</ui:SettingsExpander>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using LanMountainDesktop.PluginSdk;
|
||||||
|
using LanMountainDesktop.Services.Settings;
|
||||||
|
using LanMountainDesktop.ViewModels;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace LanMountainDesktop.Views.SettingsPages;
|
||||||
|
|
||||||
|
[SettingsPageInfo(
|
||||||
|
"wallpaper",
|
||||||
|
"Wallpaper",
|
||||||
|
SettingsPageCategory.Appearance,
|
||||||
|
IconKey = "Image",
|
||||||
|
SortOrder = 15,
|
||||||
|
TitleLocalizationKey = "settings.wallpaper.title",
|
||||||
|
DescriptionLocalizationKey = "settings.wallpaper.description")]
|
||||||
|
public partial class WallpaperSettingsPage : SettingsPageBase
|
||||||
|
{
|
||||||
|
public WallpaperSettingsPage()
|
||||||
|
: this(new WallpaperSettingsPageViewModel(HostSettingsFacadeProvider.GetOrCreate()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public WallpaperSettingsPage(WallpaperSettingsPageViewModel viewModel)
|
||||||
|
{
|
||||||
|
ViewModel = viewModel;
|
||||||
|
DataContext = ViewModel;
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WallpaperSettingsPageViewModel ViewModel { get; }
|
||||||
|
|
||||||
|
private async void OnBrowseWallpaperClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (TopLevel.GetTopLevel(this)?.StorageProvider is not { } storageProvider)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = ViewModel.FilePickerTitle,
|
||||||
|
AllowMultiple = false
|
||||||
|
});
|
||||||
|
|
||||||
|
var file = files.FirstOrDefault();
|
||||||
|
var localPath = file?.TryGetLocalPath();
|
||||||
|
if (!string.IsNullOrWhiteSpace(localPath))
|
||||||
|
{
|
||||||
|
await ViewModel.ImportWallpaperAsync(localPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
x:DataType="vm:SettingsWindowViewModel"
|
x:DataType="vm:SettingsWindowViewModel"
|
||||||
Width="1120"
|
Width="1120"
|
||||||
Height="760"
|
Height="760"
|
||||||
MinWidth="920"
|
MinWidth="560"
|
||||||
MinHeight="620"
|
MinHeight="480"
|
||||||
CanResize="True"
|
CanResize="True"
|
||||||
WindowStartupLocation="Manual"
|
WindowStartupLocation="Manual"
|
||||||
SystemDecorations="BorderOnly"
|
SystemDecorations="BorderOnly"
|
||||||
@@ -17,7 +17,29 @@
|
|||||||
Icon="/Assets/avalonia-logo.ico"
|
Icon="/Assets/avalonia-logo.ico"
|
||||||
Title="{Binding Title}">
|
Title="{Binding Title}">
|
||||||
|
|
||||||
<Grid Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
|
<Window.Resources>
|
||||||
|
<x:Double x:Key="SettingsPageContentWidth">920</x:Double>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Window.Styles>
|
||||||
|
<Style Selector="Grid.page-title-container">
|
||||||
|
<Setter Property="Margin" Value="0,16,0,0" />
|
||||||
|
<Setter Property="Width" Value="{DynamicResource SettingsPageContentWidth}" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock.page-title-text">
|
||||||
|
<Setter Property="FontSize" Value="28" />
|
||||||
|
<Setter Property="FontWeight" Value="Normal" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="TextBlock.page-title-text:narrow">
|
||||||
|
<Setter Property="FontSize" Value="24" />
|
||||||
|
</Style>
|
||||||
|
</Window.Styles>
|
||||||
|
|
||||||
|
<Grid Classes="settings-scope"
|
||||||
|
Background="{DynamicResource AdaptiveSurfaceBaseBrush}"
|
||||||
RowDefinitions="Auto,Auto,*">
|
RowDefinitions="Auto,Auto,*">
|
||||||
<Border x:Name="WindowTitleBarHost"
|
<Border x:Name="WindowTitleBarHost"
|
||||||
Height="48"
|
Height="48"
|
||||||
@@ -121,10 +143,24 @@
|
|||||||
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
|
<SolidColorBrush x:Key="NavigationViewContentGridBorderBrush" Color="Transparent" />
|
||||||
</ui:NavigationView.Resources>
|
</ui:NavigationView.Resources>
|
||||||
|
|
||||||
<Grid ColumnDefinitions="*,Auto"
|
<Grid x:Name="SettingsContentGrid"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
ColumnSpacing="20"
|
ColumnSpacing="20"
|
||||||
Margin="12,0,16,16">
|
Margin="12,0,16,16">
|
||||||
<ui:Frame x:Name="ContentFrame" />
|
<Grid Grid.Column="0"
|
||||||
|
RowDefinitions="Auto,*">
|
||||||
|
<Grid x:Name="PageTitleContainer"
|
||||||
|
Grid.Row="0"
|
||||||
|
Classes="page-title-container"
|
||||||
|
IsVisible="{Binding IsPageTitleVisible}">
|
||||||
|
<TextBlock x:Name="PageTitleTextBlock"
|
||||||
|
Classes="page-title-text"
|
||||||
|
Text="{Binding CurrentPageTitle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<ui:Frame x:Name="ContentFrame"
|
||||||
|
Grid.Row="1" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Border x:Name="DrawerBorder"
|
<Border x:Name="DrawerBorder"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using LanMountainDesktop.PluginSdk;
|
using LanMountainDesktop.PluginSdk;
|
||||||
using LanMountainDesktop.Services;
|
using LanMountainDesktop.Services;
|
||||||
@@ -17,10 +18,17 @@ namespace LanMountainDesktop.Views;
|
|||||||
|
|
||||||
public partial class SettingsWindow : Window, ISettingsPageHostContext
|
public partial class SettingsWindow : Window, ISettingsPageHostContext
|
||||||
{
|
{
|
||||||
|
private const double BaseSettingsContentWidth = 960d;
|
||||||
|
private const double MinSettingsContentWidth = 320d;
|
||||||
|
private const double MaxSettingsContentWidth = 1280d;
|
||||||
|
private const double BaseDrawerWidth = 296d;
|
||||||
|
private const double BaseNarrowThreshold = 800d;
|
||||||
|
|
||||||
private readonly ISettingsPageRegistry _pageRegistry;
|
private readonly ISettingsPageRegistry _pageRegistry;
|
||||||
private readonly IHostApplicationLifecycle _hostApplicationLifecycle;
|
private readonly IHostApplicationLifecycle _hostApplicationLifecycle;
|
||||||
private readonly Dictionary<string, Control> _cachedPages = new(StringComparer.OrdinalIgnoreCase);
|
private readonly Dictionary<string, Control> _cachedPages = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly bool _useSystemChrome;
|
private readonly bool _useSystemChrome;
|
||||||
|
private bool _isResponsiveLayoutRefreshPending;
|
||||||
|
|
||||||
public SettingsWindow()
|
public SettingsWindow()
|
||||||
: this(
|
: this(
|
||||||
@@ -44,6 +52,11 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ApplyChromeMode(useSystemChrome);
|
ApplyChromeMode(useSystemChrome);
|
||||||
|
|
||||||
|
if (RootNavigationView is not null)
|
||||||
|
{
|
||||||
|
RootNavigationView.PropertyChanged += OnRootNavigationViewPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
Opened += OnOpened;
|
Opened += OnOpened;
|
||||||
SizeChanged += OnWindowSizeChanged;
|
SizeChanged += OnWindowSizeChanged;
|
||||||
Closed += OnClosed;
|
Closed += OnClosed;
|
||||||
@@ -59,6 +72,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
SyncTitleText();
|
SyncTitleText();
|
||||||
UpdateChromeMetrics();
|
UpdateChromeMetrics();
|
||||||
UpdatePaneToggleIcon();
|
UpdatePaneToggleIcon();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadPages(string? pageId)
|
public void ReloadPages(string? pageId)
|
||||||
@@ -86,6 +101,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
ViewModel.DrawerTitle = title ?? ViewModel.DrawerFallbackTitle;
|
ViewModel.DrawerTitle = title ?? ViewModel.DrawerFallbackTitle;
|
||||||
ViewModel.IsDrawerOpen = true;
|
ViewModel.IsDrawerOpen = true;
|
||||||
SyncTitleText();
|
SyncTitleText();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CloseDrawer()
|
public void CloseDrawer()
|
||||||
@@ -98,6 +115,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
ViewModel.IsDrawerOpen = false;
|
ViewModel.IsDrawerOpen = false;
|
||||||
ViewModel.DrawerTitle = null;
|
ViewModel.DrawerTitle = null;
|
||||||
SyncTitleText();
|
SyncTitleText();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RequestRestart(string? reason = null)
|
public void RequestRestart(string? reason = null)
|
||||||
@@ -285,6 +304,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
_ = sender;
|
_ = sender;
|
||||||
_ = e;
|
_ = e;
|
||||||
UpdateChromeMetrics();
|
UpdateChromeMetrics();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWindowSizeChanged(object? sender, SizeChangedEventArgs e)
|
private void OnWindowSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||||
@@ -292,12 +313,139 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
_ = sender;
|
_ = sender;
|
||||||
_ = e;
|
_ = e;
|
||||||
UpdateChromeMetrics();
|
UpdateChromeMetrics();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryApplyResponsiveLayout()
|
||||||
|
{
|
||||||
|
if (SettingsContentGrid is null || DrawerBorder is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var width = Bounds.Width > 1 ? Bounds.Width : Math.Max(Width, MinWidth);
|
||||||
|
var renderScale = RenderScaling > 0 ? RenderScaling : 1d;
|
||||||
|
var titleScale = WindowTitleTextBlock?.FontSize > 0
|
||||||
|
? WindowTitleTextBlock.FontSize / 12d
|
||||||
|
: 1d;
|
||||||
|
var pageTitleScale = PageTitleTextBlock?.FontSize > 0
|
||||||
|
? PageTitleTextBlock.FontSize / 28d
|
||||||
|
: 1d;
|
||||||
|
var typographyScale = Math.Max(titleScale, pageTitleScale);
|
||||||
|
var contentScale = Math.Clamp(
|
||||||
|
1d + ((renderScale - 1d) * 0.7d) + ((typographyScale - 1d) * 0.45d),
|
||||||
|
1d,
|
||||||
|
1.45d);
|
||||||
|
|
||||||
|
var horizontalMargin = Math.Clamp(16d * renderScale, 12d, 32d);
|
||||||
|
var topMargin = Math.Clamp(2d * renderScale, 0d, 8d);
|
||||||
|
var bottomMargin = Math.Clamp(16d * renderScale, 12d, 28d);
|
||||||
|
var columnSpacing = Math.Clamp(20d * renderScale, 12d, 28d);
|
||||||
|
var drawerWidth = Math.Clamp(BaseDrawerWidth * contentScale, 276d, 380d);
|
||||||
|
var compactPaneWidth = Math.Clamp(48d * renderScale, 40d, 60d);
|
||||||
|
var narrowThreshold = Math.Clamp(BaseNarrowThreshold * renderScale, 760d, 980d);
|
||||||
|
var isNarrow = width < narrowThreshold;
|
||||||
|
var paneReservedWidth = GetReservedPaneWidth(compactPaneWidth, isNarrow);
|
||||||
|
|
||||||
|
SettingsContentGrid.Margin = new Thickness(horizontalMargin, topMargin, horizontalMargin, bottomMargin);
|
||||||
|
DrawerBorder.Width = drawerWidth;
|
||||||
|
|
||||||
|
if (isNarrow)
|
||||||
|
{
|
||||||
|
SettingsContentGrid.ColumnDefinitions = new ColumnDefinitions("*");
|
||||||
|
SettingsContentGrid.ColumnSpacing = 0;
|
||||||
|
if (DrawerBorder.IsVisible)
|
||||||
|
{
|
||||||
|
ViewModel.IsDrawerOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerBorder.IsVisible = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SettingsContentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto");
|
||||||
|
SettingsContentGrid.ColumnSpacing = columnSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentHostWidth = ContentFrame?.Bounds.Width > 1
|
||||||
|
? ContentFrame.Bounds.Width
|
||||||
|
: 0d;
|
||||||
|
if (contentHostWidth <= 1)
|
||||||
|
{
|
||||||
|
var rootContentWidth = RootNavigationView?.Bounds.Width > 1
|
||||||
|
? RootNavigationView.Bounds.Width - paneReservedWidth
|
||||||
|
: SettingsContentGrid.Bounds.Width;
|
||||||
|
contentHostWidth = rootContentWidth - (isNarrow ? 0d : drawerWidth + SettingsContentGrid.ColumnSpacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentHostWidth = Math.Max(MinSettingsContentWidth, contentHostWidth);
|
||||||
|
|
||||||
|
var edgePadding = Math.Clamp(24d * renderScale, 14d, 40d);
|
||||||
|
var preferredContentWidth = Math.Clamp(BaseSettingsContentWidth * contentScale, 820d, MaxSettingsContentWidth);
|
||||||
|
var availableContentWidth = Math.Max(MinSettingsContentWidth, contentHostWidth - edgePadding * 2d);
|
||||||
|
var resolvedContentWidth = availableContentWidth > preferredContentWidth
|
||||||
|
? preferredContentWidth
|
||||||
|
: availableContentWidth;
|
||||||
|
|
||||||
|
Resources["SettingsPageContentWidth"] = resolvedContentWidth;
|
||||||
|
|
||||||
|
if (PageTitleContainer is not null)
|
||||||
|
{
|
||||||
|
PageTitleContainer.Width = resolvedContentWidth;
|
||||||
|
PageTitleContainer.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PageTitleTextBlock is not null)
|
||||||
|
{
|
||||||
|
var narrowTitleThreshold = Math.Clamp(760d * renderScale, 700d, 860d);
|
||||||
|
PageTitleTextBlock.Classes.Set("narrow", resolvedContentWidth < narrowTitleThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateResponsiveLayout()
|
||||||
|
{
|
||||||
|
_ = TryApplyResponsiveLayout();
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (SettingsContentGrid is null || DrawerBorder is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var width = Bounds.Width;
|
||||||
|
const double narrowThreshold = 800;
|
||||||
|
|
||||||
|
var isNarrow = width < narrowThreshold;
|
||||||
|
|
||||||
|
// 小窗口时隐藏抽屉面板
|
||||||
|
if (isNarrow)
|
||||||
|
{
|
||||||
|
SettingsContentGrid.ColumnDefinitions = new ColumnDefinitions("*");
|
||||||
|
SettingsContentGrid.ColumnSpacing = 0;
|
||||||
|
if (DrawerBorder.IsVisible)
|
||||||
|
{
|
||||||
|
ViewModel.IsDrawerOpen = false;
|
||||||
|
}
|
||||||
|
DrawerBorder.IsVisible = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SettingsContentGrid.ColumnDefinitions = new ColumnDefinitions("*,Auto");
|
||||||
|
SettingsContentGrid.ColumnSpacing = 20;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(object? sender, EventArgs e)
|
private void OnClosed(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_cachedPages.Clear();
|
_cachedPages.Clear();
|
||||||
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
|
PendingRestartStateService.StateChanged -= OnPendingRestartStateChanged;
|
||||||
|
if (RootNavigationView is not null)
|
||||||
|
{
|
||||||
|
RootNavigationView.PropertyChanged -= OnRootNavigationViewPropertyChanged;
|
||||||
|
}
|
||||||
Opened -= OnOpened;
|
Opened -= OnOpened;
|
||||||
SizeChanged -= OnWindowSizeChanged;
|
SizeChanged -= OnWindowSizeChanged;
|
||||||
}
|
}
|
||||||
@@ -322,6 +470,8 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
|
|
||||||
RootNavigationView.IsPaneOpen = !RootNavigationView.IsPaneOpen;
|
RootNavigationView.IsPaneOpen = !RootNavigationView.IsPaneOpen;
|
||||||
UpdatePaneToggleIcon();
|
UpdatePaneToggleIcon();
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCloseWindowClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
private void OnCloseWindowClick(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||||
@@ -331,6 +481,46 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRootNavigationViewPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
_ = sender;
|
||||||
|
|
||||||
|
if (e.Property == NavigationView.IsPaneOpenProperty ||
|
||||||
|
e.Property == NavigationView.OpenPaneLengthProperty ||
|
||||||
|
e.Property == NavigationView.PaneDisplayModeProperty)
|
||||||
|
{
|
||||||
|
UpdatePaneToggleIcon();
|
||||||
|
RequestResponsiveLayoutRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RequestResponsiveLayoutRefresh()
|
||||||
|
{
|
||||||
|
if (_isResponsiveLayoutRefreshPending)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isResponsiveLayoutRefreshPending = true;
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
_isResponsiveLayoutRefreshPending = false;
|
||||||
|
UpdateResponsiveLayout();
|
||||||
|
}, DispatcherPriority.Background);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double GetReservedPaneWidth(double compactPaneWidth, bool isNarrow)
|
||||||
|
{
|
||||||
|
if (RootNavigationView is null || isNarrow)
|
||||||
|
{
|
||||||
|
return 0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RootNavigationView.IsPaneOpen
|
||||||
|
? RootNavigationView.OpenPaneLength
|
||||||
|
: compactPaneWidth;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdatePaneToggleIcon()
|
private void UpdatePaneToggleIcon()
|
||||||
{
|
{
|
||||||
if (TogglePaneButtonIcon is null || RootNavigationView is null)
|
if (TogglePaneButtonIcon is null || RootNavigationView is null)
|
||||||
@@ -457,6 +647,7 @@ public partial class SettingsWindow : Window, ISettingsPageHostContext
|
|||||||
return iconKey?.Trim() switch
|
return iconKey?.Trim() switch
|
||||||
{
|
{
|
||||||
"DesignIdeas" => Symbol.Color,
|
"DesignIdeas" => Symbol.Color,
|
||||||
|
"Image" => Symbol.Image,
|
||||||
"GridDots" => Symbol.GridDots,
|
"GridDots" => Symbol.GridDots,
|
||||||
"PuzzlePiece" => Symbol.PuzzlePiece,
|
"PuzzlePiece" => Symbol.PuzzlePiece,
|
||||||
"Info" => Symbol.Info,
|
"Info" => Symbol.Info,
|
||||||
|
|||||||
Reference in New Issue
Block a user