组件系统
This commit is contained in:
lincube
2026-03-01 16:50:06 +08:00
parent f0e44c0f87
commit 87f47e1887
20 changed files with 3312 additions and 201 deletions

View File

@@ -4,4 +4,5 @@ public static class BuiltInComponentIds
{
public const string Clock = "Clock";
public const string Blank2x4 = "Blank2x4";
public const string Date = "Date";
}

View File

@@ -29,14 +29,14 @@ public sealed class ComponentRegistry
MinWidthCells: 1,
MinHeightCells: 1,
AllowStatusBarPlacement: true,
AllowDesktopPlacement: true),
AllowDesktopPlacement: false),
new DesktopComponentDefinition(
BuiltInComponentIds.Blank2x4,
"Blank 2x4",
"Rectangle",
"Layout",
MinWidthCells: 2,
MinHeightCells: 4,
BuiltInComponentIds.Date,
"Date",
"Calendar",
"Date",
MinWidthCells: 4,
MinHeightCells: 2,
AllowStatusBarPlacement: false,
AllowDesktopPlacement: true)
};

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework>

View File

@@ -84,10 +84,14 @@
"launcher.empty": "No Start Menu entries found.",
"launcher.empty_folder": "This folder is empty.",
"launcher.folder_items_format": "{0} apps",
"button.component_library": "Component Library",
"tooltip.component_library": "Component Library",
"component_library.title": "Component Library",
"component_library.empty": "No components yet. Components will appear here later.",
"button.component_library": "Edit Desktop",
"tooltip.component_library": "Edit Desktop",
"component_library.title": "Edit Desktop",
"component_library.empty": "Swipe to pick a category, tap to open, then drag a widget onto the desktop.",
"component_library.drag_hint": "Drag to place",
"component_category.date": "Date",
"component.date": "Date",
"desktop.add_page": "Add page",
"placement.fill": "Fill",
"placement.fit": "Fit",
"placement.stretch": "Stretch",

View File

@@ -84,10 +84,14 @@
"launcher.empty": "未找到开始菜单条目。",
"launcher.empty_folder": "此文件夹为空。",
"launcher.folder_items_format": "{0} 个应用",
"button.component_library": "组件库",
"tooltip.component_library": "组件库",
"component_library.title": "组件库",
"component_library.empty": "暂无组件,后续会在这里显示。",
"button.component_library": "桌面编辑",
"tooltip.component_library": "桌面编辑",
"component_library.title": "桌面编辑",
"component_library.empty": "左右滑动选择类别,点击进入,然后拖动组件到桌面放置。",
"component_library.drag_hint": "拖动放置",
"component_category.date": "日期",
"component.date": "日历",
"desktop.add_page": "新增页面",
"placement.fill": "填充",
"placement.fit": "适应",
"placement.stretch": "拉伸",

View File

@@ -18,6 +18,8 @@ public sealed class AppSettingsSnapshot
public string LanguageCode { get; set; } = "zh-CN";
public string? TimeZoneId { get; set; }
public List<string> TopStatusComponentIds { get; set; } = [];
public List<string> PinnedTaskbarActions { get; set; } =
@@ -33,4 +35,6 @@ public sealed class AppSettingsSnapshot
public int DesktopPageCount { get; set; } = 1;
public int CurrentDesktopSurfaceIndex { get; set; } = 0;
public List<DesktopComponentPlacementSnapshot> DesktopComponentPlacements { get; set; } = [];
}

View File

@@ -0,0 +1,18 @@
namespace LanMontainDesktop.Models;
public sealed class DesktopComponentPlacementSnapshot
{
public string PlacementId { get; set; } = string.Empty;
public int PageIndex { get; set; }
public string ComponentId { get; set; } = string.Empty;
public int Row { get; set; }
public int Column { get; set; }
public int WidthCells { get; set; } = 1;
public int HeightCells { get; set; } = 1;
}

View File

@@ -3,6 +3,6 @@ namespace LanMontainDesktop.Models;
public enum TaskbarActionId
{
MinimizeToWindows,
OpenSettings
OpenSettings,
AddDesktopPage
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.ObjectModel;
namespace LanMontainDesktop.Services;
/// <summary>
/// 时区服务,提供时区信息和时间转换功能
/// </summary>
public sealed class TimeZoneService
{
private TimeZoneInfo _currentTimeZone = TimeZoneInfo.Local;
/// <summary>
/// 当前选中的时区
/// </summary>
public TimeZoneInfo CurrentTimeZone
{
get => _currentTimeZone;
set
{
if (_currentTimeZone != value)
{
_currentTimeZone = value;
TimeZoneChanged?.Invoke(this, EventArgs.Empty);
}
}
}
/// <summary>
/// 时区变更事件
/// </summary>
public event EventHandler? TimeZoneChanged;
/// <summary>
/// 获取所有可用的时区
/// </summary>
public ReadOnlyCollection<TimeZoneInfo> GetAllTimeZones()
{
return TimeZoneInfo.GetSystemTimeZones();
}
/// <summary>
/// 获取当前时区的当前时间
/// </summary>
public DateTime GetCurrentTime()
{
return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, _currentTimeZone);
}
/// <summary>
/// 根据时区ID设置当前时区
/// </summary>
public bool SetTimeZoneById(string timeZoneId)
{
try
{
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
CurrentTimeZone = timeZone;
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 获取时区显示名称包含UTC偏移
/// </summary>
public string GetTimeZoneDisplayName(TimeZoneInfo timeZone)
{
var offset = timeZone.BaseUtcOffset;
var sign = offset >= TimeSpan.Zero ? "+" : "-";
var hours = Math.Abs(offset.Hours);
var minutes = Math.Abs(offset.Minutes);
return $"(UTC{sign}{hours:D2}:{minutes:D2}) {timeZone.DisplayName}";
}
/// <summary>
/// 获取常用时区列表
/// </summary>
public TimeZoneInfo[] GetCommonTimeZones()
{
return new[]
{
TimeZoneInfo.Local, // 本地时区
TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"), // 北京时间
TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time"), // 东京时间
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"), // 太平洋时间
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"), // 东部时间
TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"), // 中欧时间
TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"), // 伦敦时间
TimeZoneInfo.FindSystemTimeZoneById("UTC"), // 协调世界时
};
}
}

View File

@@ -8,11 +8,9 @@
x:Class="LanMontainDesktop.Views.Components.ClockWidget">
<Border x:Name="RootBorder"
Classes="glass-panel"
Padding="8"
CornerRadius="0"
BorderBrush="Transparent"
BorderThickness="0"
Background="Transparent">
CornerRadius="8">
<TextBlock x:Name="TimeTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"

View File

@@ -4,6 +4,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using LanMontainDesktop.Services;
namespace LanMontainDesktop.Views.Components;
@@ -14,6 +15,8 @@ public partial class ClockWidget : UserControl
Interval = TimeSpan.FromSeconds(1)
};
private TimeZoneService? _timeZoneService;
public ClockWidget()
{
InitializeComponent();
@@ -24,6 +27,21 @@ public partial class ClockWidget : UserControl
UpdateClock();
}
/// <summary>
/// 设置时区服务
/// </summary>
public void SetTimeZoneService(TimeZoneService timeZoneService)
{
if (_timeZoneService != null)
{
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
}
_timeZoneService = timeZoneService;
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
UpdateClock();
}
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
UpdateClock();
@@ -40,9 +58,14 @@ public partial class ClockWidget : UserControl
UpdateClock();
}
private void OnTimeZoneChanged(object? sender, EventArgs e)
{
UpdateClock();
}
private void UpdateClock()
{
var now = DateTime.Now;
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
TimeTextBlock.Text = now.ToString("HH:mm:ss", CultureInfo.CurrentCulture);
}

