mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-22 00:54:26 +08:00
0.4.9.2
This commit is contained in:
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user