Files
LanMountainDesktop/LanMontainDesktop/Views/Components/ClockWidget.axaml.cs

130 lines
3.8 KiB
C#
Raw Normal View History

2026-02-27 13:43:27 +08:00
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
2026-02-27 15:15:09 +08:00
using Avalonia.Media;
2026-02-27 13:43:27 +08:00
using Avalonia.Threading;
2026-03-01 16:50:06 +08:00
using LanMontainDesktop.Services;
2026-02-27 13:43:27 +08:00
namespace LanMontainDesktop.Views.Components;
2026-03-02 20:02:14 +08:00
public enum ClockDisplayFormat
{
HourMinuteSecond, // HH:mm:ss
HourMinute // HH:mm
}
2026-03-03 04:56:04 +08:00
public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZoneAwareComponentWidget
2026-02-27 13:43:27 +08:00
{
private readonly DispatcherTimer _timer = new()
{
Interval = TimeSpan.FromSeconds(1)
};
2026-03-01 16:50:06 +08:00
private TimeZoneService? _timeZoneService;
2026-03-02 20:02:14 +08:00
private ClockDisplayFormat _displayFormat = ClockDisplayFormat.HourMinuteSecond;
2026-03-01 16:50:06 +08:00
2026-02-27 13:43:27 +08:00
public ClockWidget()
{
InitializeComponent();
_timer.Tick += OnTimerTick;
AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree;
UpdateClock();
}
2026-03-02 20:02:14 +08:00
public ClockDisplayFormat DisplayFormat
{
get => _displayFormat;
set
{
_displayFormat = value;
UpdateClock();
}
}
public void SetDisplayFormat(ClockDisplayFormat format)
{
DisplayFormat = format;
}
2026-03-01 16:50:06 +08:00
public void SetTimeZoneService(TimeZoneService timeZoneService)
{
if (_timeZoneService != null)
{
_timeZoneService.TimeZoneChanged -= OnTimeZoneChanged;
}
_timeZoneService = timeZoneService;
_timeZoneService.TimeZoneChanged += OnTimeZoneChanged;
UpdateClock();
}
2026-02-27 13:43:27 +08:00
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
UpdateClock();
_timer.Start();
}
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
_timer.Stop();
}
private void OnTimerTick(object? sender, EventArgs e)
{
UpdateClock();
}
2026-03-01 16:50:06 +08:00
private void OnTimeZoneChanged(object? sender, EventArgs e)
{
UpdateClock();
}
2026-02-27 13:43:27 +08:00
private void UpdateClock()
{
2026-03-01 16:50:06 +08:00
var now = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
2026-03-02 20:02:14 +08:00
MainTimeTextBlock.Text = now.ToString("HH:mm", CultureInfo.CurrentCulture);
SecondsTextBlock.Text = now.ToString("ss", CultureInfo.CurrentCulture);
SecondsTextBlock.IsVisible = _displayFormat == ClockDisplayFormat.HourMinuteSecond;
2026-02-27 13:43:27 +08:00
}
2026-02-27 15:15:09 +08:00
public void ApplyCellSize(double cellSize)
{
2026-03-02 20:02:14 +08:00
// --- Class Island “满盈”风格算法 ---
// 1. 计算组件高度:保持与任务栏核心比例一致 (0.74x)
var targetHeight = Math.Clamp(cellSize * 0.74, 34, 74);
RootBorder.Height = targetHeight;
// 2. 动态圆角:确保始终是完美的胶囊半圆
RootBorder.CornerRadius = new CornerRadius(targetHeight / 2);
RootBorder.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
// 3. 核心:满盈字阶 (Filled Typography)
// 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力
var mainFontSize = targetHeight * 0.68;
MainTimeTextBlock.FontSize = mainFontSize;
MainTimeTextBlock.FontWeight = FontWeight.SemiBold;
// 4. 次级信息:秒数维持 0.7x 比例,并增强透明度呼吸感
SecondsTextBlock.FontSize = mainFontSize * 0.7;
SecondsTextBlock.Opacity = 0.55;
// 5. 视觉占比:占据约 2.2 个单元格的感官宽度 (cellSize * 2 + gaps)
RootBorder.MinWidth = cellSize * 2.2;
2026-02-27 15:15:09 +08:00
2026-03-02 20:02:14 +08:00
// 6. 间距微调
if (MainTimeTextBlock.Parent is StackPanel panel)
{
panel.Spacing = Math.Clamp(cellSize * 0.06, 2, 8);
}
// 确保清除可能存在的固定 Padding由代码控制“紧密感”
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.15, 12, 24), 0);
2026-02-27 15:15:09 +08:00
}
2026-02-27 13:43:27 +08:00
}