Compare commits

...

2 Commits

12 changed files with 191 additions and 45 deletions

View File

@@ -23,6 +23,33 @@
--- ---
## [0.8.3.2] - 2026-04-09
### 新增 (Added)
-**应用启动台图标卡片显示选项**: 新增应用启动台图标卡片显示设置
- 用户可在设置中选择是否显示应用图标的专属卡片背景
- 关闭后仅显示应用图标本身,更加简洁
- 支持动态切换,实时预览效果
### 变更 (Changed)
-
### 修复 (Fixed)
- 🐛 **应用启动台文件夹应用数量限制**: 修复了应用启动台文件夹无法查看超过12个应用的问题
- 问题原因: 文件夹弹窗未实现滚动功能,应用列表超出显示区域后被截断
- 修复方案: 为文件夹内容区域添加滚动支持,允许用户滚动查看所有应用
- 🐛 **电源菜单重启导致关机问题**: 修复了点击电源菜单"重启"选项却触发关机的问题
- 问题原因: `SlideToShutDown.exe` 仅支持关机操作,不支持重启,错误地将其用于重启功能
- 修复方案: 重启操作改为使用标准的二次确认对话框(所有平台统一),仅关机操作使用 SlideToShutDown 滑动界面
- 🐛 **课表组件字体显示问题**: 修复了日间模式下课表组件字体颜色与背景色相近导致看不清的问题
- 问题原因: 主题切换时增量更新逻辑未同步更新文字颜色
- 修复方案: 在 `IncrementalUpdateItems()` 方法中同步更新课程项的文字颜色
### 移除 (Removed)
- 🗑️ **更新页面重复标题**: 移除了更新页面中重复的更新标题,优化页面布局
---
## [0.8.3.1] - 2026-04-08 ## [0.8.3.1] - 2026-04-08
### 新增 (Added) ### 新增 (Added)
@@ -78,5 +105,6 @@
## 链接 ## 链接
[Unreleased]: https://github.com/yourorg/LanMountainDesktop/compare/v0.8.3.1...HEAD [Unreleased]: https://github.com/yourorg/LanMountainDesktop/compare/v0.8.3.2...HEAD
[0.8.3.2]: https://github.com/yourorg/LanMountainDesktop/releases/tag/v0.8.3.2
[0.8.3.1]: https://github.com/yourorg/LanMountainDesktop/releases/tag/v0.8.3.1 [0.8.3.1]: https://github.com/yourorg/LanMountainDesktop/releases/tag/v0.8.3.1

View File

@@ -564,6 +564,10 @@
"settings.launcher.hidden_type_folder": "Folder", "settings.launcher.hidden_type_folder": "Folder",
"settings.launcher.hidden_type_shortcut": "App", "settings.launcher.hidden_type_shortcut": "App",
"settings.launcher.restore_button": "Unhide", "settings.launcher.restore_button": "Unhide",
"settings.launcher.appearance_header": "Appearance",
"settings.launcher.appearance_desc": "Customize the appearance of the App Launcher.",
"settings.launcher.show_tile_background_header": "Show tile background",
"settings.launcher.show_tile_background_desc": "Display a background card behind each app icon. When turned off, only the icon is shown for a cleaner look.",
"settings.plugins.title": "Plugins", "settings.plugins.title": "Plugins",
"settings.plugins.runtime_header": "Plugin Runtime", "settings.plugins.runtime_header": "Plugin Runtime",
"settings.plugins.runtime_desc": "Review plugin runtime state and load results.", "settings.plugins.runtime_desc": "Review plugin runtime state and load results.",

View File

@@ -558,6 +558,10 @@
"settings.launcher.hidden_type_folder": "文件夹", "settings.launcher.hidden_type_folder": "文件夹",
"settings.launcher.hidden_type_shortcut": "应用", "settings.launcher.hidden_type_shortcut": "应用",
"settings.launcher.restore_button": "取消隐藏", "settings.launcher.restore_button": "取消隐藏",
"settings.launcher.appearance_header": "外观",
"settings.launcher.appearance_desc": "自定义应用启动台的外观样式。",
"settings.launcher.show_tile_background_header": "显示图标卡片背景",
"settings.launcher.show_tile_background_desc": "在应用图标后显示卡片背景,关闭后仅显示图标更加简洁。",
"settings.plugins.title": "插件", "settings.plugins.title": "插件",
"settings.plugins.runtime_header": "插件运行时", "settings.plugins.runtime_header": "插件运行时",
"settings.plugins.runtime_desc": "查看插件运行时状态、加载结果与诊断信息。", "settings.plugins.runtime_desc": "查看插件运行时状态、加载结果与诊断信息。",