View File

@@ -0,0 +1,83 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="400"
d:DesignHeight="200"
x:Class="LanMontainDesktop.Views.Components.DateWidget">
<Border x:Name="RootBorder"
Background="Transparent"
CornerRadius="16"
ClipToBounds="True">
<Grid ColumnDefinitions="*,*">
<!-- 左侧:月历 -->
<Border x:Name="CalendarBackgroundBorder"
Grid.Column="0"
Padding="12"
Background="{DynamicResource AdaptiveSurfaceBaseBrush}">
<Grid RowDefinitions="Auto,*">
<!-- 月份年份 -->
<TextBlock x:Name="CalendarMonthYearTextBlock"
Grid.Row="0"
HorizontalAlignment="Center"
FontSize="12"
FontWeight="SemiBold"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Margin="0,0,0,8" />
<!-- 月历网格 -->
<Grid x:Name="CalendarGrid"
Grid.Row="1"
RowDefinitions="Auto,*,*,*,*,*"
ColumnDefinitions="*,*,*,*,*,*,*">
<!-- 星期标题 -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="日" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="一" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="2" Text="二" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="3" Text="三" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="4" Text="四" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="5" Text="五" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
<TextBlock Grid.Row="0" Grid.Column="6" Text="六" HorizontalAlignment="Center" FontSize="10" Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
</Grid>
</Grid>
</Border>
<!-- 右侧:今日详情 -->
<Border x:Name="TodayBackgroundBorder"
Grid.Column="1"
Background="{DynamicResource AdaptiveAccentBrush}"
Padding="16">
<Grid RowDefinitions="Auto,*,Auto">
<!-- 今日标签 -->
<TextBlock Grid.Row="0"
Text="今天"
HorizontalAlignment="Center"
FontSize="11"
FontWeight="Medium"
Opacity="0.8"
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
<!-- 日期数字 -->
<TextBlock x:Name="TodayDayTextBlock"
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="56"
FontWeight="Light"
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
<!-- 星期 -->
<TextBlock x:Name="TodayWeekdayTextBlock"
Grid.Row="2"
HorizontalAlignment="Center"
FontSize="13"
FontWeight="SemiBold"
Foreground="{DynamicResource AdaptiveOnAccentBrush}" />
</Grid>
</Border>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using LanMontainDesktop.Services;
namespace LanMontainDesktop.Views.Components;
public partial class DateWidget : UserControl
{
private readonly DispatcherTimer _timer = new()
{
Interval = TimeSpan.FromMinutes(1)
};
private TimeZoneService? _timeZoneService;
public DateWidget()
{
InitializeComponent();
_timer.Tick += OnTimerTick;
AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree;
UpdateDate();
}
/// <summary>
/// 设置时区服务
/// </summary>
public void SetTimeZoneService(TimeZoneService timeZoneService)
{
if (_timeZoneService != null)
{
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
}
_timeZoneService = timeZoneService;
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
UpdateDate();
}
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
UpdateDate();
_timer.Start();
}
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
}
private void OnTimerTick(object? sender, EventArgs e)
{
UpdateDate();
}
private void OnTimeZoneChanged(object? sender, EventArgs e)
{
UpdateDate();
}
private void UpdateDate()
{
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
var culture = CultureInfo.CurrentCulture;
// 右侧:今日详情
TodayDayTextBlock.Text = now.Day.ToString();
TodayWeekdayTextBlock.Text = now.ToString("dddd", culture);
// 左侧:月历
CalendarMonthYearTextBlock.Text = now.ToString("yyyy年M月", culture);
// 生成月历
GenerateCalendar(now);
}
private void GenerateCalendar(DateTime currentDate)
{
// 清空之前的日期(保留星期标题)
var childrenToRemove = new List<Control>();
foreach (var child in CalendarGrid.Children)
{
if (child is TextBlock tb && tb.Tag?.ToString() == "day")
{
childrenToRemove.Add(tb);
}
}
foreach (var child in childrenToRemove)
{
CalendarGrid.Children.Remove(child);
}
var year = currentDate.Year;
var month = currentDate.Month;
var today = currentDate.Day;
// 获取该月第一天
var firstDayOfMonth = new DateTime(year, month, 1);
var daysInMonth = DateTime.DaysInMonth(year, month);
var startDayOfWeek = (int)firstDayOfMonth.DayOfWeek; // 0 = Sunday
// 生成日期
for (int day = 1; day <= daysInMonth; day++)
{
var row = ((day + startDayOfWeek - 1) / 7) + 1; // +1 because row 0 is weekday headers
var col = (day + startDayOfWeek - 1) % 7;
if (row > 5) continue; // 最多显示6行
var dayText = new TextBlock
{
Text = day.ToString(),
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
FontSize = 10,
Tag = "day"
};
// 今天高亮
if (day == today)
{
// 使用主题色高亮今天
var accentBrush = this.TryFindResource("AdaptiveAccentBrush", out var accent)
? accent as IBrush
: Brushes.Blue;
var onAccentBrush = this.TryFindResource("AdaptiveOnAccentBrush", out var onAccent)
? onAccent as IBrush
: Brushes.White;
dayText.Foreground = onAccentBrush;
dayText.FontWeight = FontWeight.Bold;
dayText.Background = new SolidColorBrush(Colors.Transparent);
// 添加背景圆
var highlight = new Border
{
Background = accentBrush,
CornerRadius = new CornerRadius(10),
Width = 20,
Height = 20,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
Child = dayText
};
Grid.SetRow(highlight, row);
Grid.SetColumn(highlight, col);
CalendarGrid.Children.Add(highlight);
}
else
{
// 使用主题次要文本颜色
var secondaryBrush = this.TryFindResource("AdaptiveTextSecondaryBrush", out var secondary)
? secondary as IBrush
: Brushes.Gray;
dayText.Foreground = secondaryBrush;
Grid.SetRow(dayText, row);
Grid.SetColumn(dayText, col);
CalendarGrid.Children.Add(dayText);
}
}
}
public void ApplyCellSize(double cellSize)
{
// 根据格子大小调整圆角
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.12, 8, 20));
// 调整字体大小
var baseFontSize = cellSize * 0.25;
TodayDayTextBlock.FontSize = Math.Clamp(baseFontSize * 2.8, 28, 72);
TodayWeekdayTextBlock.FontSize = Math.Clamp(baseFontSize * 0.6, 10, 16);
CalendarMonthYearTextBlock.FontSize = Math.Clamp(baseFontSize * 0.55, 9, 14);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -107,6 +107,11 @@ public partial class MainWindow
Grid.SetColumnSpan(DesktopPagesViewport, gridMetrics.ColumnCount);
DesktopPagesViewport.Width = pageWidth;
DesktopPagesViewport.Height = pageHeight;
if (DesktopEditDragLayer is not null)
{
DesktopEditDragLayer.Width = pageWidth;
DesktopEditDragLayer.Height = pageHeight;
}
DesktopPagesHost.RowDefinitions.Clear();
DesktopPagesHost.RowDefinitions.Add(new RowDefinition(new GridLength(pageHeight, GridUnitType.Pixel)));
@@ -123,31 +128,35 @@ public partial class MainWindow
DesktopPagesContainer.Children.Clear();
DesktopPagesContainer.Width = pageWidth * _desktopPageCount;
DesktopPagesContainer.Height = pageHeight;
_desktopPageComponentGrids.Clear();
for (var index = 0; index < _desktopPageCount; index++)
{
DesktopPagesContainer.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(pageWidth, GridUnitType.Pixel)));
var pageSurface = new Border
var pageGrid = new Grid
{
Width = pageWidth,
Height = pageHeight,
Background = Brushes.Transparent,
BorderThickness = new Thickness(0),
Padding = new Thickness(10)
ShowGridLines = false
};
if (_desktopPageCount > 1)
for (var row = 0; row < viewportRowSpan; row++)
{
pageSurface.Child = new TextBlock
{
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top,
Foreground = Foreground,
Opacity = 0.72,
Text = Lf("desktop.page_index_format", "Desktop {0}", index + 1)
};
pageGrid.RowDefinitions.Add(new RowDefinition(new GridLength(gridMetrics.CellSize, GridUnitType.Pixel)));
}
Grid.SetColumn(pageSurface, index);
Grid.SetRow(pageSurface, 0);
DesktopPagesContainer.Children.Add(pageSurface);
for (var col = 0; col < gridMetrics.ColumnCount; col++)
{
pageGrid.ColumnDefinitions.Add(new ColumnDefinition(new GridLength(gridMetrics.CellSize, GridUnitType.Pixel)));
}
_desktopPageComponentGrids[index] = pageGrid;
RestoreDesktopPageComponents(index);
Grid.SetColumn(pageGrid, index);
Grid.SetRow(pageGrid, 0);
DesktopPagesContainer.Children.Add(pageGrid);
}
Grid.SetColumn(LauncherPagePanel, 1);
@@ -290,12 +299,17 @@ public partial class MainWindow
private bool CanSwipeDesktopSurface()
{
return !_isSettingsOpen && !_isComponentLibraryOpen && _desktopSurfacePageWidth > 1;
return !_isSettingsOpen && !_isDesktopComponentDragActive && _desktopSurfacePageWidth > 1;
}
private void OnDesktopPagesPointerPressed(object? sender, PointerPressedEventArgs e)
{
if (!CanSwipeDesktopSurface() || DesktopPagesViewport is null)
if (DesktopPagesViewport is null)
{
return;
}
if (!CanSwipeDesktopSurface())
{
return;
}
@@ -326,6 +340,16 @@ public partial class MainWindow
foreach (var node in visual.GetSelfAndVisualAncestors())
{
if (node is Control control)
{
// Avoid swiping pages when interacting with desktop components/widgets.
if (control.Classes.Contains("desktop-component") ||
control.Classes.Contains("desktop-component-host"))
{
return true;
}
}
if (node is Button or TextBox or ComboBox or ListBoxItem or Slider or ToggleSwitch)
{
return true;

View File

@@ -59,14 +59,15 @@ public partial class MainWindow
WallpaperPreviewBackButtonTextBlock.Text = L("button.back_to_windows", "Back to Windows");
ToolTip.SetTip(BackToWindowsButton, L("tooltip.back_to_windows", "Back to Windows"));
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Component Library");
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Component Library");
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "Component Library"));
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Component Library");
OpenComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
WallpaperPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
GridPreviewComponentLibraryTextBlock.Text = L("button.component_library", "Edit Desktop");
ToolTip.SetTip(OpenComponentLibraryButton, L("tooltip.component_library", "Edit Desktop"));
ComponentLibraryTitleTextBlock.Text = L("component_library.title", "Edit Desktop");
ToolTip.SetTip(CloseComponentLibraryButton, L("common.close", "Close"));
ComponentLibraryEmptyTextBlock.Text = L(
"component_library.empty",
"No components yet. Components will appear here later.");
"Swipe to pick a category, tap to open, then drag a widget onto the desktop.");
LauncherTitleTextBlock.Text = L("launcher.title", "应用启动台");
LauncherSubtitleTextBlock.Text = L("launcher.subtitle", "按 Windows 开始菜单结构显示所有应用与文件夹");
@@ -74,21 +75,19 @@ public partial class MainWindow
ToolTip.SetTip(LauncherFolderCloseButton, L("common.close", "关闭"));
SettingsNavHeaderTextBlock.Text = L("settings.nav_header", "Settings");
SettingsNavWallpaperItem.Content = L("settings.nav.wallpaper", "Wallpaper");
SettingsNavGridItem.Content = L("settings.nav.grid", "Grid");
SettingsNavColorItem.Content = L("settings.nav.color", "Color");
SettingsNavStatusBarItem.Content = L("settings.nav.status_bar", "Status Bar");
SettingsNavRegionItem.Content = L("settings.nav.region", "Region");
SettingsNavWallpaperTextBlock.Text = L("settings.nav.wallpaper", "Wallpaper");
SettingsNavGridTextBlock.Text = L("settings.nav.grid", "Grid");
SettingsNavColorTextBlock.Text = L("settings.nav.color", "Color");
SettingsNavStatusBarTextBlock.Text = L("settings.nav.status_bar", "Status Bar");
SettingsNavRegionTextBlock.Text = L("settings.nav.region", "Region");
WallpaperPanelTitleTextBlock.Text = L("settings.wallpaper.title", "个性化的背景");
WallpaperPanelDescriptionTextBlock.Text = L("settings.wallpaper.description", "选择图片或视频");
WallpaperPanelTitleTextBlock.Text = L("settings.wallpaper.title", "个性化我们的背景");
WallpaperPlacementSettingsExpander.Header = L("settings.wallpaper.placement_label", "选择契合度");
WallpaperPlacementSettingsExpander.Description = L("settings.wallpaper.placement_desc", "调整图像在桌面上的填充方式。");
PickWallpaperButton.Content = L("settings.wallpaper.pick_button", "浏览照片");
ClearWallpaperButton.Content = L("settings.wallpaper.clear_button", "重置");
GridPanelTitleTextBlock.Text = L("settings.grid.title", "Grid Layout");
ApplyGridButton.Content = L("settings.grid.apply_button", "Apply");
ColorPanelTitleTextBlock.Text = L("settings.color.title", "Color");
ThemeModeSettingsExpander.Header = L("settings.color.day_night_label", "Day/Night");
@@ -123,7 +122,7 @@ public partial class MainWindow
DesktopGrid.RowDefinitions.Count,
DesktopGrid.RowDefinitions.Count > 0 ? DesktopGrid.RowDefinitions[0].Height.Value : 0d);
PopulateComponentLibraryItems();
BuildComponentLibraryCategoryPages();
RenderLauncherRootTiles();
UpdateOpenSettingsActionVisualState();
UpdateWallpaperDisplay();

