This commit is contained in:
lincube
2026-03-07 19:59:28 +08:00
parent d6ec159af4
commit 49b18d6af1
9 changed files with 642 additions and 16 deletions

View File

@@ -11,6 +11,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -48,6 +49,7 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private string _sourceType = BaiduHotSearchSourceTypes.Official; private string _sourceType = BaiduHotSearchSourceTypes.Official;
private bool _isNightVisual = true;
private sealed record HotItemVisual( private sealed record HotItemVisual(
Border Host, Border Host,
@@ -79,6 +81,7 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -133,6 +136,67 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
UpdateAdaptiveLayout();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
BrandTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#5D93FF") : Color.Parse("#2932E1"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
foreach (var visual in _hotItemVisuals)
{
visual.IndexTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#5D93FF") : Color.Parse("#2932E1"));
visual.TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
}
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshTimerTick(object? sender, EventArgs e) private async void OnRefreshTimerTick(object? sender, EventArgs e)
{ {
await RefreshHotSearchAsync(forceRefresh: true); await RefreshHotSearchAsync(forceRefresh: true);
@@ -375,6 +439,7 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
} }
StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20); StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20);
ApplyNightModeVisual();
} }
private void UpdateInteractionState() private void UpdateInteractionState()

View File

@@ -9,6 +9,7 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -46,6 +47,7 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private bool _isNightVisual = true;
private sealed record HotItemVisual( private sealed record HotItemVisual(
Border Host, Border Host,
@@ -78,6 +80,7 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -129,6 +132,69 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
UpdateAdaptiveLayout();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
SearchBoxBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#ECF2FA"));
SearchBoxBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#3FFFFFFF") : Color.Parse("#22000000"));
SearchEntryTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
SearchGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
TopRightTitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#F472C4") : Color.Parse("#F44C9F"));
foreach (var visual in _hotItemVisuals)
{
visual.IndexTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#F472C4") : Color.Parse("#F44C9F"));
visual.TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
}
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshTimerTick(object? sender, EventArgs e) private async void OnRefreshTimerTick(object? sender, EventArgs e)
{ {
await RefreshHotSearchAsync(forceRefresh: false); await RefreshHotSearchAsync(forceRefresh: false);
@@ -396,6 +462,7 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
} }
StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20); StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20);
ApplyNightModeVisual();
} }
private void UpdateInteractionState() private void UpdateInteractionState()

View File

@@ -14,6 +14,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -88,6 +89,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRotateEnabled = true; private bool _autoRotateEnabled = true;
private bool _isNightVisual = true;
public CnrDailyNewsWidget() public CnrDailyNewsWidget()
{ {
@@ -105,6 +107,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -161,6 +164,66 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
UpdateAdaptiveLayout();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
BrandPrimaryTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
BrandSecondaryTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#6A6F77"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
RefreshLabelTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
News1TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
News2TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e) private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e)
{ {
if (_isRefreshing) if (_isRefreshing)
@@ -354,9 +417,11 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
{ {
var normalizedTitle = NormalizeCompactText(title); var normalizedTitle = NormalizeCompactText(title);
var hotLabel = L("cnrnews.widget.hot_label", "Hot"); var hotLabel = L("cnrnews.widget.hot_label", "Hot");
var primaryForeground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
if (News1TitleTextBlock.Inlines is null) if (News1TitleTextBlock.Inlines is null)
{ {
News1TitleTextBlock.Text = $"{hotLabel} | {normalizedTitle}"; News1TitleTextBlock.Text = $"{hotLabel} | {normalizedTitle}";
News1TitleTextBlock.Foreground = primaryForeground;
return; return;
} }
@@ -368,7 +433,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
}); });
News1TitleTextBlock.Inlines.Add(new Run(normalizedTitle) News1TitleTextBlock.Inlines.Add(new Run(normalizedTitle)
{ {
Foreground = new SolidColorBrush(Color.Parse("#202327")), Foreground = primaryForeground,
FontWeight = FontWeight.SemiBold FontWeight = FontWeight.SemiBold
}); });
} }
@@ -401,7 +466,7 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
var textBlock = new TextBlock var textBlock = new TextBlock
{ {
Text = NormalizeCompactText(item.Title), Text = NormalizeCompactText(item.Title),
Foreground = new SolidColorBrush(Color.Parse("#202327")), Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327")),
FontFamily = MiSansFontFamily, FontFamily = MiSansFontFamily,
FontWeight = FontWeight.SemiBold, FontWeight = FontWeight.SemiBold,
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
@@ -556,6 +621,8 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
} }
ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10); ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
ApplyNightModeVisual();
} }
private void UpdateRefreshButtonState() private void UpdateRefreshButtonState()