View File

@@ -8,6 +8,8 @@ public sealed class LauncherSettingsSnapshot
public List<string> HiddenLauncherAppPaths { get; set; } = []; public List<string> HiddenLauncherAppPaths { get; set; } = [];
public bool ShowTileBackground { get; set; } = true;
public LauncherSettingsSnapshot Clone() public LauncherSettingsSnapshot Clone()
{ {
var clone = (LauncherSettingsSnapshot)MemberwiseClone(); var clone = (LauncherSettingsSnapshot)MemberwiseClone();

View File

@@ -113,6 +113,11 @@ internal sealed class WindowsPowerManagementService : IPowerManagementService
public void ShowNativePowerUI(PowerAction action) public void ShowNativePowerUI(PowerAction action)
{ {
// SlideToShutDown.exe 只支持关机,不支持重启
// 重启操作应该通过 RestartAsync() 使用 shutdown /r 命令
if (action != PowerAction.Shutdown)
return;
var slideToShutDownPath = Environment.ExpandEnvironmentVariables(@"%windir%\System32\SlideToShutDown.exe"); var slideToShutDownPath = Environment.ExpandEnvironmentVariables(@"%windir%\System32\SlideToShutDown.exe");
if (System.IO.File.Exists(slideToShutDownPath)) if (System.IO.File.Exists(slideToShutDownPath))
{ {
@@ -124,26 +129,13 @@ internal sealed class WindowsPowerManagementService : IPowerManagementService
return; return;
} }
switch (action) // 回退到标准关机命令
{
case PowerAction.Shutdown:
Process.Start(new ProcessStartInfo Process.Start(new ProcessStartInfo
{ {
FileName = "shutdown", FileName = "shutdown",
Arguments = "/s /t 5 /c \"LanMountainDesktop: Shutting down...\"", Arguments = "/s /t 5 /c \"LanMountainDesktop: Shutting down...\"",
UseShellExecute = true UseShellExecute = true
}); });
break;
case PowerAction.Restart:
Process.Start(new ProcessStartInfo
{
FileName = "shutdown",
Arguments = "/r /t 5 /c \"LanMountainDesktop: Restarting...\"",
UseShellExecute = true
});
break;
}
} }
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]

View File

@@ -117,6 +117,36 @@ public sealed partial class LauncherSettingsPageViewModel : ViewModelBase, IDisp
[ObservableProperty] [ObservableProperty]
private bool _isHiddenItemsEmpty = true; private bool _isHiddenItemsEmpty = true;
[ObservableProperty]
private string _appearanceHeader = string.Empty;
[ObservableProperty]
private string _appearanceDescription = string.Empty;
[ObservableProperty]
private string _showTileBackgroundHeader = string.Empty;
[ObservableProperty]
private string _showTileBackgroundDescription = string.Empty;
[ObservableProperty]
private bool _showTileBackground;
partial void OnShowTileBackgroundChanged(bool value)
{
SaveShowTileBackgroundSetting(value);
}
private void SaveShowTileBackgroundSetting(bool value)
{
var snapshot = _settingsFacade.LauncherPolicy.Get()?.Clone() ?? new LauncherSettingsSnapshot();
snapshot.ShowTileBackground = value;
_settingsFacade.Settings.SaveSnapshot(
SettingsScope.Launcher,
snapshot,
changedKeys: [nameof(LauncherSettingsSnapshot.ShowTileBackground)]);
}
public void Dispose() public void Dispose()
{ {
if (_disposed) if (_disposed)
@@ -157,6 +187,8 @@ public sealed partial class LauncherSettingsPageViewModel : ViewModelBase, IDisp
ResolveCulture(), ResolveCulture(),
L("settings.launcher.hidden_summary_format", "{0} hidden items"), L("settings.launcher.hidden_summary_format", "{0} hidden items"),
HiddenItems.Count); HiddenItems.Count);
ShowTileBackground = snapshot.ShowTileBackground;
} }
private StartMenuFolderNode LoadCatalogSafe() private StartMenuFolderNode LoadCatalogSafe()
@@ -317,6 +349,10 @@ public sealed partial class LauncherSettingsPageViewModel : ViewModelBase, IDisp
HiddenDescription = L("settings.launcher.hidden_desc", "Review hidden launcher entries and show them again."); HiddenDescription = L("settings.launcher.hidden_desc", "Review hidden launcher entries and show them again.");
HiddenHint = L("settings.launcher.hidden_hint", "In desktop edit mode, select a launcher icon and click Hide. Hidden entries appear here."); HiddenHint = L("settings.launcher.hidden_hint", "In desktop edit mode, select a launcher icon and click Hide. Hidden entries appear here.");
HiddenEmptyText = L("settings.launcher.hidden_empty", "No hidden items."); HiddenEmptyText = L("settings.launcher.hidden_empty", "No hidden items.");
AppearanceHeader = L("settings.launcher.appearance_header", "Appearance");
AppearanceDescription = L("settings.launcher.appearance_desc", "Customize the appearance of the App Launcher.");
ShowTileBackgroundHeader = L("settings.launcher.show_tile_background_header", "Show tile background");
ShowTileBackgroundDescription = L("settings.launcher.show_tile_background_desc", "Display a background card behind each app icon in the launcher.");
} }
private CultureInfo ResolveCulture() private CultureInfo ResolveCulture()