View File

@@ -71,6 +71,12 @@ public partial class MainWindow
ColorSettingsPanel.IsVisible = selectedIndex == 2;
StatusBarSettingsPanel.IsVisible = selectedIndex == 3;
RegionSettingsPanel.IsVisible = selectedIndex == 4;
if (selectedIndex == 1)
{
UpdateGridPreviewLayout();
}
ApplyTaskbarActionVisibility(GetCurrentTaskbarContext());
}
@@ -633,12 +639,14 @@ public partial class MainWindow
WallpaperPlacement = GetPlacementDisplayName(GetSelectedWallpaperPlacement()),
SettingsTabIndex = Math.Max(0, SettingsNavListBox?.SelectedIndex ?? 0),
LanguageCode = _languageCode,
TimeZoneId = _timeZoneService.CurrentTimeZone.Id,
TopStatusComponentIds = _topStatusComponentIds.ToList(),
PinnedTaskbarActions = _pinnedTaskbarActions.Select(action => action.ToString()).ToList(),
EnableDynamicTaskbarActions = _enableDynamicTaskbarActions,
TaskbarLayoutMode = _taskbarLayoutMode,
DesktopPageCount = _desktopPageCount,
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex
CurrentDesktopSurfaceIndex = _currentDesktopSurfaceIndex,
DesktopComponentPlacements = _desktopComponentPlacements.ToList()
};
_appSettingsService.Save(snapshot);
@@ -1028,16 +1036,7 @@ public partial class MainWindow
{
WallpaperPlacementSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
{
Symbol = Symbol.Image,
IconVariant = variant
};
}
if (GridSizeSettingsExpander is not null)
{
GridSizeSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
{
Symbol = Symbol.Grid,
Symbol = Symbol.Wallpaper,
IconVariant = variant
};
}
@@ -1064,7 +1063,16 @@ public partial class MainWindow
{
LanguageSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
{
Symbol = Symbol.Earth,
Symbol = Symbol.Translate,
IconVariant = variant
};
}
if (TimeZoneSettingsExpander is not null)
{
TimeZoneSettingsExpander.IconSource = new FluentIcons.Avalonia.Fluent.SymbolIconSource
{
Symbol = Symbol.GlobeClock,
IconVariant = variant
};
}

