diff --git a/LanMountainDesktop/Controls/IconText.axaml b/LanMountainDesktop/Controls/IconText.axaml
index da69f8e..0b40a7e 100644
--- a/LanMountainDesktop/Controls/IconText.axaml
+++ b/LanMountainDesktop/Controls/IconText.axaml
@@ -13,7 +13,7 @@
diff --git a/LanMountainDesktop/Localization/zh-CN.json b/LanMountainDesktop/Localization/zh-CN.json
index f037590..d51ec38 100644
--- a/LanMountainDesktop/Localization/zh-CN.json
+++ b/LanMountainDesktop/Localization/zh-CN.json
@@ -32,9 +32,14 @@
"settings.wallpaper.title": "壁纸",
"settings.wallpaper.description": "选择图片或视频后可立即设为应用窗口壁纸。",
"settings.wallpaper.current_label": "当前壁纸",
+ "settings.wallpaper.type_label": "壁纸类型",
+ "settings.wallpaper.type.image": "图片",
+ "settings.wallpaper.type.video": "视频",
+ "settings.wallpaper.type.solid_color": "纯色",
+ "settings.wallpaper.color_label": "壁纸颜色",
"settings.wallpaper.placement_label": "显示方式",
"settings.wallpaper.placement_desc": "调整图像在桌面上的填充方式。",
- "settings.wallpaper.pick_button": "浏览文件",
+ "settings.wallpaper.pick_button": "选择文件",
"settings.wallpaper.clear_button": "恢复纯色",
"settings.wallpaper.no_selection": "未选择壁纸。",
"settings.wallpaper.storage_unavailable": "存储提供器不可用。",
@@ -245,8 +250,8 @@
"settings.status_bar.clock_format_label": "时钟格式",
"settings.status_bar.clock_format.hm": "时:分",
"settings.status_bar.clock_format.hms": "时:分:秒",
- "settings.components.title": "组件",
- "settings.components.description": "调整桌面网格与组件摆放密度。",
+ "settings.components.title": "网格",
+ "settings.components.description": "调整桌面网格与布局。",
"settings.components.grid_header": "网格布局",
"settings.update.title": "更新",
"settings.update.current_version_label": "当前版本",
diff --git a/LanMountainDesktop/Models/AppSettingsSnapshot.cs b/LanMountainDesktop/Models/AppSettingsSnapshot.cs
index f100d9b..1751a4a 100644
--- a/LanMountainDesktop/Models/AppSettingsSnapshot.cs
+++ b/LanMountainDesktop/Models/AppSettingsSnapshot.cs
@@ -18,6 +18,10 @@ public sealed class AppSettingsSnapshot
public string? WallpaperPath { get; set; }
+ public string WallpaperType { get; set; } = "Image";
+
+ public string? WallpaperColor { get; set; }
+
public string WallpaperPlacement { get; set; } = "Fill";
public int SettingsTabIndex { get; set; } = 0;
diff --git a/LanMountainDesktop/Services/Settings/SettingsContracts.cs b/LanMountainDesktop/Services/Settings/SettingsContracts.cs
index a2598eb..882790c 100644
--- a/LanMountainDesktop/Services/Settings/SettingsContracts.cs
+++ b/LanMountainDesktop/Services/Settings/SettingsContracts.cs
@@ -15,7 +15,7 @@ public enum WallpaperMediaType
}
public sealed record GridSettingsState(int ShortSideCells, string SpacingPreset, int EdgeInsetPercent);
-public sealed record WallpaperSettingsState(string? WallpaperPath, string Placement);
+public sealed record WallpaperSettingsState(string? WallpaperPath, string Type, string? Color, string Placement);
public sealed record ThemeAppearanceSettingsState(bool IsNightMode, string? ThemeColor, bool UseSystemChrome);
public sealed record StatusBarSettingsState(
IReadOnlyList TopStatusComponentIds,
diff --git a/LanMountainDesktop/Services/Settings/SettingsDomainServices.cs b/LanMountainDesktop/Services/Settings/SettingsDomainServices.cs
index 3ddf259..0337cfe 100644
--- a/LanMountainDesktop/Services/Settings/SettingsDomainServices.cs
+++ b/LanMountainDesktop/Services/Settings/SettingsDomainServices.cs
@@ -91,13 +91,19 @@ internal sealed class WallpaperSettingsService : IWallpaperSettingsService
public WallpaperSettingsState Get()
{
var snapshot = _settingsService.Load();
- return new WallpaperSettingsState(snapshot.WallpaperPath, snapshot.WallpaperPlacement);
+ return new WallpaperSettingsState(
+ snapshot.WallpaperPath,
+ snapshot.WallpaperType ?? "Image",
+ snapshot.WallpaperColor,
+ snapshot.WallpaperPlacement);
}
public void Save(WallpaperSettingsState state)
{
var snapshot = _settingsService.Load();
snapshot.WallpaperPath = state.WallpaperPath;
+ snapshot.WallpaperType = state.Type;
+ snapshot.WallpaperColor = state.Color;
snapshot.WallpaperPlacement = string.IsNullOrWhiteSpace(state.Placement)
? "Fill"
: state.Placement.Trim();
@@ -107,6 +113,8 @@ internal sealed class WallpaperSettingsService : IWallpaperSettingsService
changedKeys:
[
nameof(AppSettingsSnapshot.WallpaperPath),
+ nameof(AppSettingsSnapshot.WallpaperType),
+ nameof(AppSettingsSnapshot.WallpaperColor),
nameof(AppSettingsSnapshot.WallpaperPlacement)
]);
}
diff --git a/LanMountainDesktop/ViewModels/SettingsViewModels.cs b/LanMountainDesktop/ViewModels/SettingsViewModels.cs
index d80c552..3203e42 100644
--- a/LanMountainDesktop/ViewModels/SettingsViewModels.cs
+++ b/LanMountainDesktop/ViewModels/SettingsViewModels.cs
@@ -412,6 +412,19 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
[ObservableProperty]
private string _themeColor = string.Empty;
+ [ObservableProperty]
+ private Color _themeColorPickerValue;
+
+ partial void OnThemeColorPickerValueChanged(Color value)
+ {
+ if (_isInitializing)
+ {
+ return;
+ }
+
+ ThemeColor = value.ToString();
+ }
+
[ObservableProperty]
private bool _useSystemChrome;
@@ -474,6 +487,14 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
var theme = _settingsFacade.Theme.Get();
IsNightMode = theme.IsNightMode;
ThemeColor = theme.ThemeColor ?? string.Empty;
+ if (Color.TryParse(ThemeColor, out var color))
+ {
+ ThemeColorPickerValue = color;
+ }
+ else
+ {
+ ThemeColorPickerValue = Color.Parse("#FF3B82F6");
+ }
UseSystemChrome = theme.UseSystemChrome;
var wallpaper = _settingsFacade.Wallpaper.Get();
@@ -588,8 +609,11 @@ public sealed partial class AppearanceSettingsPageViewModel : ViewModelBase
private void SaveWallpaper()
{
+ var current = _settingsFacade.Wallpaper.Get();
_settingsFacade.Wallpaper.Save(new WallpaperSettingsState(
string.IsNullOrWhiteSpace(WallpaperPath) ? null : WallpaperPath,
+ current.Type,
+ current.Color,
SelectedWallpaperPlacement.Value));
}
diff --git a/LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs b/LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs
index c7696b4..27566f7 100644
--- a/LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs
+++ b/LanMountainDesktop/ViewModels/WallpaperSettingsPageViewModel.cs
@@ -1,7 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
+using Avalonia.Media;
+using Avalonia.Media.Imaging;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings;
@@ -19,6 +24,8 @@ public sealed partial class WallpaperSettingsPageViewModel : ViewModelBase
_settingsFacade = settingsFacade;
_languageCode = _localizationService.NormalizeLanguageCode(_settingsFacade.Region.Get().LanguageCode);
WallpaperPlacements = CreateWallpaperPlacements();
+ WallpaperTypes = CreateWallpaperTypes();
+ PresetColors = CreatePresetColors();
RefreshLocalizedText();
_isInitializing = true;
@@ -27,41 +34,90 @@ public sealed partial class WallpaperSettingsPageViewModel : ViewModelBase
}
public IReadOnlyList WallpaperPlacements { get; }
+ public IReadOnlyList WallpaperTypes { get; }
+ public IReadOnlyList PresetColors { get; }
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
private string _wallpaperPath = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
- private SelectionOption _selectedWallpaperPlacement = new("Fill", "Fill");
+ [ObservableProperty]
+ private SelectionOption _selectedWallpaperType = null!;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
+ private string? _selectedColor;
+
+ [ObservableProperty]
+ private SelectionOption _selectedWallpaperPlacement = null!;
+
+ [ObservableProperty]
private string _wallpaperHeader = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
+ private string _wallpaperTypeLabel = string.Empty;
+
+ [ObservableProperty]
private string _wallpaperPathLabel = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
+ private string _wallpaperColorLabel = string.Empty;
+
+ [ObservableProperty]
private string _wallpaperPlacementLabel = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
private string _wallpaperPlacementDescription = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
private string _importWallpaperButtonText = string.Empty;
- [CommunityToolkit.Mvvm.ComponentModel.ObservableProperty]
+ [ObservableProperty]
private string _filePickerTitle = string.Empty;
+ [ObservableProperty]
+ private bool _isImageOrVideo;
+
+ [ObservableProperty]
+ private bool _isSolidColor;
+
+ [ObservableProperty]
+ private Bitmap? _previewImage;
+
public void Load()
{
var wallpaper = _settingsFacade.Wallpaper.Get();
WallpaperPath = wallpaper.WallpaperPath ?? string.Empty;
+
+ SelectedWallpaperType = WallpaperTypes.FirstOrDefault(t => t.Value == wallpaper.Type) ?? WallpaperTypes[0];
+ SelectedColor = wallpaper.Color ?? PresetColors[0];
+
var wallpaperPlacement = string.IsNullOrWhiteSpace(wallpaper.Placement)
? "Fill"
: wallpaper.Placement;
SelectedWallpaperPlacement = WallpaperPlacements.FirstOrDefault(option =>
string.Equals(option.Value, wallpaperPlacement, StringComparison.OrdinalIgnoreCase))
?? WallpaperPlacements[0];
+
+ UpdateVisibility();
+ UpdatePreviewImage(WallpaperPath);
+ }
+
+ partial void OnSelectedWallpaperTypeChanged(SelectionOption value)
+ {
+ UpdateVisibility();
+ if (_isInitializing) return;
+ SaveWallpaper();
+ }
+
+ private void UpdateVisibility()
+ {
+ IsImageOrVideo = SelectedWallpaperType?.Value is "Image" or "Video";
+ IsSolidColor = SelectedWallpaperType?.Value == "SolidColor";
+ }
+
+ partial void OnSelectedColorChanged(string? value)
+ {
+ if (_isInitializing) return;
+ SaveWallpaper();
}
public async Task ImportWallpaperAsync(string sourcePath)
@@ -75,28 +131,48 @@ public sealed partial class WallpaperSettingsPageViewModel : ViewModelBase
partial void OnWallpaperPathChanged(string value)
{
- if (_isInitializing)
+ UpdatePreviewImage(value);
+ if (_isInitializing) return;
+ SaveWallpaper();
+ }
+
+ private void UpdatePreviewImage(string path)
+ {
+ if (string.IsNullOrWhiteSpace(path) || !System.IO.File.Exists(path))
{
+ PreviewImage = null;
return;
}
- SaveWallpaper();
+ try
+ {
+ using var stream = System.IO.File.OpenRead(path);
+ PreviewImage = new Bitmap(stream);
+ }
+ catch
+ {
+ PreviewImage = null;
+ }
}
partial void OnSelectedWallpaperPlacementChanged(SelectionOption value)
{
- if (_isInitializing || value is null)
- {
- return;
- }
-
+ if (_isInitializing || value is null) return;
SaveWallpaper();
}
+ [RelayCommand]
+ private void SelectColor(string color)
+ {
+ SelectedColor = color;
+ }
+
private void SaveWallpaper()
{
_settingsFacade.Wallpaper.Save(new WallpaperSettingsState(
string.IsNullOrWhiteSpace(WallpaperPath) ? null : WallpaperPath,
+ SelectedWallpaperType.Value,
+ SelectedColor,
SelectedWallpaperPlacement.Value));
}
@@ -112,10 +188,32 @@ public sealed partial class WallpaperSettingsPageViewModel : ViewModelBase
];
}
+ private IReadOnlyList CreateWallpaperTypes()
+ {
+ return
+ [
+ new SelectionOption("Image", L("settings.wallpaper.type.image", "Image")),
+ new SelectionOption("Video", L("settings.wallpaper.type.video", "Video")),
+ new SelectionOption("SolidColor", L("settings.wallpaper.type.solid_color", "Solid Color"))
+ ];
+ }
+
+ private IReadOnlyList CreatePresetColors()
+ {
+ return
+ [
+ "#D8A7B1", "#B6C9BB", "#A2B5BB", "#E6E2D3",
+ "#B5A397", "#C5C1C0", "#D4BE8D", "#C08261",
+ "#8E9775", "#9FBAD3", "#E5BAA2", "#4E596F"
+ ];
+ }
+
private void RefreshLocalizedText()
{
WallpaperHeader = L("settings.wallpaper.title", "Wallpaper");
+ WallpaperTypeLabel = L("settings.wallpaper.type_label", "Wallpaper Type");
WallpaperPathLabel = L("settings.wallpaper.current_label", "Current Wallpaper");
+ WallpaperColorLabel = L("settings.wallpaper.color_label", "Wallpaper Color");
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");
diff --git a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs
index d237dbe..f27c9f7 100644
--- a/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs
+++ b/LanMountainDesktop/Views/MainWindow.SettingsHardCut.Stubs.cs
@@ -15,6 +15,8 @@ using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services;
using LanMountainDesktop.Theme;
using LanMountainDesktop.Views.Components;
+using LibVLCSharp.Shared;
+using LibVLCSharp.Avalonia;
namespace LanMountainDesktop.Views;
@@ -218,13 +220,24 @@ public partial class MainWindow
_defaultDesktopBackground = GetThemeBrush("AdaptiveSurfaceBaseBrush");
}
- private void TryRestoreWallpaper(string? savedWallpaperPath)
+ private void TryRestoreWallpaper(string? savedWallpaperPath, string? type = null, string? color = null)
{
_wallpaperPath = string.IsNullOrWhiteSpace(savedWallpaperPath) ? null : savedWallpaperPath;
+ _wallpaperType = type ?? "Image";
+ if (TryParseColor(color, out var parsedColor))
+ {
+ _wallpaperSolidColor = parsedColor;
+ }
_wallpaperBitmap?.Dispose();
_wallpaperBitmap = null;
+ if (_wallpaperType == "SolidColor")
+ {
+ _wallpaperMediaType = WallpaperMediaType.SolidColor;
+ return;
+ }
+
if (string.IsNullOrWhiteSpace(_wallpaperPath) || !File.Exists(_wallpaperPath))
{
_wallpaperMediaType = WallpaperMediaType.None;
@@ -232,7 +245,7 @@ public partial class MainWindow
}
var extension = Path.GetExtension(_wallpaperPath);
- if (SupportedVideoExtensions.Contains(extension))
+ if (SupportedVideoExtensions.Contains(extension) || _wallpaperType == "Video")
{
_wallpaperMediaType = WallpaperMediaType.Video;
_wallpaperVideoPath = _wallpaperPath;
@@ -263,6 +276,12 @@ public partial class MainWindow
private void ApplyWallpaperBrush()
{
+ if (_wallpaperMediaType == WallpaperMediaType.SolidColor && _wallpaperSolidColor.HasValue)
+ {
+ DesktopWallpaperLayer.Background = new SolidColorBrush(_wallpaperSolidColor.Value);
+ return;
+ }
+
if (_wallpaperMediaType == WallpaperMediaType.Image && _wallpaperBitmap is not null)
{
DesktopWallpaperLayer.Background = new ImageBrush(_wallpaperBitmap)
@@ -277,16 +296,65 @@ public partial class MainWindow
private void UpdateWallpaperDisplay()
{
+ if (_wallpaperMediaType == WallpaperMediaType.Video)
+ {
+ if (!string.IsNullOrWhiteSpace(_wallpaperVideoPath))
+ {
+ StartVideoWallpaper(_wallpaperVideoPath);
+ }
+ }
+ else
+ {
+ StopVideoWallpaper();
+ }
+
ApplyWallpaperBrush();
}
+ private void StartVideoWallpaper(string videoPath)
+ {
+ if (string.IsNullOrWhiteSpace(videoPath) || !File.Exists(videoPath))
+ {
+ return;
+ }
+
+ try
+ {
+ _libVlc ??= new LibVLC();
+ _videoWallpaperPlayer ??= new MediaPlayer(_libVlc);
+
+ if (_videoWallpaperMedia?.Mrl != videoPath)
+ {
+ _videoWallpaperMedia?.Dispose();
+ _videoWallpaperMedia = new Media(_libVlc, new Uri(videoPath));
+ _videoWallpaperPlayer.Media = _videoWallpaperMedia;
+ }
+
+ if (DesktopVideoWallpaperView is { } videoView)
+ {
+ videoView.MediaPlayer = _videoWallpaperPlayer;
+ videoView.IsVisible = true;
+ }
+
+ if (!_videoWallpaperPlayer.IsPlaying)
+ {
+ _videoWallpaperPlayer.Play();
+ }
+ }
+ catch
+ {
+ }
+ }
+
private void StopVideoWallpaper()
{
- _wallpaperVideoPath = null;
- if (_wallpaperMediaType == WallpaperMediaType.Video)
+ if (DesktopVideoWallpaperView is { } videoView)
{
- _wallpaperMediaType = WallpaperMediaType.None;
+ videoView.IsVisible = false;
}
+
+ _videoWallpaperPlayer?.Stop();
+ _wallpaperVideoPath = null;
}
private double CalculateCurrentBackgroundLuminance()
@@ -398,7 +466,7 @@ public partial class MainWindow
InitializeDesktopSurfaceState(layoutSnapshot);
InitializeLauncherVisibilitySettings(launcherSnapshot);
InitializeDesktopComponentPlacements(layoutSnapshot);
- TryRestoreWallpaper(snapshot.WallpaperPath);
+ TryRestoreWallpaper(snapshot.WallpaperPath, snapshot.WallpaperType, snapshot.WallpaperColor);
if (TryParseColor(snapshot.ThemeColor, out var savedThemeColor))
{
_selectedThemeColor = savedThemeColor;
@@ -428,6 +496,8 @@ public partial class MainWindow
IsNightMode = _isNightMode,
ThemeColor = _selectedThemeColor.ToString(),
WallpaperPath = _wallpaperPath,
+ WallpaperType = _wallpaperType,
+ WallpaperColor = _wallpaperSolidColor?.ToString(),
LanguageCode = _languageCode,
TimeZoneId = _timeZoneService.CurrentTimeZone.Id,
WeatherLocationMode = _weatherLocationMode.ToString(),
diff --git a/LanMountainDesktop/Views/MainWindow.axaml b/LanMountainDesktop/Views/MainWindow.axaml
index f75eed1..29fde44 100644
--- a/LanMountainDesktop/Views/MainWindow.axaml
+++ b/LanMountainDesktop/Views/MainWindow.axaml
@@ -47,6 +47,12 @@
VerticalAlignment="Stretch"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}" />
+
+
-
+
diff --git a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml
index 83c29dd..cae0fc8 100644
--- a/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml
+++ b/LanMountainDesktop/Views/SettingsPages/ComponentsSettingsPage.axaml
@@ -20,25 +20,37 @@
-
+
-
+
+
-
+
-
+
+
diff --git a/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml
index dc6023b..6058248 100644
--- a/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml
+++ b/LanMountainDesktop/Views/SettingsPages/WallpaperSettingsPage.axaml
@@ -9,34 +9,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Margin="0,0,0,8" />
-
+
+
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Description="{Binding WallpaperPlacementDescription}"
+ IsVisible="{Binding IsImageOrVideo}"
+ Margin="0,4,0,0">
-
diff --git a/LanMountainDesktop/Views/SettingsWindow.axaml b/LanMountainDesktop/Views/SettingsWindow.axaml
index ce2d5cf..35c8422 100644
--- a/LanMountainDesktop/Views/SettingsWindow.axaml
+++ b/LanMountainDesktop/Views/SettingsWindow.axaml
@@ -29,7 +29,7 @@