View File

@@ -725,6 +725,8 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
? CreateBrush("#FF4FC3F7") ? CreateBrush("#FF4FC3F7")
: CreateBrush("#FF4D5A"); : CreateBrush("#FF4D5A");
var normalBulletBrush = CreateBrush(_isNightVisual ? "#B8BEC9" : "#9AA3B2"); var normalBulletBrush = CreateBrush(_isNightVisual ? "#B8BEC9" : "#9AA3B2");
var primaryBrush = CreateBrush(_isNightVisual ? "#F9FBFF" : "#151821");
var secondaryBrush = CreateBrush(_isNightVisual ? "#848B99" : "#667084");
for (var i = 0; i < _courseItems.Count && i < CourseListPanel.Children.Count; i++) for (var i = 0; i < _courseItems.Count && i < CourseListPanel.Children.Count; i++)
{ {
@@ -746,20 +748,32 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var timeText = textStack.Children[1] as TextBlock; var timeText = textStack.Children[1] as TextBlock;
var detailText = textStack.Children[2] as TextBlock; var detailText = textStack.Children[2] as TextBlock;
if (titleText != null && titleText.Text != item.Name) if (titleText != null)
{
if (titleText.Text != item.Name)
{ {
titleText.Text = item.Name; titleText.Text = item.Name;
} }
titleText.Foreground = primaryBrush;
}
if (timeText != null && timeText.Text != item.TimeRange) if (timeText != null)
{
if (timeText.Text != item.TimeRange)
{ {
timeText.Text = item.TimeRange; timeText.Text = item.TimeRange;
} }
timeText.Foreground = secondaryBrush;
}
if (detailText != null && detailText.Text != item.Detail) if (detailText != null)
{
if (detailText.Text != item.Detail)
{ {
detailText.Text = item.Detail; detailText.Text = item.Detail;
} }
detailText.Foreground = secondaryBrush;
}
} }
} }

View File