View File

@@ -210,6 +210,8 @@
</Border>
</Grid>
<Canvas x:Name="DesktopEditDragLayer"
IsHitTestVisible="False" />
</Grid>
</Border>
@@ -259,8 +261,8 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<fi:SymbolIcon Classes="icon-m"
Symbol="Window"
<fi:FluentIcon x:Name="BackToWindowsIcon"
Icon="Window"
IconVariant="Regular" />
<TextBlock x:Name="BackToWindowsTextBlock"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
@@ -301,7 +303,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<fi:FluentIcon Classes="icon-m"
<fi:FluentIcon x:Name="OpenComponentLibraryIcon"
Icon="Apps"
IconVariant="Regular" />
<TextBlock x:Name="OpenComponentLibraryTextBlock"
@@ -324,8 +326,8 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="6">
<fi:SymbolIcon Classes="icon-l"
Symbol="Settings"
<fi:FluentIcon x:Name="OpenSettingsIcon"
Icon="Settings"
IconVariant="Regular" />
<TextBlock x:Name="OpenSettingsButtonTextBlock"
IsVisible="False"
@@ -413,32 +415,32 @@
SelectionChanged="OnSettingsNavSelectionChanged">
<ListBoxItem x:Name="SettingsNavWallpaperItem" ToolTip.Tip="&#22721;&#32440;">
<StackPanel Orientation="Horizontal" Spacing="12">
<fi:SymbolIcon Symbol="Image" IconVariant="Regular" />
<TextBlock Text="&#22721;&#32440;" VerticalAlignment="Center" />
<fi:SymbolIcon x:Name="SettingsNavWallpaperIcon" Symbol="Wallpaper" IconVariant="Regular" />
<TextBlock x:Name="SettingsNavWallpaperTextBlock" Text="&#22721;&#32440;" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem x:Name="SettingsNavGridItem" ToolTip.Tip="&#32593;&#26684;">
<StackPanel Orientation="Horizontal" Spacing="12">
<fi:SymbolIcon Symbol="Grid" IconVariant="Regular" />
<TextBlock Text="&#32593;&#26684;" VerticalAlignment="Center" />
<fi:SymbolIcon x:Name="SettingsNavGridIcon" Symbol="Grid" IconVariant="Regular" />
<TextBlock x:Name="SettingsNavGridTextBlock" Text="&#32593;&#26684;" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem x:Name="SettingsNavColorItem" ToolTip.Tip="&#39068;&#33394;">
<StackPanel Orientation="Horizontal" Spacing="12">
<fi:SymbolIcon Symbol="Color" IconVariant="Regular" />
<TextBlock Text="&#39068;&#33394;" VerticalAlignment="Center" />
<fi:SymbolIcon x:Name="SettingsNavColorIcon" Symbol="Color" IconVariant="Regular" />
<TextBlock x:Name="SettingsNavColorTextBlock" Text="&#39068;&#33394;" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem x:Name="SettingsNavStatusBarItem" ToolTip.Tip="&#29366;&#24577;&#26639;">
<StackPanel Orientation="Horizontal" Spacing="12">
<fi:SymbolIcon Symbol="Clock" IconVariant="Regular" />
<TextBlock Text="&#29366;&#24577;&#26639;" VerticalAlignment="Center" />
<fi:SymbolIcon x:Name="SettingsNavStatusBarIcon" Symbol="Status" IconVariant="Regular" />
<TextBlock x:Name="SettingsNavStatusBarTextBlock" Text="&#29366;&#24577;&#26639;" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
<ListBoxItem x:Name="SettingsNavRegionItem" ToolTip.Tip="&#22320;&#21306;">
<StackPanel Orientation="Horizontal" Spacing="12">
<fi:SymbolIcon Symbol="Earth" IconVariant="Regular" />
<TextBlock Text="&#22320;&#21306;" VerticalAlignment="Center" />
<fi:SymbolIcon x:Name="SettingsNavRegionIcon" Symbol="Globe" IconVariant="Regular" />
<TextBlock x:Name="SettingsNavRegionTextBlock" Text="&#22320;&#21306;" VerticalAlignment="Center" />
</StackPanel>
</ListBoxItem>
</ListBox>
@@ -461,12 +463,6 @@
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Margin="0,0,0,24"
Text="个性化您的背景" />
<TextBlock x:Name="WallpaperPanelDescriptionTextBlock"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
FontSize="14"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Margin="0,-18,0,24"
Text="更改您的壁纸并将预览投影到显示器上。" />
<!-- Left Column: Monitor Preview -->
<Border x:Name="WallpaperPreviewHost"
@@ -603,45 +599,120 @@
</StackPanel>
</Grid>
<StackPanel x:Name="GridSettingsPanel"
IsVisible="False"
Spacing="16">
<Grid x:Name="GridSettingsPanel"
IsVisible="False"
ColumnDefinitions="*, *"
RowDefinitions="Auto, *">
<TextBlock x:Name="GridPanelTitleTextBlock"
FontSize="24"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
FontSize="28"
FontWeight="SemiBold"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Text="Grid layout" />
Margin="0,0,0,24"
Text="调整网格布局" />
<ui:SettingsExpander x:Name="GridSizeSettingsExpander"
Header="&#30701;&#36793;&#26684;&#25968;"
Description="&#20915;&#23450;&#26700;&#38754;&#30701;&#36793;&#30340;&#22522;&#30784;&#26684;&#23376;&#25968;&#37327;">
<ui:SettingsExpander.IconSource>
<!-- Left Column: Grid Preview -->
<Border x:Name="GridPreviewHost"
Grid.Row="1" Grid.Column="0"
Margin="0,0,16,0"
VerticalAlignment="Top"
HorizontalAlignment="Stretch">
<Border x:Name="GridPreviewFrame"
HorizontalAlignment="Stretch"
CornerRadius="14"
Background="#FF1A1A1A"
Padding="12">
<Border x:Name="GridPreviewViewport"
ClipToBounds="True"
CornerRadius="4"
Background="#30111827">
<Panel>
<Canvas x:Name="GridPreviewLinesCanvas" />
<Grid x:Name="GridPreviewGrid"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Border x:Name="GridPreviewTopStatusBarHost"
Grid.Row="0"
Background="Transparent"
Padding="2">
<StackPanel x:Name="GridPreviewTopStatusComponentsPanel"
Orientation="Horizontal"
Spacing="3">
</StackPanel>
</Border>
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<Grid ColumnDefinitions="Auto,Auto"
ColumnSpacing="8">
<ui:NumberBox x:Name="GridSizeNumberBox"
Grid.Column="0"
Width="120"
Minimum="6"
Maximum="96"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Value="12" />
<Button x:Name="ApplyGridButton"
Grid.Column="1"
Padding="12,6"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Click="OnApplyGridSizeClick"
Content="&#24212;&#29992;" />
</Grid>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<Border x:Name="GridPreviewBottomTaskbarContainer"
Classes="glass-strong"
Grid.Row="1"
Margin="3"
CornerRadius="8"
Padding="2">
<Grid ColumnDefinitions="Auto,*,Auto"
ColumnSpacing="3">
<Border x:Name="GridPreviewTaskbarFixedActionsHost" Grid.Column="0">
<StackPanel x:Name="GridPreviewBackButtonVisual" Orientation="Horizontal" Spacing="3">
<fi:SymbolIcon Classes="icon-s" Symbol="Window" />
<TextBlock x:Name="GridPreviewBackButtonTextBlock" Text="回到Windows" VerticalAlignment="Center" />
</StackPanel>
</Border>
<StackPanel x:Name="GridPreviewTaskbarDynamicActionsHost"
Grid.Column="1"
Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="3" />
<Border x:Name="GridPreviewTaskbarSettingsActionHost" Grid.Column="2">
<StackPanel Orientation="Horizontal" Spacing="3">
<StackPanel x:Name="GridPreviewComponentLibraryVisual" IsVisible="False" Orientation="Horizontal" Spacing="3">
<fi:FluentIcon x:Name="GridPreviewComponentLibraryIcon" Classes="icon-s" Icon="Apps" />
<TextBlock x:Name="GridPreviewComponentLibraryTextBlock" Text="组件库" VerticalAlignment="Center" />
</StackPanel>
<fi:SymbolIcon x:Name="GridPreviewSettingsButtonIcon" Classes="icon-s" Symbol="Settings" />
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</Panel>
</Border>
</Border>
</Border>
<TextBlock x:Name="GridInfoTextBlock"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="Grid: - cols x - rows (1:1)" />
</StackPanel>
<!-- Right Column: Settings Content -->
<StackPanel Grid.Row="1" Grid.Column="1"
Margin="16,0,0,0"
Spacing="16">
<TextBlock Text="竖排格数" FontSize="16" FontWeight="SemiBold" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
<Slider x:Name="GridSizeSlider"
Grid.Column="0"
Minimum="6"
Maximum="96"
TickFrequency="1"
TickPlacement="None"
Value="12"
ValueChanged="OnGridSizeSliderChanged" />
<ui:NumberBox x:Name="GridSizeNumberBox"
Grid.Column="1"
Width="80"
Minimum="6"
Maximum="96"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Value="12" />
</Grid>
<Button x:Name="ApplyGridButton"
HorizontalAlignment="Stretch"
Padding="0,10"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Click="OnApplyGridSizeClick"
Content="&#24212;&#29992;" />
<TextBlock x:Name="GridInfoTextBlock"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="Grid: - cols x - rows (1:1)" />
</StackPanel>
</Grid>
<StackPanel x:Name="ColorSettingsPanel"
IsVisible="False"
@@ -899,6 +970,18 @@
</ComboBox>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander x:Name="TimeZoneSettingsExpander"
Header="时区"
Description="选择时区,时钟和日历将根据此时区显示时间。">
<ui:SettingsExpander.IconSource>
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ComboBox x:Name="TimeZoneComboBox"
Width="280"
SelectionChanged="OnTimeZoneSelectionChanged" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</StackPanel>
</Grid>
</Border>
@@ -956,20 +1039,89 @@
CornerRadius="12"
Padding="14">
<Grid>
<ScrollViewer x:Name="ComponentLibraryItemsScrollViewer"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<WrapPanel x:Name="ComponentLibraryItemsPanel"
ItemWidth="190"
ItemHeight="104"
Orientation="Horizontal" />
</ScrollViewer>
<!-- Category picker (outer) -->
<Grid x:Name="ComponentLibraryCategoriesView">
<Border x:Name="ComponentLibraryCategoryViewport"
Background="Transparent"
ClipToBounds="True"
PointerPressed="OnComponentLibraryCategoryViewportPointerPressed"
PointerMoved="OnComponentLibraryCategoryViewportPointerMoved"
PointerReleased="OnComponentLibraryCategoryViewportPointerReleased"
PointerCaptureLost="OnComponentLibraryCategoryViewportPointerCaptureLost">
<Grid>
<Grid x:Name="ComponentLibraryCategoryPagesHost"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<Grid.RenderTransform>
<TranslateTransform>
<TranslateTransform.Transitions>
<Transitions>
<DoubleTransition Property="X" Duration="0:0:0.22" />
</Transitions>
</TranslateTransform.Transitions>
</TranslateTransform>
</Grid.RenderTransform>
<TextBlock x:Name="ComponentLibraryEmptyTextBlock"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="&#26242;&#26080;&#32452;&#20214;&#65292;&#21518;&#32493;&#23558;&#22312;&#36825;&#37324;&#26174;&#31034;&#12290;" />
<Grid x:Name="ComponentLibraryCategoryPagesContainer" />
</Grid>
<TextBlock x:Name="ComponentLibraryEmptyTextBlock"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
Text="No components." />
</Grid>
</Border>
</Grid>
<!-- Component picker (inner) -->
<Grid x:Name="ComponentLibraryComponentsView"
IsVisible="False"
RowDefinitions="Auto,*"
RowSpacing="10">
<Button x:Name="ComponentLibraryBackButton"
Grid.Row="0"
HorizontalAlignment="Left"
Padding="8,6"
Background="Transparent"
BorderThickness="0"
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
Click="OnComponentLibraryBackClick">
<StackPanel Orientation="Horizontal" Spacing="8">
<fi:SymbolIcon Classes="icon-s" Symbol="ArrowLeft" IconVariant="Regular" />
<TextBlock x:Name="ComponentLibraryBackTextBlock"
VerticalAlignment="Center"
Text="Back" />
</StackPanel>
</Button>
<Border x:Name="ComponentLibraryComponentViewport"
Grid.Row="1"
Background="Transparent"
ClipToBounds="True"
PointerPressed="OnComponentLibraryComponentViewportPointerPressed"
PointerMoved="OnComponentLibraryComponentViewportPointerMoved"
PointerReleased="OnComponentLibraryComponentViewportPointerReleased"
PointerCaptureLost="OnComponentLibraryComponentViewportPointerCaptureLost">
<Grid>
<Grid x:Name="ComponentLibraryComponentPagesHost"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<Grid.RenderTransform>
<TranslateTransform>
<TranslateTransform.Transitions>
<Transitions>
<DoubleTransition Property="X" Duration="0:0:0.22" />
</Transitions>
</TranslateTransform.Transitions>
</TranslateTransform>
</Grid.RenderTransform>
<Grid x:Name="ComponentLibraryComponentPagesContainer" />
</Grid>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
</Grid>

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,11 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Line = Avalonia.Controls.Shapes.Line;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Styling;
@@ -66,6 +68,7 @@ public partial class MainWindow : Window
private readonly MonetColorService _monetColorService = new();
private readonly AppSettingsService _appSettingsService = new();
private readonly LocalizationService _localizationService = new();
private readonly TimeZoneService _timeZoneService = new();
private readonly ComponentRegistry _componentRegistry = ComponentRegistry
.CreateDefault()
.RegisterExtensions(
@@ -109,6 +112,7 @@ public partial class MainWindow : Window
InitializeComponent();
_fluentAvaloniaTheme = Application.Current?.Styles.OfType<FluentAvaloniaTheme>().FirstOrDefault();
PropertyChanged += OnWindowPropertyChanged;
InitializeDesktopComponentDragHandlers();
}
protected override void OnOpened(EventArgs e)
@@ -118,11 +122,19 @@ public partial class MainWindow : Window
_suppressSettingsPersistence = true;
var snapshot = _appSettingsService.Load();
if (!string.IsNullOrWhiteSpace(snapshot.TimeZoneId))
{
_timeZoneService.SetTimeZoneById(snapshot.TimeZoneId);
}
_targetShortSideCells = Math.Clamp(
snapshot.GridShortSideCells > 0 ? snapshot.GridShortSideCells : CalculateDefaultShortSideCellCountFromDpi(),
MinShortSideCells,
MaxShortSideCells);
GridSizeNumberBox.Value = _targetShortSideCells;
GridSizeSlider.Value = _targetShortSideCells;
GridSizeSlider.ValueChanged += OnGridSizeSliderChanged;
GridSizeNumberBox.ValueChanged += OnGridSizeNumberBoxChanged;
SettingsNavListBox.SelectedIndex = Math.Clamp(snapshot.SettingsTabIndex, 0, 4);
UpdateSettingsTabContent();
@@ -132,6 +144,7 @@ public partial class MainWindow : Window
ApplyTaskbarSettings(snapshot);
InitializeLocalization(snapshot.LanguageCode);
InitializeDesktopSurfaceState(snapshot);
InitializeDesktopComponentPlacements(snapshot);
InitializeSettingsIcons();
TryRestoreWallpaper(snapshot.WallpaperPath);
@@ -153,9 +166,11 @@ public partial class MainWindow : Window
_settingsContentPanelTransform = SettingsContentPanel.RenderTransform as TranslateTransform;
DesktopHost.SizeChanged += OnDesktopHostSizeChanged;
WallpaperPreviewHost.SizeChanged += OnWallpaperPreviewHostSizeChanged;
GridPreviewHost.SizeChanged += OnGridPreviewHostSizeChanged;
RebuildDesktopGrid();
PopulateComponentLibraryItems();
LoadLauncherEntriesAsync();
InitializeTimeZoneSettings();
ClockWidget.SetTimeZoneService(_timeZoneService);
_suppressSettingsPersistence = false;
PersistSettings();
@@ -181,6 +196,9 @@ public partial class MainWindow : Window
PropertyChanged -= OnWindowPropertyChanged;
DesktopHost.SizeChanged -= OnDesktopHostSizeChanged;
WallpaperPreviewHost.SizeChanged -= OnWallpaperPreviewHostSizeChanged;
GridPreviewHost.SizeChanged -= OnGridPreviewHostSizeChanged;
GridSizeSlider.ValueChanged -= OnGridSizeSliderChanged;
GridSizeNumberBox.ValueChanged -= OnGridSizeNumberBoxChanged;
base.OnClosed(e);
}
@@ -202,6 +220,195 @@ public partial class MainWindow : Window
UpdateWallpaperPreviewLayout();
}
private void OnGridPreviewHostSizeChanged(object? sender, SizeChangedEventArgs e)
{
UpdateGridPreviewLayout();
}
private void OnGridSizeSliderChanged(object? sender, RoutedEventArgs e)
{
var sliderValue = (int)Math.Round(GridSizeSlider.Value);
if (Math.Abs(GridSizeNumberBox.Value - sliderValue) > double.Epsilon)
{
GridSizeNumberBox.Value = sliderValue;
}
UpdateGridPreviewLayout();
}
private void OnGridSizeNumberBoxChanged(object? sender, NumberBoxValueChangedEventArgs e)
{
var numberBoxValue = (int)Math.Round(GridSizeNumberBox.Value);
if (Math.Abs(GridSizeSlider.Value - numberBoxValue) > double.Epsilon)
{
GridSizeSlider.Value = numberBoxValue;
}
UpdateGridPreviewLayout();
}
private void UpdateGridPreviewLayout()
{
if (GridPreviewFrame is null ||
GridPreviewHost is null ||
GridPreviewViewport is null ||
GridPreviewGrid is null)
{
return;
}
var previewShortSideCells = (int)Math.Round(GridSizeSlider.Value);
if (previewShortSideCells < MinShortSideCells || previewShortSideCells > MaxShortSideCells)
{
previewShortSideCells = _targetShortSideCells;
}
var desktopWidth = Math.Max(1, DesktopHost.Bounds.Width);
var desktopHeight = Math.Max(1, DesktopHost.Bounds.Height);
var aspectRatio = desktopWidth / desktopHeight;
var availableWidth = Math.Max(100, GridPreviewHost.Bounds.Width);
var framePadding = GridPreviewFrame.Padding;
var horizontalPadding = framePadding.Left + framePadding.Right;
var verticalPadding = framePadding.Top + framePadding.Bottom;
var gridPreviewWidth = availableWidth;
var gridPreviewHeight = gridPreviewWidth / aspectRatio;
GridPreviewFrame.Width = gridPreviewWidth;
GridPreviewFrame.Height = gridPreviewHeight;
var innerWidth = Math.Max(1, gridPreviewWidth - horizontalPadding);
var innerHeight = Math.Max(1, gridPreviewHeight - verticalPadding);
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, previewShortSideCells);
if (gridMetrics.CellSize <= 0)
{
return;
}
GridPreviewGrid.Width = gridMetrics.ColumnCount * gridMetrics.CellSize;
GridPreviewGrid.Height = gridMetrics.RowCount * gridMetrics.CellSize;
GridPreviewGrid.RowDefinitions.Clear();
GridPreviewGrid.ColumnDefinitions.Clear();
for (var row = 0; row < gridMetrics.RowCount; row++)
{
GridPreviewGrid.RowDefinitions.Add(
new RowDefinition(new GridLength(gridMetrics.CellSize, GridUnitType.Pixel)));
}
for (var col = 0; col < gridMetrics.ColumnCount; col++)
{
GridPreviewGrid.ColumnDefinitions.Add(
new ColumnDefinition(new GridLength(gridMetrics.CellSize, GridUnitType.Pixel)));
}
PlaceStatusBarComponent(
GridPreviewTopStatusBarHost,
column: 0,
requestedColumnSpan: gridMetrics.ColumnCount,
totalColumns: gridMetrics.ColumnCount);
var taskbarRow = gridMetrics.RowCount - 1;
Grid.SetRow(GridPreviewBottomTaskbarContainer, taskbarRow);
Grid.SetColumn(GridPreviewBottomTaskbarContainer, 0);
Grid.SetRowSpan(GridPreviewBottomTaskbarContainer, 1);
Grid.SetColumnSpan(GridPreviewBottomTaskbarContainer, gridMetrics.ColumnCount);
ApplyGridPreviewWidgetSizing(gridMetrics.CellSize);
GridInfoTextBlock.Text = Lf(
"settings.grid.info_format",
"Grid: {0} cols x {1} rows | cell {2:F1}px (1:1)",
gridMetrics.ColumnCount,
gridMetrics.RowCount,
gridMetrics.CellSize);
DrawGridPreviewLines(gridMetrics);
}
private void DrawGridPreviewLines(GridMetrics gridMetrics)
{
if (GridPreviewLinesCanvas is null || GridPreviewViewport is null || GridPreviewGrid is null)
{
return;
}
var viewportBackground = GridPreviewViewport.Background as SolidColorBrush;
var backgroundColor = viewportBackground?.Color ?? Color.Parse("#30111827");
var luminance = CalculateRelativeLuminance(backgroundColor);
var lineColor = luminance >= LightBackgroundLuminanceThreshold
? Color.Parse("#80000000")
: Color.Parse("#80FFFFFF");
GridPreviewLinesCanvas.Children.Clear();
var cellSize = gridMetrics.CellSize;
var gridWidth = gridMetrics.ColumnCount * cellSize;
var gridHeight = gridMetrics.RowCount * cellSize;
GridPreviewLinesCanvas.Width = gridWidth;
GridPreviewLinesCanvas.Height = gridHeight;
Canvas.SetLeft(GridPreviewLinesCanvas, 0);
Canvas.SetTop(GridPreviewLinesCanvas, 0);
var dashLength = cellSize * 0.3;
var gapLength = cellSize * 0.2;
for (var row = 0; row <= gridMetrics.RowCount; row++)
{
var y = row * cellSize;
var line = new Line
{
StartPoint = new Point(0, y),
EndPoint = new Point(gridWidth, y),
Stroke = new SolidColorBrush(lineColor),
StrokeThickness = 1,
StrokeDashArray = new Avalonia.Collections.AvaloniaList<double> { dashLength, gapLength },
IsHitTestVisible = false
};
GridPreviewLinesCanvas.Children.Add(line);
}
for (var col = 0; col <= gridMetrics.ColumnCount; col++)
{
var x = col * cellSize;
var line = new Line
{
StartPoint = new Point(x, 0),
EndPoint = new Point(x, gridHeight),
Stroke = new SolidColorBrush(lineColor),
StrokeThickness = 1,
StrokeDashArray = new Avalonia.Collections.AvaloniaList<double> { dashLength, gapLength },
IsHitTestVisible = false
};
GridPreviewLinesCanvas.Children.Add(line);
}
}
private void ApplyGridPreviewWidgetSizing(double cellSize)
{
var margin = Math.Clamp(cellSize * 0.08, 1, 6);
var previewTaskbarCell = Math.Clamp(cellSize, 10, 36);
var iconSize = Math.Clamp(cellSize * 0.35, 8, 16);
GridPreviewTopStatusBarHost.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 1, 4));
GridPreviewBottomTaskbarContainer.Margin = new Thickness(margin);
GridPreviewBottomTaskbarContainer.CornerRadius = new CornerRadius(Math.Clamp(cellSize * 0.22, 4, 10));
GridPreviewBottomTaskbarContainer.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 1, 4));
GridPreviewBackButtonTextBlock.FontSize = Math.Clamp(cellSize * 0.19, 5, 13);
GridPreviewComponentLibraryTextBlock.FontSize = Math.Clamp(cellSize * 0.18, 5, 12);
GridPreviewComponentLibraryIcon.FontSize = iconSize;
GridPreviewBackButtonVisual.MinHeight = previewTaskbarCell;
GridPreviewBackButtonVisual.MinWidth = Math.Clamp(cellSize * 2.1, 30, 120);
GridPreviewComponentLibraryVisual.MinHeight = previewTaskbarCell;
GridPreviewComponentLibraryVisual.MinWidth = Math.Clamp(cellSize * 2.0, 28, 110);
GridPreviewSettingsButtonIcon.Width = Math.Clamp(previewTaskbarCell * 0.42, 6, 14);
GridPreviewSettingsButtonIcon.Height = Math.Clamp(previewTaskbarCell * 0.42, 6, 14);
}
private void OnApplyGridSizeClick(object? sender, RoutedEventArgs e)
{
var requested = (int)Math.Round(GridSizeNumberBox.Value);
@@ -217,7 +424,13 @@ public partial class MainWindow : Window
GridSizeNumberBox.Value = _targetShortSideCells;
}
if (Math.Abs(GridSizeSlider.Value - _targetShortSideCells) > double.Epsilon)
{
GridSizeSlider.Value = _targetShortSideCells;
}
RebuildDesktopGrid();
PersistSettings();
}
private void RebuildDesktopGrid()
@@ -328,6 +541,8 @@ public partial class MainWindow : Window
var verticalPadding = Math.Clamp(cellSize * 0.08, 2, 12);
var horizontalPadding = Math.Clamp(cellSize * 0.20, 4, 22);
var taskbarCell = Math.Clamp(cellSize, 28, 128);
var unifiedFontSize = Math.Clamp(cellSize * 0.22, 8, 22);
var unifiedIconSize = Math.Clamp(cellSize * 0.28, 10, 26);
TopStatusBarHost.Padding = new Thickness(Math.Clamp(cellSize * 0.08, 1.5, 10));
ClockWidget.Margin = new Thickness(margin);
@@ -339,24 +554,29 @@ public partial class MainWindow : Window
BackToWindowsButton.Margin = new Thickness(0);
BackToWindowsButton.Padding = new Thickness(horizontalPadding, verticalPadding);
BackToWindowsButton.FontSize = Math.Clamp(cellSize * 0.22, 8, 22);
BackToWindowsButton.FontSize = unifiedFontSize;
BackToWindowsButton.MinHeight = taskbarCell;
BackToWindowsButton.MinWidth = Math.Clamp(cellSize * 2.3, 90, 320);
BackToWindowsIcon.FontSize = unifiedIconSize;
OpenComponentLibraryButton.Margin = new Thickness(0);
OpenComponentLibraryButton.Padding = new Thickness(horizontalPadding, verticalPadding);
OpenComponentLibraryButton.FontSize = Math.Clamp(cellSize * 0.22, 8, 22);
OpenComponentLibraryButton.FontSize = unifiedFontSize;
OpenComponentLibraryButton.MinHeight = taskbarCell;
OpenComponentLibraryButton.MinWidth = Math.Clamp(cellSize * 2.0, 88, 300);
OpenComponentLibraryIcon.FontSize = unifiedIconSize;
OpenSettingsButton.Margin = new Thickness(0);
OpenSettingsButton.Height = taskbarCell;
OpenSettingsButton.MinHeight = taskbarCell;
OpenSettingsIcon.FontSize = unifiedIconSize;
if (_isSettingsOpen)
{
OpenSettingsButton.Width = double.NaN;
OpenSettingsButton.MinWidth = Math.Clamp(cellSize * 2.3, 120, 340);
OpenSettingsButton.Padding = new Thickness(horizontalPadding, verticalPadding);
OpenSettingsButton.FontSize = unifiedFontSize;
}
else
{
@@ -435,21 +655,23 @@ public partial class MainWindow : Window
var desktopHeight = Math.Max(1, DesktopHost.Bounds.Height);
var aspectRatio = desktopWidth / desktopHeight;
// Use the host width (which is roughly 50% of the settings area)
// Subtract padding for the outer host container if needed, but let it stretch
var availableWidth = Math.Max(100, WallpaperPreviewHost.Bounds.Width);
// Calculate height based on aspect ratio
var framePadding = WallpaperPreviewFrame.Padding;
var horizontalPadding = framePadding.Left + framePadding.Right;
var verticalPadding = framePadding.Top + framePadding.Bottom;
var previewWidth = availableWidth;
var previewHeight = previewWidth / aspectRatio;
// Apply sizes to the monitor frame
WallpaperPreviewFrame.Width = previewWidth;
WallpaperPreviewFrame.Height = previewHeight;
WallpaperPreviewClockTextBlock.Text = DateTime.Now.ToString("HH:mm");
var gridMetrics = CalculateGridMetrics(previewWidth, previewHeight, _targetShortSideCells);
var innerWidth = Math.Max(1, previewWidth - horizontalPadding);
var innerHeight = Math.Max(1, previewHeight - verticalPadding);
var gridMetrics = CalculateGridMetrics(innerWidth, innerHeight, _targetShortSideCells);
if (gridMetrics.CellSize <= 0)
{
return;
@@ -542,4 +764,44 @@ public partial class MainWindow : Window
}
});
}
}
private void InitializeTimeZoneSettings()
{
// 填充时区下拉框
TimeZoneComboBox.Items.Clear();
var timeZones = _timeZoneService.GetAllTimeZones();
foreach (var tz in timeZones)
{
var displayText = _timeZoneService.GetTimeZoneDisplayName(tz);
var item = new ComboBoxItem
{
Content = displayText,
Tag = tz.Id
};
TimeZoneComboBox.Items.Add(item);
// 选中当前时区
if (tz.Id == _timeZoneService.CurrentTimeZone.Id)
{
TimeZoneComboBox.SelectedItem = item;
}
}
}
private void OnTimeZoneSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (_suppressLanguageSelectionEvents || TimeZoneComboBox.SelectedItem is not ComboBoxItem item)
{
return;
}
var timeZoneId = item.Tag?.ToString();
if (string.IsNullOrEmpty(timeZoneId))
{
return;
}
_timeZoneService.SetTimeZoneById(timeZoneId);
PersistSettings();
}
}