View File

@@ -9,6 +9,7 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.VisualTree; using Avalonia.VisualTree;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -44,6 +45,7 @@ public partial class DailyWord2x2Widget : UserControl, IDesktopComponentWidget,
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private bool _isNightVisual = true;
private bool _isMeaningVisible; private bool _isMeaningVisible;
public DailyWord2x2Widget() public DailyWord2x2Widget()
@@ -59,6 +61,7 @@ public partial class DailyWord2x2Widget : UserControl, IDesktopComponentWidget,
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -113,6 +116,62 @@ public partial class DailyWord2x2Widget : UserControl, IDesktopComponentWidget,
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
ApplyNightModeVisual();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFBFA"));
WordTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#2B2F35"));
MeaningTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5A6069"));
HiddenHintTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#8A9099"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EEF1F4"));
RefreshIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e) private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e)
{ {
if (_isRefreshing) if (_isRefreshing)

View File

@@ -8,6 +8,7 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -41,6 +42,7 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private bool _isNightVisual = true;
public DailyWordWidget() public DailyWordWidget()
{ {
@@ -58,6 +60,7 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -112,6 +115,64 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
ApplyNightModeVisual();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFBFA"));
WordTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#FF9D6C") : Color.Parse("#F07541"));
PronunciationTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#6B7078"));
MeaningTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#2B2F35"));
ExampleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#2B2F35"));
ExampleTranslationTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#7A8088"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#14A0A6AF"));
RefreshIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#626870"));
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e) private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e)
{ {
if (_isRefreshing) if (_isRefreshing)
@@ -229,6 +290,14 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells; var totalWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * BaseWidthCells;
var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells; var totalHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * BaseHeightCells;
var isFourByThree = false;
if (Bounds.Width > 1 && Bounds.Height > 1)
{
var widthRatio = Bounds.Width / (_currentCellSize * BaseWidthCells);
var heightRatio = Bounds.Height / (_currentCellSize * BaseHeightCells);
isFourByThree = widthRatio >= 0.9 && heightRatio >= 1.35;
}
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52)); RootBorder.CornerRadius = new CornerRadius(Math.Clamp(34 * scale, 16, 52));
RootBorder.Padding = new Thickness(0); RootBorder.Padding = new Thickness(0);
@@ -261,15 +330,15 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
ExampleTranslationTextBlock.MaxWidth = contentWidth; ExampleTranslationTextBlock.MaxWidth = contentWidth;
var compactLayout = totalHeight < _currentCellSize * 1.72; var compactLayout = totalHeight < _currentCellSize * 1.72;
MeaningTextBlock.MaxLines = compactLayout ? 1 : 2; MeaningTextBlock.MaxLines = compactLayout ? 1 : (isFourByThree ? 3 : 2);
ExampleTextBlock.MaxLines = compactLayout ? 1 : 2; ExampleTextBlock.MaxLines = compactLayout ? 1 : (isFourByThree ? 4 : 2);
ExampleTranslationTextBlock.IsVisible = !compactLayout; ExampleTranslationTextBlock.IsVisible = !compactLayout || isFourByThree;
ExampleTranslationTextBlock.MaxLines = 1; ExampleTranslationTextBlock.MaxLines = isFourByThree ? 2 : 1;
var contentHeight = Math.Max(52, totalHeight - RootBorder.Padding.Top - RootBorder.Padding.Bottom - CardBorder.Padding.Top - CardBorder.Padding.Bottom); var contentHeight = Math.Max(52, totalHeight - RootBorder.Padding.Top - RootBorder.Padding.Bottom - CardBorder.Padding.Top - CardBorder.Padding.Bottom);
var wordHeightBudget = Math.Max(18, contentHeight * 0.24); var wordHeightBudget = Math.Max(18, contentHeight * 0.24);
var pronunciationHeightBudget = Math.Max(14, contentHeight * 0.16); var pronunciationHeightBudget = Math.Max(14, contentHeight * 0.16);
var meaningHeightBudget = Math.Max(16, contentHeight * (compactLayout ? 0.26 : 0.30)); var meaningHeightBudget = Math.Max(16, contentHeight * (compactLayout ? 0.26 : (isFourByThree ? 0.35 : 0.30)));
var exampleHeightBudget = Math.Max(16, contentHeight - wordHeightBudget - pronunciationHeightBudget - meaningHeightBudget - Math.Clamp(16 * scale, 8, 24)); var exampleHeightBudget = Math.Max(16, contentHeight - wordHeightBudget - pronunciationHeightBudget - meaningHeightBudget - Math.Clamp(16 * scale, 8, 24));
if (!ExampleTranslationTextBlock.IsVisible) if (!ExampleTranslationTextBlock.IsVisible)
{ {
@@ -433,11 +502,26 @@ public partial class DailyWordWidget : UserControl, IDesktopComponentWidget, IRe
private double ResolveScale() private double ResolveScale()
{ {
var cellScale = Math.Clamp(_currentCellSize / BaseCellSize, 0.56, 2.0); var cellScale = Math.Clamp(_currentCellSize / BaseCellSize, 0.56, 2.0);
var widthCells = BaseWidthCells;
var heightCells = BaseHeightCells;
if (Bounds.Width > 1 && Bounds.Height > 1)
{
var widthRatio = Bounds.Width / (_currentCellSize * widthCells);
var heightRatio = Bounds.Height / (_currentCellSize * heightCells);
if (widthRatio >= 0.9 && heightRatio >= 1.35)
{
heightCells = 3;
}
}
var widthScale = Bounds.Width > 1 var widthScale = Bounds.Width > 1
? Math.Clamp(Bounds.Width / Math.Max(1, _currentCellSize * BaseWidthCells), 0.56, 2.0) ? Math.Clamp(Bounds.Width / Math.Max(1, _currentCellSize * widthCells), 0.56, 2.0)
: 1; : 1;
var heightScale = Bounds.Height > 1 var heightScale = Bounds.Height > 1
? Math.Clamp(Bounds.Height / Math.Max(1, _currentCellSize * BaseHeightCells), 0.56, 2.0) ? Math.Clamp(Bounds.Height / Math.Max(1, _currentCellSize * heightCells), 0.56, 2.0)
: 1; : 1;
return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.56, 2.0); return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.56, 2.0);
} }

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -13,6 +13,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -58,6 +59,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private bool _isNightVisual = true;
private sealed record NewsItemVisual( private sealed record NewsItemVisual(
Border Host, Border Host,
@@ -86,6 +88,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -141,6 +144,67 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
UpdateAdaptiveLayout();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
BrandTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
foreach (var visual in _itemVisuals)
{
visual.Host.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#F7F8FA"));
visual.TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
}
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshTimerTick(object? sender, EventArgs e) private async void OnRefreshTimerTick(object? sender, EventArgs e)
{ {
await RefreshNewsAsync(forceRefresh: true); await RefreshNewsAsync(forceRefresh: true);
@@ -398,6 +462,7 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
} }
StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 20); StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 20);
ApplyNightModeVisual();
} }
private void UpdateInteractionState() private void UpdateInteractionState()

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@@ -8,6 +8,7 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -36,6 +37,7 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
private bool _isAttached; private bool _isAttached;
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
private bool _pausedStudyMonitoringForRecording; private bool _pausedStudyMonitoringForRecording;
private bool _isNightVisual = true;
public RecordingWidget() public RecordingWidget()
{ {
@@ -45,6 +47,7 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
InitializeWaveBars(); InitializeWaveBars();
ReloadLanguageCode(); ReloadLanguageCode();
@@ -146,6 +149,68 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
ApplyNightModeVisual();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
RootBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#ECEFF3"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#D9DEE7"));
TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#11151D"));
TimerTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#A4A9B2"));
FutureLine.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#A3A8B3"));
DiscardButtonBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#F8FAFD"));
DiscardButtonBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4451") : Color.Parse("#E0E5EC"));
DiscardIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#141922"));
SaveButtonBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#F8FAFD"));
SaveButtonBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4451") : Color.Parse("#E0E5EC"));
SaveIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#141922"));
HintTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#7A818E"));
}
private void OnUiTick(object? sender, EventArgs e) private void OnUiTick(object? sender, EventArgs e)
{ {
if (!_isAttached || !_isOnActivePage) if (!_isAttached || !_isOnActivePage)
@@ -291,11 +356,18 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
SaveButtonBorder.Opacity = SaveButtonBorder.IsHitTestVisible ? 1 : 0.42; SaveButtonBorder.Opacity = SaveButtonBorder.IsHitTestVisible ? 1 : 0.42;
RecordToggleButtonBorder.Opacity = RecordToggleButtonBorder.IsHitTestVisible ? 1 : 0.54; RecordToggleButtonBorder.Opacity = RecordToggleButtonBorder.IsHitTestVisible ? 1 : 0.54;
TimerTextBlock.Foreground = CreateBrush(!isSupported if (!isSupported)
? "#B2B7C0" {
: isReady TimerTextBlock.Foreground = CreateBrush(_isNightVisual ? "#A8B1C2" : "#B2B7C0");
? "#A4A9B2" }
: "#151922"); else if (isReady)
{
TimerTextBlock.Foreground = CreateBrush(_isNightVisual ? "#A8B1C2" : "#A4A9B2");
}
else
{
TimerTextBlock.Foreground = CreateBrush(_isNightVisual ? "#E8EAED" : "#151922");
}
HintTextBlock.IsVisible = !isReady || !isSupported; HintTextBlock.IsVisible = !isReady || !isSupported;
RecordDot.IsVisible = snapshot.State == AudioRecorderRuntimeState.Ready; RecordDot.IsVisible = snapshot.State == AudioRecorderRuntimeState.Ready;

View File

@@ -13,6 +13,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -60,6 +61,7 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
private bool _isAttached; private bool _isAttached;
private bool _isRefreshing; private bool _isRefreshing;
private bool _autoRefreshEnabled = true; private bool _autoRefreshEnabled = true;
private bool _isNightVisual = true;
private sealed record ForumItemVisual( private sealed record ForumItemVisual(
Border Host, Border Host,
@@ -153,6 +155,7 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
UpdateLanguageCode(); UpdateLanguageCode();
@@ -208,6 +211,70 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_isNightVisual = ResolveNightMode();
UpdateAdaptiveLayout();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
CardBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#FCFCFD"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#00000000"));
HeaderTitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
HeaderDot.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#FF6B6B") : Color.Parse("#FF4D4F"));
RefreshButton.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#EFF1F5"));
RefreshGlyphIcon.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#5E6671"));
foreach (var visual in _itemVisuals)
{
visual.Host.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#2D3440") : Color.Parse("#F7F8FA"));
visual.AvatarHost.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#3D4451") : Color.Parse("#E7EBF4"));
visual.AvatarFallbackText.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#A8B1C2") : Color.Parse("#4A5466"));
visual.TitleTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#E8EAED") : Color.Parse("#202327"));
}
StatusTextBlock.Foreground = new SolidColorBrush(_isNightVisual ? Color.Parse("#8B95A5") : Color.Parse("#6A6F77"));
}
private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e) private async void OnRefreshButtonClick(object? sender, RoutedEventArgs e)
{ {
if (_isRefreshing) if (_isRefreshing)
@@ -606,6 +673,8 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
StatusTextBlock.FontSize = Math.Clamp(14 * softScale, 10, 18); StatusTextBlock.FontSize = Math.Clamp(14 * softScale, 10, 18);
ApplyNightModeVisual();
if (_visibleItemCount != previousVisibleItemCount && if (_visibleItemCount != previousVisibleItemCount &&
_isAttached && _isAttached &&
!_isRefreshing && !_isRefreshing &&

View File

@@ -6,6 +6,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Shapes; using Avalonia.Controls.Shapes;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -81,6 +82,8 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
public required TextBlock OffsetTextBlock { get; init; } public required TextBlock OffsetTextBlock { get; init; }
public bool? IsNightApplied { get; set; } public bool? IsNightApplied { get; set; }
public bool? IsSystemNightApplied { get; set; }
} }
private readonly DispatcherTimer _clockTimer = new() private readonly DispatcherTimer _clockTimer = new()
@@ -99,6 +102,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
private double _currentCellSize = BaseCellSize; private double _currentCellSize = BaseCellSize;
private DateTime _nextLanguageProbeUtc = DateTime.MinValue; private DateTime _nextLanguageProbeUtc = DateTime.MinValue;
private string _secondHandMode = ClockSecondHandMode.Tick; private string _secondHandMode = ClockSecondHandMode.Tick;
private bool _isNightVisual = true;
public WorldClockWidget() public WorldClockWidget()
{ {
@@ -114,6 +118,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
AttachedToVisualTree += OnAttachedToVisualTree; AttachedToVisualTree += OnAttachedToVisualTree;
DetachedFromVisualTree += OnDetachedFromVisualTree; DetachedFromVisualTree += OnDetachedFromVisualTree;
SizeChanged += OnSizeChanged; SizeChanged += OnSizeChanged;
ActualThemeVariantChanged += OnActualThemeVariantChanged;
} }
public void SetTimeZoneService(TimeZoneService timeZoneService) public void SetTimeZoneService(TimeZoneService timeZoneService)
@@ -211,6 +216,79 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
ApplyCellSize(_currentCellSize); ApplyCellSize(_currentCellSize);
} }
private void OnActualThemeVariantChanged(object? sender, EventArgs e)
{
_ = sender;
_ = e;
_isNightVisual = ResolveNightMode();
ApplyNightModeVisual();
}
private bool ResolveNightMode()
{
if (ActualThemeVariant == ThemeVariant.Dark)
{
return true;
}
if (ActualThemeVariant == ThemeVariant.Light)
{
return false;
}
if (this.TryFindResource("AdaptiveSurfaceBaseBrush", out var value) &&
value is ISolidColorBrush brush)
{
return CalculateRelativeLuminance(brush.Color) < 0.45;
}
return true;
}
private static double CalculateRelativeLuminance(Color color)
{
static double ToLinear(double channel)
{
return channel <= 0.03928
? channel / 12.92
: Math.Pow((channel + 0.055) / 1.055, 2.4);
}
var r = ToLinear(color.R / 255d);
var g = ToLinear(color.G / 255d);
var b = ToLinear(color.B / 255d);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
private void ApplyNightModeVisual()
{
RootBorder.Background = new SolidColorBrush(_isNightVisual ? Color.Parse("#1B2129") : Color.Parse("#F4F5F7"));
RootBorder.BorderBrush = new SolidColorBrush(_isNightVisual ? Color.Parse("#33FFFFFF") : Color.Parse("#16000000"));
foreach (var entry in _entryVisuals)
{
ApplyTextThemeForSystemNight(entry, _isNightVisual);
}
}
private void ApplyTextThemeForSystemNight(ClockEntryVisual entry, bool isSystemNight)
{
if (entry.IsSystemNightApplied.HasValue && entry.IsSystemNightApplied.Value == isSystemNight)
{
return;
}
entry.IsSystemNightApplied = isSystemNight;
var cityForeground = isSystemNight ? "#E8EAED" : "#20232A";
var dayForeground = isSystemNight ? "#A8B1C2" : "#646C79";
var offsetForeground = isSystemNight ? "#A8B1C2" : "#7A7F89";
entry.CityTextBlock.Foreground = CreateBrush(cityForeground);
entry.DayTextBlock.Foreground = CreateBrush(dayForeground);
entry.OffsetTextBlock.Foreground = CreateBrush(offsetForeground);
}
private void OnTimeZoneChanged(object? sender, EventArgs e) private void OnTimeZoneChanged(object? sender, EventArgs e)
{ {
_ = sender; _ = sender;