@@ -400,10 +400,12 @@ public partial class MainWindow
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
// Windows: 使用 SlideToShutDown 滑动关机界面
_powerService.ShowNativePowerUI(PowerAction.Shutdown); _powerService.ShowNativePowerUI(PowerAction.Shutdown);
} }
else else
{ {
// Linux: 二次确认对话框
await ShowPowerConfirmDialogAsync(L("power.shutdown_confirm_title", "Shutdown"), await ShowPowerConfirmDialogAsync(L("power.shutdown_confirm_title", "Shutdown"),
L("power.shutdown_confirm_message", "Are you sure you want to shut down this computer?"), L("power.shutdown_confirm_message", "Are you sure you want to shut down this computer?"),
() => _powerService.ShutdownAsync()); () => _powerService.ShutdownAsync());
@@ -416,17 +418,12 @@ public partial class MainWindow
_ = e; _ = e;
ClosePopupIfOpen(); ClosePopupIfOpen();
if (OperatingSystem.IsWindows()) // 所有平台:统一使用二次确认对话框
{ // Note: SlideToShutDown.exe 只支持关机,不支持重启
_powerService.ShowNativePowerUI(PowerAction.Restart);
}
else
{
await ShowPowerConfirmDialogAsync(L("power.restart_confirm_title", "Restart"), await ShowPowerConfirmDialogAsync(L("power.restart_confirm_title", "Restart"),
L("power.restart_confirm_message", "Are you sure you want to restart this computer?"), L("power.restart_confirm_message", "Are you sure you want to restart this computer?"),
() => _powerService.RestartAsync()); () => _powerService.RestartAsync());
} }
}
private async void OnPowerLogoutClick(object? sender, RoutedEventArgs e) private async void OnPowerLogoutClick(object? sender, RoutedEventArgs e)
{ {

View File

@@ -47,6 +47,7 @@ public partial class MainWindow
private readonly Stack<StartMenuFolderNode> _launcherFolderStack = []; private readonly Stack<StartMenuFolderNode> _launcherFolderStack = [];
private readonly HashSet<string> _hiddenLauncherFolderPaths = new(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _hiddenLauncherFolderPaths = new(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _hiddenLauncherAppPaths = new(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _hiddenLauncherAppPaths = new(StringComparer.OrdinalIgnoreCase);
private bool _showLauncherTileBackground = true;
private Button? _selectedLauncherTileButton; private Button? _selectedLauncherTileButton;
private LauncherEntryKind? _selectedLauncherEntryKind; private LauncherEntryKind? _selectedLauncherEntryKind;
private string? _selectedLauncherEntryKey; private string? _selectedLauncherEntryKey;
@@ -116,6 +117,8 @@ public partial class MainWindow
} }
} }
} }
_showLauncherTileBackground = snapshot.ShowTileBackground;
} }
private void InitializeDesktopSurfaceSwipeHandlers() private void InitializeDesktopSurfaceSwipeHandlers()
@@ -1137,7 +1140,6 @@ public partial class MainWindow
var button = new Button var button = new Button
{ {
Classes = { "glass-panel" },
Margin = new Thickness(0, 0, 12, 12), Margin = new Thickness(0, 0, 12, 12),
BorderThickness = new Thickness(0), BorderThickness = new Thickness(0),
BorderBrush = Brushes.Transparent, BorderBrush = Brushes.Transparent,
@@ -1146,6 +1148,16 @@ public partial class MainWindow
Content = content Content = content
// 不设置固定 Width 和 Height由 UpdateLauncherTileLayout 动态设置 // 不设置固定 Width 和 Height由 UpdateLauncherTileLayout 动态设置
}; };
// 根据设置决定是否显示背景
if (_showLauncherTileBackground)
{
button.Classes.Add("glass-panel");
}
else
{
button.Background = Brushes.Transparent;
}
button.Click += (_, _) => button.Click += (_, _) =>
{ {
if (_isComponentLibraryOpen) if (_isComponentLibraryOpen)
@@ -1676,7 +1688,6 @@ public partial class MainWindow
var button = new Button var button = new Button
{ {
Classes = { "glass-panel" },
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
BorderThickness = new Thickness(0), BorderThickness = new Thickness(0),
@@ -1684,6 +1695,17 @@ public partial class MainWindow
Padding = new Thickness(8, 8, 8, 6), Padding = new Thickness(8, 8, 8, 6),
Content = content Content = content
}; };
// 根据设置决定是否显示背景
if (_showLauncherTileBackground)
{
button.Classes.Add("glass-panel");
}
else
{
button.Background = Brushes.Transparent;
}
button.Click += (_, _) => button.Click += (_, _) =>
{ {
if (_isComponentLibraryOpen) if (_isComponentLibraryOpen)
@@ -1745,7 +1767,6 @@ public partial class MainWindow
var button = new Button var button = new Button
{ {
Classes = { "glass-panel" },
HorizontalAlignment = HorizontalAlignment.Stretch, HorizontalAlignment = HorizontalAlignment.Stretch,
VerticalAlignment = VerticalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch,
BorderThickness = new Thickness(0), BorderThickness = new Thickness(0),
@@ -1753,6 +1774,17 @@ public partial class MainWindow
Padding = new Thickness(8, 8, 8, 6), Padding = new Thickness(8, 8, 8, 6),
Content = content Content = content
}; };
// 根据设置决定是否显示背景
if (_showLauncherTileBackground)
{
button.Classes.Add("glass-panel");
}
else
{
button.Background = Brushes.Transparent;
}
button.Click += (_, _) => button.Click += (_, _) =>
{ {
if (_isComponentLibraryOpen) if (_isComponentLibraryOpen)

View File

@@ -44,6 +44,23 @@ public partial class MainWindow
return; return;
} }
// 启动台设置变化时,重新渲染启动台图标
if (e.Scope == SettingsScope.Launcher && e.ChangedKeys is { Count: > 0 })
{
var changedKeys = e.ChangedKeys.ToArray();
if (changedKeys.Any(key =>
string.Equals(key, nameof(LauncherSettingsSnapshot.ShowTileBackground), StringComparison.OrdinalIgnoreCase)))
{
Dispatcher.UIThread.Post(() =>
{
var launcherSnapshot = _settingsService.LoadSnapshot<LauncherSettingsSnapshot>(SettingsScope.Launcher);
InitializeLauncherVisibilitySettings(launcherSnapshot);
RenderLauncherRootTiles();
}, DispatcherPriority.Background);
return;
}
}
if (e.Scope == SettingsScope.App && e.ChangedKeys is { Count: > 0 }) if (e.Scope == SettingsScope.App && e.ChangedKeys is { Count: > 0 })
{ {
var changedKeys = e.ChangedKeys.ToArray(); var changedKeys = e.ChangedKeys.ToArray();

View File

@@ -4,6 +4,7 @@
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" xmlns:fi="using:FluentIcons.Avalonia.Fluent"
xmlns:symbol="using:FluentIcons.Common"
x:Class="LanMountainDesktop.Views.SettingsPages.LauncherSettingsPage" x:Class="LanMountainDesktop.Views.SettingsPages.LauncherSettingsPage"
x:DataType="vm:LauncherSettingsPageViewModel"> x:DataType="vm:LauncherSettingsPageViewModel">
<ScrollViewer VerticalScrollBarVisibility="Auto"> <ScrollViewer VerticalScrollBarVisibility="Auto">
@@ -52,9 +53,33 @@
</Border> </Border>
<controls:IconText Icon="Apps" <controls:IconText Icon="Apps"
Text="{Binding HiddenHeader}" Text="{Binding AppearanceHeader}"
Margin="0,0,0,4" /> Margin="0,0,0,4" />
<ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding AppearanceHeader}"
Description="{Binding AppearanceDescription}"
IsExpanded="True">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="{x:Static symbol:Symbol.Apps}" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpanderItem>
<Grid ColumnDefinitions="*,Auto">
<StackPanel Spacing="2">
<TextBlock Text="{Binding ShowTileBackgroundHeader}" />
<TextBlock Classes="settings-item-description"
Text="{Binding ShowTileBackgroundDescription}" />
</StackPanel>
<ToggleSwitch Grid.Column="1"
IsChecked="{Binding ShowTileBackground}" />
</Grid>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
<controls:IconText Icon="Apps"
Text="{Binding HiddenHeader}"
Margin="0,24,0,4" />
<ui:SettingsExpander Classes="settings-expander-card" <ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding HiddenHeader}" Header="{Binding HiddenHeader}"
Description="{Binding HiddenDescription}" Description="{Binding HiddenDescription}"

View File

@@ -37,11 +37,6 @@
<ScrollViewer VerticalScrollBarVisibility="Auto"> <ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Classes="settings-page-container settings-page-animated"> <StackPanel Classes="settings-page-container settings-page-animated">
<TextBlock Classes="settings-section-title"
Text="{Binding PageTitle}" />
<TextBlock Classes="settings-section-description"
Text="{Binding PageDescription}" />
<Border Classes="update-status-card"> <Border Classes="update-status-card">
<StackPanel Spacing="18"> <StackPanel Spacing="18">
<Grid ColumnDefinitions="Auto,*,Auto" <Grid ColumnDefinitions="Auto,*,Auto"