Revert "0.7.0.1"

This reverts commit ea8ce1f5ff.
This commit is contained in:
lincube
2026-03-20 14:12:40 +08:00
parent ea8ce1f5ff
commit 5d48a03f57
31 changed files with 602 additions and 2134 deletions

View File

@@ -7,7 +7,6 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
@@ -66,7 +65,6 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
private readonly LocalizationService _localizationService = new(); private readonly LocalizationService _localizationService = new();
private TimeZoneService? _timeZoneService; private TimeZoneService? _timeZoneService;
private double _currentCellSize = 48; private double _currentCellSize = 48;
private double _layoutScale = 1d;
private bool _dialInitialized; private bool _dialInitialized;
private bool _handsInitialized; private bool _handsInitialized;
private bool? _isNightModeApplied; private bool? _isNightModeApplied;
@@ -187,18 +185,17 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
private void BuildTicks(bool isNightMode) private void BuildTicks(bool isNightMode)
{ {
TickCanvas.Children.Clear(); TickCanvas.Children.Clear();
var scale = Math.Clamp(_layoutScale, 0.78d, 1.22d);
var majorBrush = CreateBrush(isNightMode ? "#1A1A1A" : "#1E2430"); var majorBrush = CreateBrush(isNightMode ? "#1A1A1A" : "#1E2430");
var minorBrush = CreateBrush(isNightMode ? "#D0D0D0" : "#D7DCE5"); var minorBrush = CreateBrush(isNightMode ? "#D0D0D0" : "#D7DCE5");
var majorThickness = (isNightMode ? 3.0 : 2.8) * scale; var majorThickness = isNightMode ? 3.0 : 2.8;
var minorThickness = (isNightMode ? 1.4 : 1.2) * scale; var minorThickness = isNightMode ? 1.4 : 1.2;
for (var i = 0; i < 60; i++) for (var i = 0; i < 60; i++)
{ {
var angle = (i * 6 - 90) * Math.PI / 180d; var angle = (i * 6 - 90) * Math.PI / 180d;
var isHourTick = i % 5 == 0; var isHourTick = i % 5 == 0;
var outerRadius = Center - (7 * scale); var outerRadius = Center - 7;
var innerRadius = outerRadius - (isHourTick ? 16 * scale : 8 * scale); var innerRadius = outerRadius - (isHourTick ? 16 : 8);
var x1 = Center + Math.Cos(angle) * innerRadius; var x1 = Center + Math.Cos(angle) * innerRadius;
var y1 = Center + Math.Sin(angle) * innerRadius; var y1 = Center + Math.Sin(angle) * innerRadius;
@@ -221,50 +218,34 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
private void BuildNumbers(bool isNightMode) private void BuildNumbers(bool isNightMode)
{ {
NumberCanvas.Children.Clear(); NumberCanvas.Children.Clear();
var scale = Math.Clamp(_layoutScale, 0.78d, 1.22d);
var foreground = CreateBrush(isNightMode ? "#101010" : "#0F131A"); var foreground = CreateBrush(isNightMode ? "#101010" : "#0F131A");
var fontWeight = isNightMode ? FontWeight.Bold : FontWeight.SemiBold; var fontWeight = isNightMode ? FontWeight.Bold : FontWeight.SemiBold;
for (var number = 1; number <= 12; number++) for (var number = 1; number <= 12; number++)
{ {
var angle = (number * 30 - 90) * Math.PI / 180d; var angle = (number * 30 - 90) * Math.PI / 180d;
var radius = 88 * scale; var radius = 88;
var x = Center + Math.Cos(angle) * radius; var x = Center + Math.Cos(angle) * radius;
var y = Center + Math.Sin(angle) * radius; var y = Center + Math.Sin(angle) * radius;
var isDoubleDigit = number >= 10; var isDoubleDigit = number >= 10;
var glyphBox = ComponentTypographyLayoutService.ResolveGlyphBox( var width = isDoubleDigit ? 44 : 28;
isDoubleDigit ? 44 * scale : 36 * scale, var height = 34;
34 * scale,
preferredSizeScale: isDoubleDigit ? 0.92d : 0.88d,
minSize: 18,
maxSize: isDoubleDigit ? 36 : 30,
insetScale: 0d);
var text = number.ToString(CultureInfo.InvariantCulture);
var fontSize = ComponentTypographyLayoutService.FitFontSize(
text,
glyphBox.Width,
glyphBox.Height,
maxLines: 1,
minFontSize: 12 * scale,
maxFontSize: 18 * scale,
weight: fontWeight,
lineHeightFactor: 1d);
var numberText = new TextBlock var text = new TextBlock
{ {
Text = text, Text = number.ToString(CultureInfo.InvariantCulture),
Width = glyphBox.Width, Width = width,
Height = glyphBox.Height, Height = height,
TextAlignment = TextAlignment.Center, TextAlignment = TextAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
FontSize = fontSize, FontSize = 18,
FontWeight = fontWeight, FontWeight = fontWeight,
Foreground = foreground Foreground = foreground
}; };
Canvas.SetLeft(numberText, x - glyphBox.Width / 2d); Canvas.SetLeft(text, x - width / 2d);
Canvas.SetTop(numberText, y - glyphBox.Height / 2d); Canvas.SetTop(text, y - height / 2d);
NumberCanvas.Children.Add(numberText); NumberCanvas.Children.Add(text);
} }
} }
@@ -344,14 +325,10 @@ public partial class AnalogClockWidget : UserControl, IDesktopComponentWidget, I
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
var chromeScale = ComponentChromeCornerRadiusHelper.ResolveScale();
_layoutScale = Math.Clamp(scale * (0.9d + (chromeScale * 0.1d)), 0.58d, 2.0d);
RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(42 * _layoutScale, 16, 56); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(42 * scale, 16, 56);
RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(14 * _layoutScale, 14 * _layoutScale, null, 0.55d); RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(14 * scale, 14 * scale, null, 0.55d);
ApplyModeVisualIfNeeded(); ApplyModeVisualIfNeeded();
BuildTicks(_isNightModeApplied ?? ResolveIsNightMode());
BuildNumbers(_isNightModeApplied ?? ResolveIsNightMode());
} }
private double ResolveScale() private double ResolveScale()

View File

@@ -13,7 +13,6 @@ using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -433,61 +432,29 @@ public partial class BaiduHotSearchWidget : UserControl, IDesktopComponentWidget
RefreshGlyphIcon.FontSize = Math.Clamp(refreshButtonSize * 0.46, 10, 20); RefreshGlyphIcon.FontSize = Math.Clamp(refreshButtonSize * 0.46, 10, 20);
var lineColumnGap = Math.Clamp(lineRowHeight * 0.34, 5, 12); var lineColumnGap = Math.Clamp(lineRowHeight * 0.34, 5, 12);
var indexBadge = ComponentTypographyLayoutService.ResolveBadgeBox( var indexWidth = Math.Clamp(lineRowHeight * 1.02, 16, 28);
lineRowHeight, var indexFont = Math.Clamp(lineRowHeight * 0.50, 10, 16);
lineRowHeight, var itemFont = Math.Clamp(lineRowHeight * 0.62, 12, 24);
preferredSizeScale: 0.62d,
minSize: 16,
maxSize: 28);
var indexFont = ComponentTypographyLayoutService.FitFontSize(
"88",
indexBadge.Width,
indexBadge.Height,
1,
minFontSize: 10,
maxFontSize: Math.Clamp(indexBadge.Height * 0.82d, 10, 18),
weight: FontWeight.Bold,
lineHeightFactor: 1.0d,
fontFamily: MiSansFontFamily);
var itemFontMin = Math.Clamp(lineRowHeight * 0.42, 11, 16);
var itemFontMax = Math.Clamp(lineRowHeight * 0.68, 12, 24);
var rowPadding = Math.Clamp(lineRowHeight * 0.08, 1, 4); var rowPadding = Math.Clamp(lineRowHeight * 0.08, 1, 4);
var itemTextWidth = Math.Max(56, innerWidth - indexBadge.Width - lineColumnGap); var itemTextWidth = Math.Max(56, innerWidth - indexWidth - lineColumnGap);
foreach (var visual in _hotItemVisuals) foreach (var visual in _hotItemVisuals)
{ {
visual.RowGrid.ColumnSpacing = lineColumnGap; visual.RowGrid.ColumnSpacing = lineColumnGap;
if (visual.RowGrid.ColumnDefinitions.Count > 0) if (visual.RowGrid.ColumnDefinitions.Count > 0)
{ {
visual.RowGrid.ColumnDefinitions[0].Width = new GridLength(indexBadge.Width, GridUnitType.Pixel); visual.RowGrid.ColumnDefinitions[0].Width = new GridLength(indexWidth, GridUnitType.Pixel);
} }
visual.Host.Padding = new Thickness(0, rowPadding, 0, rowPadding); visual.Host.Padding = new Thickness(0, rowPadding, 0, rowPadding);
visual.IndexTextBlock.FontSize = indexFont; visual.IndexTextBlock.FontSize = indexFont;
visual.IndexTextBlock.MaxWidth = indexBadge.Width; visual.IndexTextBlock.MaxWidth = indexWidth;
visual.IndexTextBlock.MinWidth = indexBadge.Width; visual.TitleTextBlock.FontSize = itemFont;
visual.IndexTextBlock.Margin = indexBadge.Margin;
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
visual.TitleTextBlock.Text,
itemTextWidth,
lineRowHeight * 1.9d,
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(visual.TitleTextBlock.Text) > 24 ? 2 : 1,
minFontSize: itemFontMin,
maxFontSize: itemFontMax,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.08d,
fontFamily: MiSansFontFamily);
visual.TitleTextBlock.FontSize = titleLayout.FontSize;
visual.TitleTextBlock.LineHeight = titleLayout.LineHeight;
visual.TitleTextBlock.MaxLines = titleLayout.MaxLines;
visual.TitleTextBlock.FontWeight = titleLayout.Weight;
visual.TitleTextBlock.MaxWidth = itemTextWidth; visual.TitleTextBlock.MaxWidth = itemTextWidth;
visual.TitleTextBlock.TextAlignment = TextAlignment.Left; visual.TitleTextBlock.TextAlignment = TextAlignment.Left;
} }
StatusTextBlock.FontSize = Math.Clamp(itemFontMax, 10, 20); StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20);
ApplyNightModeVisual(); ApplyNightModeVisual();
} }

View File

@@ -11,7 +11,6 @@ using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -438,93 +437,37 @@ public partial class BilibiliHotSearchWidget : UserControl, IDesktopComponentWid
Math.Clamp(searchBoxHeight * 0.24, 5, 10), Math.Clamp(searchBoxHeight * 0.24, 5, 10),
0); 0);
SearchGlyphIcon.FontSize = Math.Clamp(searchBoxHeight * 0.45, 10, 20); SearchGlyphIcon.FontSize = Math.Clamp(searchBoxHeight * 0.45, 10, 20);
SearchEntryTextBlock.FontSize = Math.Clamp(searchBoxHeight * 0.44, 10, 18);
var searchLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
SearchEntryTextBlock.Text,
Math.Max(54, SearchBoxBorder.Width - Math.Clamp(searchBoxHeight * 0.48, 8, 16)),
searchBoxHeight,
minLines: 1,
maxLines: 1,
minFontSize: 10,
maxFontSize: Math.Clamp(searchBoxHeight * 0.44, 10, 18),
weightCandidates: new[] { FontWeight.Medium, FontWeight.SemiBold },
lineHeightFactor: 1.0d,
fontFamily: MiSansFontFamily);
SearchEntryTextBlock.FontSize = searchLayout.FontSize;
SearchEntryTextBlock.LineHeight = searchLayout.LineHeight;
TopRightTitleTextBlock.MaxWidth = Math.Max(80, innerWidth - SearchBoxBorder.Width - HeaderGrid.ColumnSpacing); TopRightTitleTextBlock.MaxWidth = Math.Max(80, innerWidth - SearchBoxBorder.Width - HeaderGrid.ColumnSpacing);
var topRightLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( TopRightTitleTextBlock.FontSize = Math.Clamp(topRowHeight * 0.46, 11, 22);
TopRightTitleTextBlock.Text,
TopRightTitleTextBlock.MaxWidth,
topRowHeight,
minLines: 1,
maxLines: 1,
minFontSize: 11,
maxFontSize: Math.Clamp(topRowHeight * 0.46, 11, 22),
weightCandidates: new[] { FontWeight.Medium, FontWeight.SemiBold },
lineHeightFactor: 1.0d,
fontFamily: MiSansFontFamily);
TopRightTitleTextBlock.FontSize = topRightLayout.FontSize;
TopRightTitleTextBlock.LineHeight = topRightLayout.LineHeight;
var lineColumnGap = Math.Clamp(lineRowHeight * 0.34, 5, 12); var lineColumnGap = Math.Clamp(lineRowHeight * 0.34, 5, 12);
var indexBadge = ComponentTypographyLayoutService.ResolveBadgeBox( var indexWidth = Math.Clamp(lineRowHeight * 1.02, 16, 28);
lineRowHeight, var indexFont = Math.Clamp(lineRowHeight * 0.50, 10, 16);
lineRowHeight, var itemFont = Math.Clamp(lineRowHeight * 0.62, 12, 24);
preferredSizeScale: 0.62d,
minSize: 16,
maxSize: 28);
var indexFont = ComponentTypographyLayoutService.FitFontSize(
"88",
indexBadge.Width,
indexBadge.Height,
1,
minFontSize: 10,
maxFontSize: Math.Clamp(indexBadge.Height * 0.82d, 10, 18),
weight: FontWeight.Bold,
lineHeightFactor: 1.0d,
fontFamily: MiSansFontFamily);
var itemFontMin = Math.Clamp(lineRowHeight * 0.42, 11, 16);
var itemFontMax = Math.Clamp(lineRowHeight * 0.68, 12, 24);
var rowPadding = Math.Clamp(lineRowHeight * 0.08, 1, 4); var rowPadding = Math.Clamp(lineRowHeight * 0.08, 1, 4);
var itemTextWidth = Math.Max(56, innerWidth - indexBadge.Width - lineColumnGap); var itemTextWidth = Math.Max(56, innerWidth - indexWidth - lineColumnGap);
foreach (var visual in _hotItemVisuals) foreach (var visual in _hotItemVisuals)
{ {
visual.RowGrid.ColumnSpacing = lineColumnGap; visual.RowGrid.ColumnSpacing = lineColumnGap;
if (visual.RowGrid.ColumnDefinitions.Count > 0) if (visual.RowGrid.ColumnDefinitions.Count > 0)
{ {
visual.RowGrid.ColumnDefinitions[0].Width = new GridLength(indexBadge.Width, GridUnitType.Pixel); visual.RowGrid.ColumnDefinitions[0].Width = new GridLength(indexWidth, GridUnitType.Pixel);
} }
visual.Host.Padding = new Thickness(0, rowPadding, 0, rowPadding); visual.Host.Padding = new Thickness(0, rowPadding, 0, rowPadding);
visual.IndexTextBlock.FontSize = indexFont; visual.IndexTextBlock.FontSize = indexFont;
visual.IndexTextBlock.MaxWidth = indexBadge.Width; visual.IndexTextBlock.MaxWidth = indexWidth;
visual.IndexTextBlock.MinWidth = indexBadge.Width;
visual.IndexTextBlock.Margin = indexBadge.Margin;
visual.IndexTextBlock.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right; visual.IndexTextBlock.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right;
visual.IndexTextBlock.TextAlignment = TextAlignment.Right; visual.IndexTextBlock.TextAlignment = TextAlignment.Right;
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( visual.TitleTextBlock.FontSize = itemFont;
visual.TitleTextBlock.Text,
itemTextWidth,
lineRowHeight * 1.9d,
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(visual.TitleTextBlock.Text) > 24 ? 2 : 1,
minFontSize: itemFontMin,
maxFontSize: itemFontMax,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.08d,
fontFamily: MiSansFontFamily);
visual.TitleTextBlock.FontSize = titleLayout.FontSize;
visual.TitleTextBlock.LineHeight = titleLayout.LineHeight;
visual.TitleTextBlock.MaxLines = titleLayout.MaxLines;
visual.TitleTextBlock.FontWeight = titleLayout.Weight;
visual.TitleTextBlock.MaxWidth = itemTextWidth; visual.TitleTextBlock.MaxWidth = itemTextWidth;
visual.TitleTextBlock.TextAlignment = TextAlignment.Left; visual.TitleTextBlock.TextAlignment = TextAlignment.Left;
} }
StatusTextBlock.FontSize = Math.Clamp(itemFontMax, 10, 20); StatusTextBlock.FontSize = Math.Clamp(itemFont, 10, 20);
ApplyNightModeVisual(); ApplyNightModeVisual();
} }

View File

@@ -6,7 +6,6 @@ using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using AvaloniaWebView; using AvaloniaWebView;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using WebViewCore.Events; using WebViewCore.Events;
@@ -53,7 +52,6 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
} }
AddressTextBox.Text = DefaultHomeUri.ToString(); AddressTextBox.Text = DefaultHomeUri.ToString();
UpdateAddressTypography();
UpdateWebViewActiveState(); UpdateWebViewActiveState();
} }
@@ -118,7 +116,6 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
AddressTextBox.FontSize = Math.Clamp(_currentCellSize * 0.30, 12, 15); AddressTextBox.FontSize = Math.Clamp(_currentCellSize * 0.30, 12, 15);
AddressTextBox.Height = buttonSize; AddressTextBox.Height = buttonSize;
UpdateAddressTypography();
} }
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode) public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
@@ -286,7 +283,6 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
{ {
_lastKnownUri = uri; _lastKnownUri = uri;
AddressTextBox.Text = uri.ToString(); AddressTextBox.Text = uri.ToString();
UpdateAddressTypography();
if (_isWebViewActive) if (_isWebViewActive)
{ {
TryNavigate(uri, "NavigateTo"); TryNavigate(uri, "NavigateTo");
@@ -302,7 +298,6 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
_lastKnownUri = e.Url; _lastKnownUri = e.Url;
AddressTextBox.Text = e.Url.ToString(); AddressTextBox.Text = e.Url.ToString();
UpdateAddressTypography();
} }
private void UpdateWebViewActiveState() private void UpdateWebViewActiveState()
@@ -412,7 +407,6 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
GoButton.IsEnabled = false; GoButton.IsEnabled = false;
AddressTextBox.IsEnabled = false; AddressTextBox.IsEnabled = false;
AddressTextBox.Text = _lastKnownUri.ToString(); AddressTextBox.Text = _lastKnownUri.ToString();
UpdateAddressTypography();
UnavailableMessageTextBlock.Text = _isWebViewFaulted UnavailableMessageTextBlock.Text = _isWebViewFaulted
? "The browser component is temporarily unavailable. Restart the app to retry." ? "The browser component is temporarily unavailable. Restart the app to retry."
@@ -457,25 +451,4 @@ public partial class BrowserWidget : UserControl, IDesktopComponentWidget,
? uri ? uri
: null; : null;
} }
private void UpdateAddressTypography()
{
var maxWidth = AddressTextBox.Bounds.Width > 1
? AddressTextBox.Bounds.Width
: Math.Max(120, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 7) - 120);
var maxHeight = AddressTextBox.Bounds.Height > 1
? AddressTextBox.Bounds.Height
: Math.Max(20, AddressTextBox.Height);
AddressTextBox.FontSize = ComponentTypographyLayoutService.FitFontSize(
AddressTextBox.Text,
maxWidth,
maxHeight,
maxLines: 1,
minFontSize: 12,
maxFontSize: 16,
weight: FontWeight.Normal,
lineHeightFactor: 1.06d,
fontFamily: AddressTextBox.FontFamily);
}
} }

View File

@@ -8,7 +8,6 @@ using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
@@ -44,7 +43,6 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
private TimeZoneService? _timeZoneService; private TimeZoneService? _timeZoneService;
private double _currentCellSize = 48; private double _currentCellSize = 48;
private double _layoutScale = 1d;
private IReadOnlyList<CourseItemViewModel> _courseItems = Array.Empty<CourseItemViewModel>(); private IReadOnlyList<CourseItemViewModel> _courseItems = Array.Empty<CourseItemViewModel>();
private bool _isNightVisual = true; private bool _isNightVisual = true;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
@@ -495,20 +493,18 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var useMonetColor = ComponentColorSchemeHelper.ShouldUseMonetColor( var useMonetColor = ComponentColorSchemeHelper.ShouldUseMonetColor(
_componentColorScheme, _componentColorScheme,
ComponentColorSchemeHelper.GetCurrentGlobalThemeColorMode()); ComponentColorSchemeHelper.GetCurrentGlobalThemeColorMode());
var chromeScale = ComponentChromeCornerRadiusHelper.ResolveScale();
_layoutScale = Math.Clamp(ResolveScale() * (0.9d + (chromeScale * 0.1d)), 0.52d, 2.2d);
var bulletSize = Math.Clamp(10 * _layoutScale, 5, 12); var scale = ResolveScale();
var lineSpacing = Math.Clamp(4 * _layoutScale, 1.5, 8); var bulletSize = Math.Clamp(10 * scale, 5, 12);
var courseNameSize = Math.Clamp(42 * scale, 14, 42);
var secondarySize = Math.Clamp(29 * scale, 10, 28);
var lineSpacing = Math.Clamp(4 * scale, 1.5, 8);
var itemPadding = new Thickness( var itemPadding = new Thickness(
ComponentChromeCornerRadiusHelper.SafeValue(6 * _layoutScale, 3, 10), ComponentChromeCornerRadiusHelper.SafeValue(6 * scale, 3, 10),
ComponentChromeCornerRadiusHelper.SafeValue(4 * _layoutScale, 2, 8), ComponentChromeCornerRadiusHelper.SafeValue(4 * scale, 2, 8),
ComponentChromeCornerRadiusHelper.SafeValue(4 * _layoutScale, 2, 8), ComponentChromeCornerRadiusHelper.SafeValue(4 * scale, 2, 8),
ComponentChromeCornerRadiusHelper.SafeValue(4 * _layoutScale, 2, 8)); ComponentChromeCornerRadiusHelper.SafeValue(4 * scale, 2, 8));
var maxVisibleItems = ResolveMaxVisibleItems(_layoutScale); var maxVisibleItems = ResolveMaxVisibleItems(scale);
var itemContentWidth = Math.Max(28, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 4) - itemPadding.Left - itemPadding.Right - bulletSize - Math.Clamp(10 * _layoutScale, 4, 14));
var titleHeight = Math.Clamp(34 * _layoutScale, 16, 42);
var secondaryHeight = Math.Clamp(24 * _layoutScale, 12, 30);
var primaryBrush = CreateBrush(_isNightVisual ? "#F9FBFF" : "#151821"); var primaryBrush = CreateBrush(_isNightVisual ? "#F9FBFF" : "#151821");
var secondaryBrush = CreateBrush(_isNightVisual ? "#848B99" : "#667084"); var secondaryBrush = CreateBrush(_isNightVisual ? "#848B99" : "#667084");
@@ -529,32 +525,14 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
CornerRadius = new CornerRadius(bulletSize * 0.5), CornerRadius = new CornerRadius(bulletSize * 0.5),
Background = bulletBrush, Background = bulletBrush,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top, VerticalAlignment = Avalonia.Layout.VerticalAlignment.Top,
Margin = new Thickness(0, Math.Clamp(8 * _layoutScale, 2, 12), 0, 0) Margin = new Thickness(0, Math.Clamp(8 * scale, 2, 12), 0, 0)
}; };
var titleText = new TextBlock var titleText = new TextBlock
{ {
Text = item.Name, Text = item.Name,
FontSize = ComponentTypographyLayoutService.FitAdaptiveTextLayout( FontSize = courseNameSize,
item.Name, FontWeight = ToVariableWeight(Lerp(620, 780, Math.Clamp((scale - 0.60) / 1.2, 0, 1))),
itemContentWidth,
titleHeight,
minLines: 1,
maxLines: 2,
minFontSize: 14,
maxFontSize: Math.Clamp(42 * _layoutScale, 14, 42),
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1.05d).FontSize,
FontWeight = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
item.Name,
itemContentWidth,
titleHeight,
minLines: 1,
maxLines: 2,
minFontSize: 14,
maxFontSize: Math.Clamp(42 * _layoutScale, 14, 42),
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1.05d).Weight,
Foreground = primaryBrush, Foreground = primaryBrush,
TextTrimming = TextTrimming.CharacterEllipsis, TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.NoWrap TextWrapping = TextWrapping.NoWrap
@@ -563,26 +541,8 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var timeText = new TextBlock var timeText = new TextBlock
{ {
Text = item.TimeRange, Text = item.TimeRange,
FontSize = ComponentTypographyLayoutService.FitAdaptiveTextLayout( FontSize = secondarySize,
item.TimeRange, FontWeight = ToVariableWeight(Lerp(520, 680, Math.Clamp((scale - 0.60) / 1.2, 0, 1))),
itemContentWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 10,
maxFontSize: Math.Clamp(28 * _layoutScale, 10, 28),
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 1d).FontSize,
FontWeight = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
item.TimeRange,
itemContentWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 10,
maxFontSize: Math.Clamp(28 * _layoutScale, 10, 28),
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 1d).Weight,
Foreground = secondaryBrush, Foreground = secondaryBrush,
TextTrimming = TextTrimming.CharacterEllipsis, TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.NoWrap TextWrapping = TextWrapping.NoWrap
@@ -591,26 +551,8 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var detailText = new TextBlock var detailText = new TextBlock
{ {
Text = item.Detail, Text = item.Detail,
FontSize = ComponentTypographyLayoutService.FitAdaptiveTextLayout( FontSize = secondarySize,
item.Detail, FontWeight = ToVariableWeight(Lerp(500, 640, Math.Clamp((scale - 0.60) / 1.2, 0, 1))),
itemContentWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 10,
maxFontSize: Math.Clamp(28 * _layoutScale, 10, 28),
weightCandidates: new[] { FontWeight.Normal, FontWeight.Medium },
lineHeightFactor: 1d).FontSize,
FontWeight = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
item.Detail,
itemContentWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 10,
maxFontSize: Math.Clamp(28 * _layoutScale, 10, 28),
weightCandidates: new[] { FontWeight.Normal, FontWeight.Medium },
lineHeightFactor: 1d).Weight,
Foreground = secondaryBrush, Foreground = secondaryBrush,
TextTrimming = TextTrimming.CharacterEllipsis, TextTrimming = TextTrimming.CharacterEllipsis,
TextWrapping = TextWrapping.NoWrap TextWrapping = TextWrapping.NoWrap
@@ -625,7 +567,7 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var itemGrid = new Grid var itemGrid = new Grid
{ {
ColumnDefinitions = new ColumnDefinitions("Auto,*"), ColumnDefinitions = new ColumnDefinitions("Auto,*"),
ColumnSpacing = Math.Clamp(10 * _layoutScale, 4, 14) ColumnSpacing = Math.Clamp(10 * scale, 4, 14)
}; };
itemGrid.Children.Add(bullet); itemGrid.Children.Add(bullet);
itemGrid.Children.Add(textStack); itemGrid.Children.Add(textStack);
@@ -661,8 +603,6 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
} }
var scale = ResolveScale(); var scale = ResolveScale();
var chromeScale = ComponentChromeCornerRadiusHelper.ResolveScale();
_layoutScale = Math.Clamp(scale * (0.9d + (chromeScale * 0.1d)), 0.52d, 2.2d);
_isNightVisual = ResolveNightMode(); _isNightVisual = ResolveNightMode();
var useMonetColor = ComponentColorSchemeHelper.ShouldUseMonetColor( var useMonetColor = ComponentColorSchemeHelper.ShouldUseMonetColor(
@@ -672,66 +612,6 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
var slashBrush = useMonetColor var slashBrush = useMonetColor
? CreateBrush("#FF4FC3F7") ? CreateBrush("#FF4FC3F7")
: CreateBrush("#FF3250"); : CreateBrush("#FF3250");
var sampleNow = _timeZoneService?.GetCurrentTime() ?? DateTime.Now;
var headerWidth = Math.Max(42, Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 4);
var headerHeight = Math.Max(42, Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 4);
var headerPrimaryWidth = Math.Clamp(headerWidth * 0.34, 28, 92);
var headerSecondaryWidth = Math.Clamp(headerWidth * 0.52, 40, 148);
var dateHeight = Math.Clamp(headerHeight * 0.30, 26, 96);
var secondaryHeaderHeight = Math.Clamp(headerHeight * 0.12, 16, 42);
var weekdayLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
FormatWeekday(sampleNow.DayOfWeek),
headerSecondaryWidth,
secondaryHeaderHeight,
minLines: 1,
maxLines: 1,
minFontSize: 13,
maxFontSize: 32,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1.02d);
var classCountLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
FormatClassCount(_courseItems.Count),
headerSecondaryWidth,
secondaryHeaderHeight,
minLines: 1,
maxLines: 1,
minFontSize: 14,
maxFontSize: 36,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1.02d);
var statusText = string.IsNullOrWhiteSpace(StatusTextBlock.Text)
? L("schedule.widget.no_class_today", "浠婂ぉ娌℃湁璇剧▼")
: StatusTextBlock.Text;
var statusLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
statusText,
headerSecondaryWidth,
secondaryHeaderHeight,
minLines: 1,
maxLines: 1,
minFontSize: 12,
maxFontSize: 30,
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 1.02d);
var monthLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
sampleNow.Month.ToString(CultureInfo.InvariantCulture),
headerPrimaryWidth,
dateHeight,
minLines: 1,
maxLines: 1,
minFontSize: 26,
maxFontSize: 82,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1d);
var dayLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
sampleNow.Day.ToString(CultureInfo.InvariantCulture),
headerPrimaryWidth,
dateHeight,
minLines: 1,
maxLines: 1,
minFontSize: 26,
maxFontSize: 82,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1d);
RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.45, 24, 44); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(_currentCellSize * 0.45, 24, 44);
RootBorder.Background = _isNightVisual RootBorder.Background = _isNightVisual
@@ -740,21 +620,21 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
RootBorder.BorderBrush = CreateBrush(_isNightVisual ? "#24FFFFFF" : "#15000000"); RootBorder.BorderBrush = CreateBrush(_isNightVisual ? "#24FFFFFF" : "#15000000");
var rootPadding = new Thickness( var rootPadding = new Thickness(
ComponentChromeCornerRadiusHelper.SafeValue(16 * _layoutScale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
ComponentChromeCornerRadiusHelper.SafeValue(14 * _layoutScale, 9, 20), ComponentChromeCornerRadiusHelper.SafeValue(14 * scale, 9, 20),
ComponentChromeCornerRadiusHelper.SafeValue(16 * _layoutScale, 10, 24), ComponentChromeCornerRadiusHelper.SafeValue(16 * scale, 10, 24),
ComponentChromeCornerRadiusHelper.SafeValue(14 * _layoutScale, 8, 20)); ComponentChromeCornerRadiusHelper.SafeValue(14 * scale, 8, 20));
RootBorder.Padding = rootPadding; RootBorder.Padding = rootPadding;
LayoutGrid.RowSpacing = Math.Clamp(14 * _layoutScale, 6, 20); LayoutGrid.RowSpacing = Math.Clamp(14 * scale, 6, 20);
HeaderGrid.ColumnSpacing = Math.Clamp(10 * _layoutScale, 4, 16); HeaderGrid.ColumnSpacing = Math.Clamp(10 * scale, 4, 16);
DateGroup.Spacing = Math.Clamp(1.5 * _layoutScale, 0.5, 3); DateGroup.Spacing = Math.Clamp(1.5 * scale, 0.5, 3);
MetaStack.Spacing = Math.Clamp(6 * _layoutScale, 3, 10); MetaStack.Spacing = Math.Clamp(6 * scale, 3, 10);
CourseListPanel.Spacing = Math.Clamp(6 * _layoutScale, 3, 10); CourseListPanel.Spacing = Math.Clamp(6 * scale, 3, 10);
var dateFont = Math.Clamp(66 * _layoutScale, 26, 82); var dateFont = Math.Clamp(66 * scale, 26, 82);
MonthTextBlock.FontSize = monthLayout.FontSize; MonthTextBlock.FontSize = dateFont;
DayTextBlock.FontSize = dayLayout.FontSize; DayTextBlock.FontSize = dateFont;
SlashTextBlock.FontSize = dateFont; SlashTextBlock.FontSize = dateFont;
MonthTextBlock.Foreground = CreateBrush(_isNightVisual ? "#F8FAFF" : "#131722"); MonthTextBlock.Foreground = CreateBrush(_isNightVisual ? "#F8FAFF" : "#131722");
@@ -764,13 +644,12 @@ public partial class ClassScheduleWidget : UserControl, IDesktopComponentWidget,
ClassCountTextBlock.Foreground = CreateBrush(_isNightVisual ? "#8D95A4" : "#738095"); ClassCountTextBlock.Foreground = CreateBrush(_isNightVisual ? "#8D95A4" : "#738095");
StatusTextBlock.Foreground = CreateBrush(_isNightVisual ? "#9AA2B1" : "#4B5565"); StatusTextBlock.Foreground = CreateBrush(_isNightVisual ? "#9AA2B1" : "#4B5565");
WeekdayTextBlock.FontSize = weekdayLayout.FontSize; WeekdayTextBlock.FontSize = Math.Clamp(34 * scale, 13, 32);
ClassCountTextBlock.FontSize = classCountLayout.FontSize; ClassCountTextBlock.FontSize = Math.Clamp(40 * scale, 14, 36);
StatusTextBlock.FontSize = statusLayout.FontSize; StatusTextBlock.FontSize = Math.Clamp(30 * scale, 12, 30);
WeekdayTextBlock.FontWeight = weekdayLayout.Weight; WeekdayTextBlock.FontWeight = ToVariableWeight(Lerp(560, 700, Math.Clamp((scale - 0.60) / 1.2, 0, 1)));
ClassCountTextBlock.FontWeight = classCountLayout.Weight; ClassCountTextBlock.FontWeight = ToVariableWeight(Lerp(560, 680, Math.Clamp((scale - 0.60) / 1.2, 0, 1)));
StatusTextBlock.FontWeight = statusLayout.Weight;
} }
private static string FormatTime(TimeSpan time) private static string FormatTime(TimeSpan time)

View File

@@ -4,7 +4,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
@@ -121,13 +120,11 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
SecondsTextBlock.Text = now.ToString("ss", CultureInfo.CurrentCulture); SecondsTextBlock.Text = now.ToString("ss", CultureInfo.CurrentCulture);
SecondsTextBlock.IsVisible = _displayFormat == ClockDisplayFormat.HourMinuteSecond; SecondsTextBlock.IsVisible = _displayFormat == ClockDisplayFormat.HourMinuteSecond;
ApplyTypographyLayout();
} }
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
{ {
_lastAppliedCellSize = cellSize; _lastAppliedCellSize = cellSize;
var layoutScale = Math.Clamp((cellSize / 44d) * (0.9d + (ComponentChromeCornerRadiusHelper.ResolveScale() * 0.1d)), 0.65d, 1.95d);
// --- Class Island “满盈”风格算法 --- // --- Class Island “满盈”风格算法 ---
@@ -141,7 +138,7 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
// 3. 核心:满盈字阶 (Filled Typography) // 3. 核心:满盈字阶 (Filled Typography)
// 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力 // 使主时间文字占据容器高度的 ~68%,产生饱满的视觉张力
var mainFontSize = targetHeight * 0.68 * layoutScale; var mainFontSize = targetHeight * 0.68;
MainTimeTextBlock.FontSize = mainFontSize; MainTimeTextBlock.FontSize = mainFontSize;
MainTimeTextBlock.FontWeight = FontWeight.SemiBold; MainTimeTextBlock.FontWeight = FontWeight.SemiBold;
@@ -155,74 +152,19 @@ public partial class ClockWidget : UserControl, IDesktopComponentWidget, ITimeZo
// 6. 间距微调 // 6. 间距微调
if (MainTimeTextBlock.Parent is StackPanel panel) if (MainTimeTextBlock.Parent is StackPanel panel)
{ {
panel.Spacing = Math.Clamp(cellSize * 0.06 * layoutScale, 2, 8); panel.Spacing = Math.Clamp(cellSize * 0.06, 2, 8);
} }
if (_transparentBackground) if (_transparentBackground)
{ {
RootBorder.MinWidth = 0; RootBorder.MinWidth = 0;
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.06 * layoutScale, 4, 10), 0); RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.06, 4, 10), 0);
return; return;
} }
// 确保清除可能存在的固定 Padding由代码控制“紧密感” // 确保清除可能存在的固定 Padding由代码控制“紧密感”
RootBorder.MinWidth = cellSize * 2.2; RootBorder.MinWidth = cellSize * 2.2;
RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.15 * layoutScale, 12, 24), 0); RootBorder.Padding = new Thickness(Math.Clamp(cellSize * 0.15, 12, 24), 0);
ApplyTypographyLayout();
}
private void ApplyTypographyLayout()
{
var layoutScale = Math.Clamp((_lastAppliedCellSize / 44d) * (0.9d + (ComponentChromeCornerRadiusHelper.ResolveScale() * 0.1d)), 0.65d, 1.95d);
var availableWidth = Math.Max(1, RootBorder.Bounds.Width > 1 ? RootBorder.Bounds.Width : Math.Max(1, _lastAppliedCellSize * 2.2));
var availableHeight = Math.Max(1, RootBorder.Bounds.Height > 1 ? RootBorder.Bounds.Height : Math.Clamp(_lastAppliedCellSize * 0.74, 34, 74));
var contentWidth = Math.Max(1, availableWidth - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(1, availableHeight - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var mainLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
MainTimeTextBlock.Text,
contentWidth * (SecondsTextBlock.IsVisible ? 0.78d : 0.84d),
contentHeight * 0.80d,
minLines: 1,
maxLines: 1,
minFontSize: Math.Clamp(18 * layoutScale, 16, 28),
maxFontSize: Math.Clamp(44 * layoutScale, 28, 64),
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 0.96d);
MainTimeTextBlock.FontSize = mainLayout.FontSize;
MainTimeTextBlock.FontWeight = mainLayout.Weight;
if (SecondsTextBlock.IsVisible)
{
var secondsLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
SecondsTextBlock.Text,
contentWidth * 0.28d,
contentHeight * 0.46d,
minLines: 1,
maxLines: 1,
minFontSize: Math.Clamp(11 * layoutScale, 9, 18),
maxFontSize: Math.Clamp(28 * layoutScale, 14, 34),
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 0.96d);
SecondsTextBlock.FontSize = secondsLayout.FontSize;
SecondsTextBlock.FontWeight = secondsLayout.Weight;
SecondsTextBlock.Opacity = 0.55;
}
if (MainTimeTextBlock.Parent is StackPanel panel)
{
panel.Spacing = Math.Clamp(contentHeight * 0.06 * layoutScale, 2, 8);
}
if (_transparentBackground)
{
RootBorder.MinWidth = 0;
RootBorder.Padding = new Thickness(Math.Clamp(_lastAppliedCellSize * 0.06 * layoutScale, 4, 10), 0);
return;
}
RootBorder.MinWidth = _lastAppliedCellSize * 2.2;
RootBorder.Padding = new Thickness(Math.Clamp(_lastAppliedCellSize * 0.15 * layoutScale, 12, 24), 0);
} }
private void ApplyChrome() private void ApplyChrome()

View File

@@ -16,7 +16,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -600,40 +599,17 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
News2TitleTextBlock.MaxWidth = availableTextWidth; News2TitleTextBlock.MaxWidth = availableTextWidth;
var newsFont = Math.Clamp(21 * scale, 10.5, 28); var newsFont = Math.Clamp(21 * scale, 10.5, 28);
var newsHeightBudget = Math.Max(28, imageHeight + columnGap * 2d); News1TitleTextBlock.FontSize = newsFont;
var news1Layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( News2TitleTextBlock.FontSize = newsFont;
News1TitleTextBlock.Text, var mainNewsLineHeight = newsFont * 1.14;
availableTextWidth, News1TitleTextBlock.LineHeight = mainNewsLineHeight;
newsHeightBudget, News2TitleTextBlock.LineHeight = mainNewsLineHeight;
minLines: 1, var mainNewsMinHeight = mainNewsLineHeight * 2;
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(News1TitleTextBlock.Text) > 30 ? 2 : 1, News1TitleTextBlock.MinHeight = mainNewsMinHeight;
minFontSize: Math.Clamp(newsFont * 0.72, 10.5, 18), News2TitleTextBlock.MinHeight = mainNewsMinHeight;
maxFontSize: newsFont,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.14d,
fontFamily: MiSansFontFamily);
var news2Layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
News2TitleTextBlock.Text,
availableTextWidth,
newsHeightBudget,
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(News2TitleTextBlock.Text) > 30 ? 2 : 1,
minFontSize: Math.Clamp(newsFont * 0.72, 10.5, 18),
maxFontSize: newsFont,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.14d,
fontFamily: MiSansFontFamily);
News1TitleTextBlock.FontSize = news1Layout.FontSize;
News1TitleTextBlock.LineHeight = news1Layout.LineHeight;
News1TitleTextBlock.MinHeight = news1Layout.LineHeight * news1Layout.MaxLines;
News1TitleTextBlock.MaxLines = news1Layout.MaxLines;
News1TitleTextBlock.FontWeight = news1Layout.Weight;
News2TitleTextBlock.FontSize = news2Layout.FontSize;
News2TitleTextBlock.LineHeight = news2Layout.LineHeight;
News2TitleTextBlock.MinHeight = news2Layout.LineHeight * news2Layout.MaxLines;
News2TitleTextBlock.MaxLines = news2Layout.MaxLines;
News2TitleTextBlock.FontWeight = news2Layout.Weight;
StatusTextBlock.FontSize = Math.Clamp(16 * scale, 9, 24); StatusTextBlock.FontSize = Math.Clamp(16 * scale, 9, 24);
News1TitleTextBlock.MaxLines = 2;
News2TitleTextBlock.MaxLines = 2;
foreach (var row in _extraNewsRows) foreach (var row in _extraNewsRows)
{ {
@@ -647,23 +623,11 @@ public partial class CnrDailyNewsWidget : UserControl, IDesktopComponentWidget,
row.ImageHost.Height = imageHeight; row.ImageHost.Height = imageHeight;
row.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22); row.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 8, 22);
var rowTitleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
row.TitleTextBlock.Text,
availableTextWidth,
Math.Max(32, imageHeight + columnGap),
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(row.TitleTextBlock.Text) > 28 ? 2 : 1,
minFontSize: Math.Clamp(19 * scale, 10, 16),
maxFontSize: Math.Clamp(19 * scale, 10, 25),
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.12d,
fontFamily: MiSansFontFamily);
row.TitleTextBlock.MaxWidth = availableTextWidth; row.TitleTextBlock.MaxWidth = availableTextWidth;
row.TitleTextBlock.FontSize = rowTitleLayout.FontSize; row.TitleTextBlock.FontSize = Math.Clamp(19 * scale, 10, 25);
row.TitleTextBlock.LineHeight = rowTitleLayout.LineHeight; row.TitleTextBlock.LineHeight = row.TitleTextBlock.FontSize * 1.12;
row.TitleTextBlock.MinHeight = rowTitleLayout.LineHeight * rowTitleLayout.MaxLines; row.TitleTextBlock.MinHeight = row.TitleTextBlock.LineHeight * 2;
row.TitleTextBlock.MaxLines = rowTitleLayout.MaxLines; row.TitleTextBlock.MaxLines = 2;
row.TitleTextBlock.FontWeight = rowTitleLayout.Weight;
} }
ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10); ExtraNewsItemsPanel.Spacing = Math.Clamp(6 * scale, 3, 10);

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.Globalization; using System.Globalization;
@@ -13,7 +13,6 @@ using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
@@ -27,13 +26,13 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
private static readonly IReadOnlyDictionary<DayOfWeek, string> ZhWeekdays = private static readonly IReadOnlyDictionary<DayOfWeek, string> ZhWeekdays =
new Dictionary<DayOfWeek, string> new Dictionary<DayOfWeek, string>
{ {
[DayOfWeek.Monday] = "星期一", [DayOfWeek.Monday] = "星期一",
[DayOfWeek.Tuesday] = "星期二", [DayOfWeek.Tuesday] = "星期二",
[DayOfWeek.Wednesday] = "星期三", [DayOfWeek.Wednesday] = "星期三",
[DayOfWeek.Thursday] = "星期四", [DayOfWeek.Thursday] = "星期四",
[DayOfWeek.Friday] = "星期五", [DayOfWeek.Friday] = "星期五",
[DayOfWeek.Saturday] = "星期六", [DayOfWeek.Saturday] = "星期六",
[DayOfWeek.Sunday] = "星期日" [DayOfWeek.Sunday] = "星期日"
}; };
private static readonly Regex MultiWhitespaceRegex = new(@"\s+", RegexOptions.Compiled); private static readonly Regex MultiWhitespaceRegex = new(@"\s+", RegexOptions.Compiled);
@@ -450,7 +449,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
var leftSingleLineHeight = Math.Max(12, (leftContentHeight - dateStackSpacing) / 2d); var leftSingleLineHeight = Math.Max(12, (leftContentHeight - dateStackSpacing) / 2d);
var dateBase = Math.Clamp(44 * scale, 16, 62); var dateBase = Math.Clamp(44 * scale, 16, 62);
DateTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize( DateTextBlock.FontSize = FitFontSize(
DateTextBlock.Text, DateTextBlock.Text,
leftContentWidth, leftContentWidth,
leftSingleLineHeight, leftSingleLineHeight,
@@ -458,11 +457,10 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
minFontSize: Math.Max(12, dateBase * 0.68), minFontSize: Math.Max(12, dateBase * 0.68),
maxFontSize: dateBase, maxFontSize: dateBase,
weight: FontWeight.Bold, weight: FontWeight.Bold,
lineHeightFactor: 1.10, lineHeightFactor: 1.10);
fontFamily: MiSansFontFamily);
DateTextBlock.LineHeight = DateTextBlock.FontSize * 1.10; DateTextBlock.LineHeight = DateTextBlock.FontSize * 1.10;
WeekdayTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize( WeekdayTextBlock.FontSize = FitFontSize(
WeekdayTextBlock.Text, WeekdayTextBlock.Text,
leftContentWidth, leftContentWidth,
leftSingleLineHeight, leftSingleLineHeight,
@@ -470,8 +468,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
minFontSize: Math.Max(12, dateBase * 0.68), minFontSize: Math.Max(12, dateBase * 0.68),
maxFontSize: dateBase, maxFontSize: dateBase,
weight: FontWeight.Bold, weight: FontWeight.Bold,
lineHeightFactor: 1.10, lineHeightFactor: 1.10);
fontFamily: MiSansFontFamily);
WeekdayTextBlock.LineHeight = WeekdayTextBlock.FontSize * 1.10; WeekdayTextBlock.LineHeight = WeekdayTextBlock.FontSize * 1.10;
var rightContentHeight = Math.Max(42, totalHeight - rootPadding.Top - rootPadding.Bottom - InfoPanel.Padding.Top - InfoPanel.Padding.Bottom); var rightContentHeight = Math.Max(42, totalHeight - rootPadding.Top - rootPadding.Bottom - InfoPanel.Padding.Top - InfoPanel.Padding.Bottom);
@@ -519,7 +516,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
yearHeightBudget = minYearHeight + extraHeight * (yearWeight / weightSum); yearHeightBudget = minYearHeight + extraHeight * (yearWeight / weightSum);
} }
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var titleLayout = FitAdaptiveTextLayout(
PaintingTitleTextBlock.Text, PaintingTitleTextBlock.Text,
rightContentWidth, rightContentWidth,
titleHeightBudget, titleHeightBudget,
@@ -528,8 +525,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
minFontSize: titleMin, minFontSize: titleMin,
maxFontSize: titleBase, maxFontSize: titleBase,
weightCandidates: TitleWeightCandidates, weightCandidates: TitleWeightCandidates,
lineHeightFactor: 1.10, lineHeightFactor: 1.10);
fontFamily: MiSansFontFamily);
PaintingTitleTextBlock.MaxWidth = rightContentWidth; PaintingTitleTextBlock.MaxWidth = rightContentWidth;
PaintingTitleTextBlock.Margin = new Thickness(0, 0, 0, titleBottomMargin); PaintingTitleTextBlock.Margin = new Thickness(0, 0, 0, titleBottomMargin);
PaintingTitleTextBlock.MaxLines = titleLayout.MaxLines; PaintingTitleTextBlock.MaxLines = titleLayout.MaxLines;
@@ -542,7 +538,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
artistInfoStack.Spacing = bottomStackSpacing; artistInfoStack.Spacing = bottomStackSpacing;
} }
var artistLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var artistLayout = FitAdaptiveTextLayout(
ArtistTextBlock.Text, ArtistTextBlock.Text,
rightContentWidth, rightContentWidth,
artistHeightBudget, artistHeightBudget,
@@ -551,15 +547,14 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
minFontSize: artistMin, minFontSize: artistMin,
maxFontSize: artistBase, maxFontSize: artistBase,
weightCandidates: ArtistWeightCandidates, weightCandidates: ArtistWeightCandidates,
lineHeightFactor: 1.14, lineHeightFactor: 1.14);
fontFamily: MiSansFontFamily);
ArtistTextBlock.MaxWidth = rightContentWidth; ArtistTextBlock.MaxWidth = rightContentWidth;
ArtistTextBlock.MaxLines = artistLayout.MaxLines; ArtistTextBlock.MaxLines = artistLayout.MaxLines;
ArtistTextBlock.FontWeight = artistLayout.Weight; ArtistTextBlock.FontWeight = artistLayout.Weight;
ArtistTextBlock.FontSize = artistLayout.FontSize; ArtistTextBlock.FontSize = artistLayout.FontSize;
ArtistTextBlock.LineHeight = artistLayout.LineHeight; ArtistTextBlock.LineHeight = artistLayout.LineHeight;
var yearLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var yearLayout = FitAdaptiveTextLayout(
YearTextBlock.Text, YearTextBlock.Text,
rightContentWidth, rightContentWidth,
yearHeightBudget, yearHeightBudget,
@@ -568,8 +563,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
minFontSize: yearMin, minFontSize: yearMin,
maxFontSize: yearBase, maxFontSize: yearBase,
weightCandidates: SecondaryWeightCandidates, weightCandidates: SecondaryWeightCandidates,
lineHeightFactor: 1.08, lineHeightFactor: 1.08);
fontFamily: MiSansFontFamily);
YearTextBlock.MaxWidth = rightContentWidth; YearTextBlock.MaxWidth = rightContentWidth;
YearTextBlock.MaxLines = yearLayout.MaxLines; YearTextBlock.MaxLines = yearLayout.MaxLines;
YearTextBlock.FontWeight = yearLayout.Weight; YearTextBlock.FontWeight = yearLayout.Weight;
@@ -723,7 +717,7 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
normalized = "Untitled"; normalized = "Untitled";
} }
return $"“{normalized}”"; return $"“{normalized}”";
} }
private void CancelRefreshRequest() private void CancelRefreshRequest()
@@ -777,4 +771,222 @@ public partial class DailyArtworkWidget : UserControl, IDesktopComponentWidget,
return MultiWhitespaceRegex.Replace(text.Trim(), " "); return MultiWhitespaceRegex.Replace(text.Trim(), " ");
} }
private static double FitFontSize(
string? text,
double maxWidth,
double maxHeight,
int maxLines,
double minFontSize,
double maxFontSize,
FontWeight weight,
double lineHeightFactor)
{
var content = string.IsNullOrWhiteSpace(text) ? " " : text.Trim();
var min = Math.Max(6, minFontSize);
var max = Math.Max(min, maxFontSize);
var low = min;
var high = max;
var best = min;
for (var i = 0; i < 18; i++)
{
var candidate = (low + high) / 2d;
var lineHeight = candidate * lineHeightFactor;
var size = MeasureTextSize(content, candidate, weight, Math.Max(1, maxWidth), lineHeight);
var lineCount = Math.Max(1, (int)Math.Ceiling(size.Height / Math.Max(1, lineHeight)));
var fits = size.Height <= maxHeight + 0.6 && lineCount <= Math.Max(1, maxLines);
if (fits)
{
best = candidate;
low = candidate;
}
else
{
high = candidate;
}
}
return best;
}
private static AdaptiveTextLayout FitAdaptiveTextLayout(
string? text,
double maxWidth,
double maxHeight,
int minLines,
int maxLines,
double minFontSize,
double maxFontSize,
FontWeight[] weightCandidates,
double lineHeightFactor)
{
var content = string.IsNullOrWhiteSpace(text) ? " " : text.Trim();
var safeMinLines = Math.Max(1, minLines);
var safeMaxLines = Math.Max(safeMinLines, maxLines);
var linesByHeight = ResolveMaxLinesByHeight(maxHeight, minFontSize, lineHeightFactor, safeMinLines, safeMaxLines);
var candidates = weightCandidates is { Length: > 0 }
? weightCandidates
: new[] { FontWeight.Normal };
AdaptiveTextLayout? best = null;
foreach (var weight in candidates)
{
for (var lineLimit = linesByHeight; lineLimit >= safeMinLines; lineLimit--)
{
var fontSize = FitFontSize(
content,
maxWidth,
maxHeight,
lineLimit,
minFontSize,
maxFontSize,
weight,
lineHeightFactor);
var lineHeight = fontSize * lineHeightFactor;
var measuredSize = MeasureTextSize(content, fontSize, weight, Math.Max(1, maxWidth), lineHeight);
var measuredLineCount = ResolveLineCount(measuredSize.Height, lineHeight);
var overflowLines = Math.Max(0, measuredLineCount - lineLimit);
var overflowHeight = Math.Max(0, measuredSize.Height - maxHeight);
var overflowScore = overflowLines * 1000d + overflowHeight;
var fitsCompletely = overflowLines == 0 && overflowHeight <= 0.6;
var candidate = new AdaptiveTextLayout(fontSize, weight, lineLimit, lineHeight, overflowScore, fitsCompletely);
if (best is null || IsBetterAdaptiveTextCandidate(candidate, best.Value))
{
best = candidate;
}
}
}
if (best is not null)
{
return best.Value;
}
var fallbackFontSize = Math.Max(6, minFontSize);
return new AdaptiveTextLayout(
fallbackFontSize,
FontWeight.Normal,
safeMinLines,
fallbackFontSize * lineHeightFactor,
double.MaxValue,
fitsCompletely: false);
}
private static bool IsBetterAdaptiveTextCandidate(AdaptiveTextLayout candidate, AdaptiveTextLayout best)
{
if (candidate.FitsCompletely && !best.FitsCompletely)
{
return true;
}
if (!candidate.FitsCompletely && best.FitsCompletely)
{
return false;
}
if (candidate.FitsCompletely && best.FitsCompletely)
{
if (candidate.FontSize > best.FontSize + 0.12)
{
return true;
}
if (Math.Abs(candidate.FontSize - best.FontSize) <= 0.12 && candidate.MaxLines < best.MaxLines)
{
return true;
}
return false;
}
if (candidate.OverflowScore < best.OverflowScore - 0.2)
{
return true;
}
if (Math.Abs(candidate.OverflowScore - best.OverflowScore) <= 0.2 &&
candidate.FontSize > best.FontSize + 0.12)
{
return true;
}
if (Math.Abs(candidate.OverflowScore - best.OverflowScore) <= 0.2 &&
Math.Abs(candidate.FontSize - best.FontSize) <= 0.12 &&
candidate.MaxLines > best.MaxLines)
{
return true;
}
return false;
}
private static int ResolveMaxLinesByHeight(
double maxHeight,
double minFontSize,
double lineHeightFactor,
int minLines,
int maxLines)
{
var safeMinLines = Math.Max(1, minLines);
var safeMaxLines = Math.Max(safeMinLines, maxLines);
var lineHeight = Math.Max(1, Math.Max(6, minFontSize) * lineHeightFactor);
var maxHeightWithTolerance = Math.Max(1, maxHeight + 0.6);
var linesByHeight = (int)Math.Floor(maxHeightWithTolerance / lineHeight);
return Math.Clamp(linesByHeight, safeMinLines, safeMaxLines);
}
private static int ResolveLineCount(double measuredHeight, double lineHeight)
{
return Math.Max(1, (int)Math.Ceiling(measuredHeight / Math.Max(1, lineHeight)));
}
private readonly struct AdaptiveTextLayout
{
public AdaptiveTextLayout(
double fontSize,
FontWeight weight,
int maxLines,
double lineHeight,
double overflowScore,
bool fitsCompletely)
{
FontSize = fontSize;
Weight = weight;
MaxLines = Math.Max(1, maxLines);
LineHeight = lineHeight;
OverflowScore = overflowScore;
FitsCompletely = fitsCompletely;
}
public double FontSize { get; }
public FontWeight Weight { get; }
public int MaxLines { get; }
public double LineHeight { get; }
public double OverflowScore { get; }
public bool FitsCompletely { get; }
}
private static Size MeasureTextSize(string text, double fontSize, FontWeight weight, double maxWidth, double lineHeight)
{
var probe = new TextBlock
{
Text = text,
FontFamily = MiSansFontFamily,
FontSize = fontSize,
FontWeight = weight,
TextWrapping = TextWrapping.Wrap,
LineHeight = lineHeight
};
probe.Measure(new Size(Math.Max(1, maxWidth), double.PositiveInfinity));
return probe.DesiredSize;
}
} }

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -11,7 +11,6 @@ using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -49,6 +48,8 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
private const double MinPoetryFontSize = 8; private const double MinPoetryFontSize = 8;
private const double MinAuthorFontSize = 7; private const double MinAuthorFontSize = 7;
private readonly record struct TextFitResult(double FontSize, FontWeight FontWeight, double LineHeight);
private readonly DispatcherTimer _refreshTimer = new() private readonly DispatcherTimer _refreshTimer = new()
{ {
Interval = TimeSpan.FromHours(6) Interval = TimeSpan.FromHours(6)
@@ -267,7 +268,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
{ {
if (!string.IsNullOrWhiteSpace(snapshot.Origin)) if (!string.IsNullOrWhiteSpace(snapshot.Origin))
{ {
return $"{snapshot.Origin.Trim()} · {snapshot.Author.Trim()}"; return $"{snapshot.Origin.Trim()} \u00B7 {snapshot.Author.Trim()}";
} }
return snapshot.Author.Trim(); return snapshot.Author.Trim();
@@ -296,7 +297,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
private void ApplyLoadingState() private void ApplyLoadingState()
{ {
_poetryRawText = L("poetry.widget.loading_content", "Loading..."); _poetryRawText = L("poetry.widget.loading_content", "Loading...");
_authorRawText = L("poetry.widget.loading_author", "·"); _authorRawText = L("poetry.widget.loading_author", "...");
StatusTextBlock.IsVisible = false; StatusTextBlock.IsVisible = false;
ApplyModeVisualIfNeeded(force: true); ApplyModeVisualIfNeeded(force: true);
} }
@@ -483,29 +484,23 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
minFontWeight: ToVariableWeight(poemMinWeight), minFontWeight: ToVariableWeight(poemMinWeight),
lineHeightFactor: 1.12); lineHeightFactor: 1.12);
var poemFit = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var poemFit = FitTextStable(
poemPrepared, poemPrepared,
poemWidth, poemWidth,
availablePoemHeight, availablePoemHeight,
minFontSize: poemMinFontSize, minFontSize: poemMinFontSize,
maxFontSize: Math.Clamp(poemPreferredFontSize * 1.20, poemMinFontSize, 62), maxFontSize: Math.Clamp(poemPreferredFontSize * 1.20, poemMinFontSize, 62),
minLines: poemMaxLines,
maxLines: poemMaxLines, maxLines: poemMaxLines,
weightCandidates: new[]
{
ToVariableWeight(poemMinWeight),
ToVariableWeight((poemMinWeight + poemMaxWeight) / 2d),
ToVariableWeight(poemMaxWeight)
},
lineHeightFactor: 1.12, lineHeightFactor: 1.12,
fontFamily: MiSansFontFamily); minWeight: poemMinWeight,
maxWeight: poemMaxWeight);
PoetryContentTextBlock.Text = poemPrepared; PoetryContentTextBlock.Text = poemPrepared;
PoetryContentTextBlock.MaxWidth = poemWidth; PoetryContentTextBlock.MaxWidth = poemWidth;
PoetryContentTextBlock.MaxLines = poemMaxLines; PoetryContentTextBlock.MaxLines = poemMaxLines;
PoetryContentTextBlock.FontSize = poemFit.FontSize; PoetryContentTextBlock.FontSize = poemFit.FontSize;
PoetryContentTextBlock.LineHeight = poemFit.LineHeight; PoetryContentTextBlock.LineHeight = poemFit.LineHeight;
PoetryContentTextBlock.FontWeight = poemFit.Weight; PoetryContentTextBlock.FontWeight = poemFit.FontWeight;
var authorWidth = Math.Max(72, Math.Min(innerWidth * (isNightMode ? 0.5 : 0.56), innerWidth - 8)); var authorWidth = Math.Max(72, Math.Min(innerWidth * (isNightMode ? 0.5 : 0.56), innerWidth - 8));
var authorUnitsTarget = 20; var authorUnitsTarget = 20;
@@ -525,22 +520,16 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
minFontWeight: ToVariableWeight(authorMinWeight), minFontWeight: ToVariableWeight(authorMinWeight),
lineHeightFactor: 1.12); lineHeightFactor: 1.12);
var authorFit = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var authorFit = FitTextStable(
authorPrepared, authorPrepared,
authorWidth, authorWidth,
AuthorAccent.Height, AuthorAccent.Height,
minFontSize: authorMinFontSize, minFontSize: authorMinFontSize,
maxFontSize: Math.Clamp(authorPreferredFontSize * 1.15, authorMinFontSize, 42), maxFontSize: Math.Clamp(authorPreferredFontSize * 1.15, authorMinFontSize, 42),
minLines: 1,
maxLines: 1, maxLines: 1,
weightCandidates: new[]
{
ToVariableWeight(authorMinWeight),
ToVariableWeight((authorMinWeight + authorMaxWeight) / 2d),
ToVariableWeight(authorMaxWeight)
},
lineHeightFactor: 1.12, lineHeightFactor: 1.12,
fontFamily: MiSansFontFamily); minWeight: authorMinWeight,
maxWeight: authorMaxWeight);
AuthorTextBlock.Text = authorPrepared; AuthorTextBlock.Text = authorPrepared;
AuthorTextBlock.TextWrapping = TextWrapping.NoWrap; AuthorTextBlock.TextWrapping = TextWrapping.NoWrap;
@@ -548,7 +537,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
AuthorTextBlock.MaxWidth = authorWidth; AuthorTextBlock.MaxWidth = authorWidth;
AuthorTextBlock.FontSize = authorFit.FontSize; AuthorTextBlock.FontSize = authorFit.FontSize;
AuthorTextBlock.LineHeight = authorFit.LineHeight; AuthorTextBlock.LineHeight = authorFit.LineHeight;
AuthorTextBlock.FontWeight = authorFit.Weight; AuthorTextBlock.FontWeight = authorFit.FontWeight;
} }
private void UpdateRefreshButtonState() private void UpdateRefreshButtonState()
@@ -702,7 +691,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
if (current.Length == 0) if (current.Length == 0)
{ {
if (ComponentTypographyLayoutService.CountTextDisplayUnits(remain) <= target || lines.Count == lineLimit - 1) if (EstimateDisplayUnits(remain) <= target || lines.Count == lineLimit - 1)
{ {
current.Append(remain); current.Append(remain);
remain = string.Empty; remain = string.Empty;
@@ -725,7 +714,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
} }
var merged = current + remain; var merged = current + remain;
if (ComponentTypographyLayoutService.CountTextDisplayUnits(merged) <= target || lines.Count == lineLimit - 1) if (EstimateDisplayUnits(merged) <= target || lines.Count == lineLimit - 1)
{ {
current.Append(remain); current.Append(remain);
remain = string.Empty; remain = string.Empty;
@@ -859,13 +848,7 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
} }
var lineHeight = fontSize * lineHeightFactor; var lineHeight = fontSize * lineHeightFactor;
var measured = ComponentTypographyLayoutService.MeasureTextSize( var measured = MeasureTextSize(text, fontSize, fontWeight, maxWidth, lineHeight);
text,
fontSize,
fontWeight,
maxWidth,
lineHeight,
MiSansFontFamily);
var lineCount = Math.Max(1, (int)Math.Ceiling(measured.Height / Math.Max(1, lineHeight))); var lineCount = Math.Max(1, (int)Math.Ceiling(measured.Height / Math.Max(1, lineHeight)));
return measured.Height <= maxHeight + 0.6 && lineCount <= Math.Max(1, maxLines); return measured.Height <= maxHeight + 0.6 && lineCount <= Math.Max(1, maxLines);
} }
@@ -908,6 +891,86 @@ public partial class DailyPoetryWidget : UserControl, IDesktopComponentWidget, I
return text.Length; return text.Length;
} }
private static double EstimateDisplayUnits(string text)
{
var units = 0d;
foreach (var ch in text)
{
units += ch <= 127 ? 0.56 : 1d;
}
return units;
}
private static TextFitResult FitTextStable(
string? text,
double maxWidth,
double maxHeight,
double minFontSize,
double maxFontSize,
int maxLines,
double lineHeightFactor,
double minWeight,
double maxWeight)
{
var normalizedText = string.IsNullOrWhiteSpace(text) ? " " : text.Trim();
var min = Math.Max(6, minFontSize);
var max = Math.Max(min, maxFontSize);
var low = min;
var high = max;
var bestSize = min;
var bestWeight = ToVariableWeight(minWeight);
for (var i = 0; i < 22; i++)
{
var candidate = (low + high) / 2d;
var progress = max <= min
? 0
: Math.Clamp((candidate - min) / (max - min), 0, 1);
var candidateWeight = ToVariableWeight(Lerp(minWeight, maxWeight, progress));
var lineHeight = candidate * lineHeightFactor;
var measured = MeasureTextSize(normalizedText, candidate, candidateWeight, Math.Max(1, maxWidth), lineHeight);
var lineCount = Math.Max(1, (int)Math.Ceiling(measured.Height / Math.Max(1, lineHeight)));
var fits = measured.Height <= maxHeight + 0.6 && lineCount <= Math.Max(1, maxLines);
if (fits)
{
bestSize = candidate;
bestWeight = candidateWeight;
low = candidate;
}
else
{
high = candidate;
}
}
var lineHeightResult = bestSize * lineHeightFactor;
return new TextFitResult(bestSize, bestWeight, lineHeightResult);
}
private static Size MeasureTextSize(
string text,
double fontSize,
FontWeight fontWeight,
double maxWidth,
double lineHeight)
{
var probe = new TextBlock
{
Text = text,
FontFamily = MiSansFontFamily,
FontSize = fontSize,
FontWeight = fontWeight,
TextWrapping = TextWrapping.Wrap,
LineHeight = lineHeight
};
probe.Measure(new Size(Math.Max(1, maxWidth), double.PositiveInfinity));
return probe.DesiredSize;
}
private static FontWeight ToVariableWeight(double weight) private static FontWeight ToVariableWeight(double weight)
{ {

View File

@@ -9,7 +9,6 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
@@ -75,7 +74,6 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
UpdateCurrencyLabels(); UpdateCurrencyLabels();
UpdateAmounts(); UpdateAmounts();
ApplyLoadingState(); ApplyLoadingState();
UpdateTypography();
} }
public void ApplyCellSize(double cellSize) public void ApplyCellSize(double cellSize)
@@ -84,7 +82,6 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
var scale = ResolveScale(); var scale = ResolveScale();
RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 14, 48); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(34 * scale, 14, 48);
RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(12 * scale, 12 * scale, null, 0.55d); RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(12 * scale, 12 * scale, null, 0.55d);
UpdateTypography();
} }
public void SetRecommendationInfoService(IRecommendationInfoService recommendationInfoService) public void SetRecommendationInfoService(IRecommendationInfoService recommendationInfoService)
@@ -246,7 +243,6 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
FromCurrencyNameTextBlock.Text = IsZh() ? from.ZhName : from.EnName; FromCurrencyNameTextBlock.Text = IsZh() ? from.ZhName : from.EnName;
ToCurrencyCodeTextBlock.Text = to.Code; ToCurrencyCodeTextBlock.Text = to.Code;
ToCurrencyNameTextBlock.Text = IsZh() ? to.ZhName : to.EnName; ToCurrencyNameTextBlock.Text = IsZh() ? to.ZhName : to.EnName;
UpdateTypography();
} }
private void UpdateAmounts() private void UpdateAmounts()
@@ -262,7 +258,6 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
_fromCurrency, _fromCurrency,
_calculatorDataService.FormatAmount(_currentRate, maxFractionDigits: 6), _calculatorDataService.FormatAmount(_currentRate, maxFractionDigits: 6),
_toCurrency); _toCurrency);
UpdateTypography();
} }
private void ApplyLoadingState() private void ApplyLoadingState()
@@ -338,103 +333,6 @@ public partial class ExchangeRateCalculatorWidget : UserControl, IDesktopCompone
return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.72, 1.95); return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.72, 1.95);
} }
private void UpdateTypography()
{
var layoutWidth = LayoutRoot.Width > 1 ? LayoutRoot.Width : 304d;
var layoutHeight = LayoutRoot.Height > 1 ? LayoutRoot.Height : 304d;
var fromStackWidth = Math.Max(72, (layoutWidth - 86) * 0.36);
var amountWidth = Math.Max(116, (layoutWidth - 86) * 0.50);
var rateWidth = Math.Max(120, layoutWidth - 24);
var statusWidth = Math.Max(120, layoutWidth - 24);
FromCurrencyCodeTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
FromCurrencyCodeTextBlock.Text,
fromStackWidth,
26,
1,
11,
20,
FontWeight.SemiBold,
1.06d,
MiSansFontFamily);
FromCurrencyNameTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
FromCurrencyNameTextBlock.Text,
fromStackWidth,
18,
1,
9,
14,
FontWeight.Normal,
1.06d,
MiSansFontFamily);
ToCurrencyCodeTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
ToCurrencyCodeTextBlock.Text,
fromStackWidth,
26,
1,
11,
20,
FontWeight.SemiBold,
1.06d,
MiSansFontFamily);
ToCurrencyNameTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
ToCurrencyNameTextBlock.Text,
fromStackWidth,
18,
1,
9,
14,
FontWeight.Normal,
1.06d,
MiSansFontFamily);
InputAmountTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
InputAmountTextBlock.Text,
amountWidth,
Math.Max(42, layoutHeight * 0.16),
1,
20,
42,
FontWeight.Bold,
1.02d,
MiSansFontFamily);
ConvertedAmountTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
ConvertedAmountTextBlock.Text,
amountWidth,
Math.Max(42, layoutHeight * 0.16),
1,
20,
42,
FontWeight.Bold,
1.02d,
MiSansFontFamily);
RateTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
RateTextBlock.Text,
rateWidth,
20,
1,
10,
15,
FontWeight.Normal,
1.06d,
MiSansFontFamily);
if (StatusTextBlock.IsVisible)
{
StatusTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
StatusTextBlock.Text,
statusWidth,
22,
1,
10,
16,
FontWeight.Normal,
1.06d,
MiSansFontFamily);
}
}
private void CancelRefreshRequest() private void CancelRefreshRequest()
{ {
var cts = Interlocked.Exchange(ref _refreshCts, null); var cts = Interlocked.Exchange(ref _refreshCts, null);

View File

@@ -9,7 +9,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -545,91 +544,34 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text) var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text)
? "00°" ? "00°"
: TemperatureTextBlock.Text.Trim(); : TemperatureTextBlock.Text.Trim();
var temperatureGlyphCount = Math.Clamp(temperatureSample.Length, 3, 6);
var temperatureMaxWidth = Math.Max(30, innerWidth - iconSize - SummaryGrid.ColumnSpacing - 6); var temperatureMaxWidth = Math.Max(30, innerWidth - iconSize - SummaryGrid.ColumnSpacing - 6);
var rawTemperatureSize = Math.Clamp(Lerp(72, 102, iconGrowth) * topScale, 14, 340); var rawTemperatureSize = Math.Clamp(Lerp(72, 102, iconGrowth) * topScale, 14, 340);
var temperatureLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var fitTemperatureSize = temperatureMaxWidth / (temperatureGlyphCount * 0.62);
TemperatureTextBlock.Text, TemperatureTextBlock.FontSize = Math.Clamp(Math.Min(rawTemperatureSize, fitTemperatureSize), 10, 340);
temperatureMaxWidth, TemperatureTextBlock.FontWeight = ToVariableWeight(Lerp(300, 380, emphasis));
Math.Max(18, summaryHeight * 0.84),
1,
1,
Math.Max(10, rawTemperatureSize * 0.42),
rawTemperatureSize,
[ToVariableWeight(Lerp(300, 380, emphasis))],
1.02);
TemperatureTextBlock.FontSize = temperatureLayout.FontSize;
TemperatureTextBlock.FontWeight = temperatureLayout.Weight;
TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 30, Math.Max(300, innerWidth * 0.66)); TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 30, Math.Max(300, innerWidth * 0.66));
TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.2 * topScale, -12, 0), 0, 0); TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.2 * topScale, -12, 0), 0, 0);
var cityBadge = ComponentTypographyLayoutService.ResolveBadgeBox( var cityFontSize = Math.Clamp(18.5 * topScale, 7, 86);
innerWidth * 0.36, var conditionFontSize = Math.Clamp(20 * topScale, 7, 90);
Math.Max(16, summaryHeight * 0.34), var rangeFontSize = Math.Clamp(20 * topScale, 7, 90);
preferredSizeScale: 0.28d, CityTextBlock.FontSize = cityFontSize;
minSize: 10, ConditionTextBlock.FontSize = conditionFontSize;
maxSize: 24, RangeTextBlock.FontSize = rangeFontSize;
insetScale: 0.18d); CityTextBlock.FontWeight = ToVariableWeight(Lerp(530, 620, emphasis));
CityInfoBadge.Padding = cityBadge.Padding; ConditionTextBlock.FontWeight = ToVariableWeight(Lerp(580, 660, emphasis));
CityInfoBadge.CornerRadius = new CornerRadius(cityBadge.Size / 2d); RangeTextBlock.FontWeight = ToVariableWeight(Lerp(600, 680, emphasis));
CityInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.36, 34, 420); CityTextBlock.LineHeight = cityFontSize * 1.08;
var cityLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( ConditionTextBlock.LineHeight = conditionFontSize * 1.06;
CityTextBlock.Text, RangeTextBlock.LineHeight = rangeFontSize * 1.06;
Math.Max(24, CityInfoBadge.MaxWidth - cityBadge.Padding.Left - cityBadge.Padding.Right),
Math.Max(12, summaryHeight * 0.36),
1,
1,
6,
Math.Max(6, 18.5 * topScale),
[ToVariableWeight(Lerp(530, 620, emphasis))],
1.08);
CityTextBlock.FontSize = cityLayout.FontSize;
CityTextBlock.FontWeight = cityLayout.Weight;
CityTextBlock.LineHeight = cityLayout.LineHeight;
CityTextBlock.MaxWidth = CityInfoBadge.MaxWidth;
var conditionBadge = ComponentTypographyLayoutService.ResolveBadgeBox(
innerWidth * 0.25,
Math.Max(16, summaryHeight * 0.34),
preferredSizeScale: 0.26d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
ConditionInfoBadge.Padding = conditionBadge.Padding;
ConditionInfoBadge.CornerRadius = new CornerRadius(conditionBadge.Size / 2d);
ConditionInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.25, 26, 340);
var conditionLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ConditionTextBlock.Text,
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right),
Math.Max(12, summaryHeight * 0.30),
1,
1,
7,
Math.Max(6, 20 * topScale),
[ToVariableWeight(Lerp(580, 660, emphasis))],
1.06);
var rangeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
RangeTextBlock.Text,
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right),
Math.Max(12, summaryHeight * 0.30),
1,
1,
7,
Math.Max(6, 20 * topScale),
[ToVariableWeight(Lerp(600, 680, emphasis))],
1.06);
ConditionTextBlock.FontSize = conditionLayout.FontSize;
ConditionTextBlock.FontWeight = conditionLayout.Weight;
ConditionTextBlock.LineHeight = conditionLayout.LineHeight;
RangeTextBlock.FontSize = rangeLayout.FontSize;
RangeTextBlock.FontWeight = rangeLayout.Weight;
RangeTextBlock.LineHeight = rangeLayout.LineHeight;
CityTextBlock.MaxWidth = CityInfoBadge.MaxWidth;
ConditionTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
RangeTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
WeatherIconImage.Width = iconSize; WeatherIconImage.Width = iconSize;
WeatherIconImage.Height = iconSize; WeatherIconImage.Height = iconSize;
WeatherIconImage.Margin = new Thickness(0, Math.Clamp(-2.4 * topScale, -12, 0), 0, 0); WeatherIconImage.Margin = new Thickness(0, Math.Clamp(-2.4 * topScale, -12, 0), 0, 0);
ConditionTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.25, 28, 340);
RangeTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.31, 34, 380);
CityTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.36, 34, 420);
HourlyPanelBorder.Padding = new Thickness(0); HourlyPanelBorder.Padding = new Thickness(0);
HourlyPanelBorder.CornerRadius = new CornerRadius(0); HourlyPanelBorder.CornerRadius = new CornerRadius(0);
@@ -648,30 +590,10 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var hourlyStackSpacing = Math.Clamp(2 * hourlyCellScale, 0.2, 10); var hourlyStackSpacing = Math.Clamp(2 * hourlyCellScale, 0.2, 10);
for (var i = 0; i < _hourlyTempBlocks.Length; i++) for (var i = 0; i < _hourlyTempBlocks.Length; i++)
{ {
var hourlyTempLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( _hourlyTempBlocks[i].FontSize = hourlyTempSize;
_hourlyTempBlocks[i].Text, _hourlyTimeBlocks[i].FontSize = hourlyTimeSize;
Math.Clamp(hourlyCellWidth, 12, 260), _hourlyTempBlocks[i].FontWeight = ToVariableWeight(Lerp(540, 650, emphasis));
Math.Max(10, hourlyHeight * 0.42), _hourlyTimeBlocks[i].FontWeight = ToVariableWeight(Lerp(450, 560, emphasis));
1,
1,
6,
hourlyTempSize,
[ToVariableWeight(Lerp(540, 650, emphasis))],
1.02);
var hourlyTimeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
_hourlyTimeBlocks[i].Text,
Math.Clamp(hourlyCellWidth, 12, 260),
Math.Max(10, hourlyHeight * 0.34),
1,
1,
6,
hourlyTimeSize,
[ToVariableWeight(Lerp(450, 560, emphasis))],
1.02);
_hourlyTempBlocks[i].FontSize = hourlyTempLayout.FontSize;
_hourlyTimeBlocks[i].FontSize = hourlyTimeLayout.FontSize;
_hourlyTempBlocks[i].FontWeight = hourlyTempLayout.Weight;
_hourlyTimeBlocks[i].FontWeight = hourlyTimeLayout.Weight;
_hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260); _hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260);
_hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260); _hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260);
_hourlyIconBlocks[i].Width = hourlyIconSize; _hourlyIconBlocks[i].Width = hourlyIconSize;
@@ -698,42 +620,12 @@ public partial class ExtendedWeatherWidget : UserControl, IDesktopComponentWidge
var dailyHighRightGap = Math.Clamp(innerWidth * 0.018, 1, 28); var dailyHighRightGap = Math.Clamp(innerWidth * 0.018, 1, 28);
for (var i = 0; i < _dailyLabelBlocks.Length; i++) for (var i = 0; i < _dailyLabelBlocks.Length; i++)
{ {
var dailyLabelLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( _dailyLabelBlocks[i].FontSize = dailyLabelSize;
_dailyLabelBlocks[i].Text, _dailyHighBlocks[i].FontSize = dailyTempSize;
dailyLabelMaxWidth, _dailyLowBlocks[i].FontSize = dailyTempSize;
Math.Max(10, dailyRowHeight * 0.50), _dailyLabelBlocks[i].FontWeight = ToVariableWeight(Lerp(520, 620, emphasis));
1, _dailyHighBlocks[i].FontWeight = ToVariableWeight(Lerp(560, 680, emphasis));
1, _dailyLowBlocks[i].FontWeight = ToVariableWeight(Lerp(470, 590, emphasis));
6,
dailyLabelSize,
[ToVariableWeight(Lerp(520, 620, emphasis))],
1.04);
var dailyHighLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
_dailyHighBlocks[i].Text,
dailyHighWidth,
Math.Max(10, dailyRowHeight * 0.42),
1,
1,
6,
dailyTempSize,
[ToVariableWeight(Lerp(560, 680, emphasis))],
1.02);
var dailyLowLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
_dailyLowBlocks[i].Text,
dailyLowWidth,
Math.Max(10, dailyRowHeight * 0.42),
1,
1,
6,
dailyTempSize,
[ToVariableWeight(Lerp(470, 590, emphasis))],
1.02);
_dailyLabelBlocks[i].FontSize = dailyLabelLayout.FontSize;
_dailyHighBlocks[i].FontSize = dailyHighLayout.FontSize;
_dailyLowBlocks[i].FontSize = dailyLowLayout.FontSize;
_dailyLabelBlocks[i].FontWeight = dailyLabelLayout.Weight;
_dailyHighBlocks[i].FontWeight = dailyHighLayout.Weight;
_dailyLowBlocks[i].FontWeight = dailyLowLayout.Weight;
_dailyLabelBlocks[i].MaxWidth = dailyLabelMaxWidth; _dailyLabelBlocks[i].MaxWidth = dailyLabelMaxWidth;
_dailyHighBlocks[i].Width = dailyHighWidth; _dailyHighBlocks[i].Width = dailyHighWidth;
_dailyLowBlocks[i].Width = dailyLowWidth; _dailyLowBlocks[i].Width = dailyLowWidth;

View File

@@ -11,7 +11,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -1272,88 +1271,31 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text) var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text)
? "00°" ? "00°"
: TemperatureTextBlock.Text.Trim(); : TemperatureTextBlock.Text.Trim();
var temperatureGlyphCount = Math.Clamp(temperatureSample.Length, 3, 6);
var temperatureMaxWidth = Math.Max(28, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 4); var temperatureMaxWidth = Math.Max(28, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 4);
var rawTemperatureSize = Math.Clamp(Lerp(64, 92, iconGrowth) * topScale, 12, 320); var rawTemperatureSize = Math.Clamp(Lerp(64, 92, iconGrowth) * topScale, 12, 320);
var temperatureLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var fitTemperatureSize = temperatureMaxWidth / (temperatureGlyphCount * 0.62);
TemperatureTextBlock.Text, TemperatureTextBlock.FontSize = Math.Clamp(Math.Min(rawTemperatureSize, fitTemperatureSize), 9, 320);
temperatureMaxWidth, TemperatureTextBlock.FontWeight = ToVariableWeight(Lerp(300, 360, emphasis));
Math.Max(18, topZoneHeight * 0.84),
1,
1,
Math.Max(9, rawTemperatureSize * 0.42),
rawTemperatureSize,
[ToVariableWeight(Lerp(300, 360, emphasis))],
1.02);
TemperatureTextBlock.FontSize = temperatureLayout.FontSize;
TemperatureTextBlock.FontWeight = temperatureLayout.Weight;
TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.0 * topScale, -10, 0), 0, 0); TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.0 * topScale, -10, 0), 0, 0);
TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 28, Math.Max(280, innerWidth * 0.68)); TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 28, Math.Max(280, innerWidth * 0.68));
var cityBadge = ComponentTypographyLayoutService.ResolveBadgeBox( CityInfoBadge.Padding = new Thickness(0);
innerWidth * 0.37, CityInfoBadge.CornerRadius = new CornerRadius(0);
Math.Max(16, topZoneHeight * 0.34),
preferredSizeScale: 0.28d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
CityInfoBadge.Padding = cityBadge.Padding;
CityInfoBadge.CornerRadius = new CornerRadius(cityBadge.Size / 2d);
CityInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.37, 34, 460);
LocationIcon.FontSize = Math.Clamp(13 * topScale, 6, 52); LocationIcon.FontSize = Math.Clamp(13 * topScale, 6, 52);
var cityLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( CityTextBlock.FontSize = Math.Clamp(18.5 * topScale, 7, 88);
CityTextBlock.Text, CityTextBlock.FontWeight = ToVariableWeight(Lerp(530, 620, emphasis));
Math.Max(24, CityInfoBadge.MaxWidth - cityBadge.Padding.Left - cityBadge.Padding.Right), CityTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.37, 34, 460);
Math.Max(12, topZoneHeight * 0.36),
1,
1,
6,
Math.Max(6, 18.5 * topScale),
[ToVariableWeight(Lerp(530, 620, emphasis))],
1.08);
CityTextBlock.FontSize = cityLayout.FontSize;
CityTextBlock.FontWeight = cityLayout.Weight;
CityTextBlock.LineHeight = cityLayout.LineHeight;
CityTextBlock.MaxWidth = CityInfoBadge.MaxWidth;
var conditionBadge = ComponentTypographyLayoutService.ResolveBadgeBox( ConditionInfoBadge.Padding = new Thickness(0);
innerWidth * 0.24, ConditionInfoBadge.CornerRadius = new CornerRadius(0);
Math.Max(16, bottomZoneHeight * 0.34),
preferredSizeScale: 0.26d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
ConditionInfoBadge.Padding = conditionBadge.Padding;
ConditionInfoBadge.CornerRadius = new CornerRadius(conditionBadge.Size / 2d);
ConditionInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.24, 26, 320);
ConditionRangeStack.Spacing = Math.Clamp(8.5 * topScale, 1, 24); ConditionRangeStack.Spacing = Math.Clamp(8.5 * topScale, 1, 24);
var conditionLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( ConditionTextBlock.FontSize = Math.Clamp(19 * topScale, 7, 78);
ConditionTextBlock.Text, RangeTextBlock.FontSize = Math.Clamp(21 * topScale, 7, 84);
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right), ConditionTextBlock.FontWeight = ToVariableWeight(Lerp(580, 660, emphasis));
Math.Max(12, bottomZoneHeight * 0.30), RangeTextBlock.FontWeight = ToVariableWeight(Lerp(600, 680, emphasis));
1, ConditionTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.24, 26, 320);
1, RangeTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.31, 32, 360);
7,
Math.Max(6, 19 * topScale),
[ToVariableWeight(Lerp(580, 660, emphasis))],
1.10);
var rangeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
RangeTextBlock.Text,
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right),
Math.Max(12, bottomZoneHeight * 0.30),
1,
1,
7,
Math.Max(6, 21 * topScale),
[ToVariableWeight(Lerp(600, 680, emphasis))],
1.10);
ConditionTextBlock.FontSize = conditionLayout.FontSize;
ConditionTextBlock.FontWeight = conditionLayout.Weight;
ConditionTextBlock.LineHeight = conditionLayout.LineHeight;
RangeTextBlock.FontSize = rangeLayout.FontSize;
RangeTextBlock.FontWeight = rangeLayout.Weight;
RangeTextBlock.LineHeight = rangeLayout.LineHeight;
ConditionTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
RangeTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
BottomInfoStack.Spacing = Math.Clamp(2.0 * topScale, 0.4, 14); BottomInfoStack.Spacing = Math.Clamp(2.0 * topScale, 0.4, 14);
WeatherIconImage.Width = iconSize; WeatherIconImage.Width = iconSize;
@@ -1383,34 +1325,14 @@ public partial class HourlyWeatherWidget : UserControl, IDesktopComponentWidget,
for (var i = 0; i < _hourlyTimeBlocks.Length; i++) for (var i = 0; i < _hourlyTimeBlocks.Length; i++)
{ {
var hourlyTempLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( _hourlyTempBlocks[i].FontSize = hourlyTempSize;
_hourlyTempBlocks[i].Text, _hourlyTimeBlocks[i].FontSize = hourlyTimeSize;
Math.Clamp(hourlyCellWidth, 12, 240),
Math.Max(10, hourlyCellScale * 28),
1,
1,
6,
hourlyTempSize,
[ToVariableWeight(Lerp(580, 690, emphasis))],
1.02);
var hourlyTimeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
_hourlyTimeBlocks[i].Text,
Math.Clamp(hourlyCellWidth, 12, 240),
Math.Max(10, hourlyCellScale * 24),
1,
1,
6,
hourlyTimeSize,
[ToVariableWeight(Lerp(500, 600, emphasis))],
1.02);
_hourlyTempBlocks[i].FontSize = hourlyTempLayout.FontSize;
_hourlyTimeBlocks[i].FontSize = hourlyTimeLayout.FontSize;
_hourlyIconBlocks[i].Width = hourlyIconSize; _hourlyIconBlocks[i].Width = hourlyIconSize;
_hourlyIconBlocks[i].Height = hourlyIconSize; _hourlyIconBlocks[i].Height = hourlyIconSize;
_hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 240); _hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 240);
_hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 240); _hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 240);
_hourlyTimeBlocks[i].FontWeight = hourlyTimeLayout.Weight; _hourlyTimeBlocks[i].FontWeight = ToVariableWeight(Lerp(500, 600, emphasis));
_hourlyTempBlocks[i].FontWeight = hourlyTempLayout.Weight; _hourlyTempBlocks[i].FontWeight = ToVariableWeight(Lerp(580, 690, emphasis));
if (_hourlyTimeBlocks[i].Parent is StackPanel hourlyStack) if (_hourlyTimeBlocks[i].Parent is StackPanel hourlyStack)
{ {
hourlyStack.Spacing = stackSpacing; hourlyStack.Spacing = stackSpacing;

View File

@@ -15,7 +15,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -463,23 +462,11 @@ public partial class IfengNewsWidget : UserControl, IDesktopComponentWidget, IRe
visual.ImageHost.Height = imageHeight; visual.ImageHost.Height = imageHeight;
visual.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(imageHeight * 0.15, 8, 16); visual.ImageHost.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(imageHeight * 0.15, 8, 16);
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
visual.TitleTextBlock.Text,
textWidth,
itemHeight,
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(visual.TitleTextBlock.Text) > 28 ? 2 : 1,
minFontSize: Math.Clamp(titleFont * 0.72, 10, 16),
maxFontSize: titleFont,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.12d,
fontFamily: MiSansFontFamily);
visual.TitleTextBlock.MaxWidth = textWidth; visual.TitleTextBlock.MaxWidth = textWidth;
visual.TitleTextBlock.FontSize = titleLayout.FontSize; visual.TitleTextBlock.FontSize = titleFont;
visual.TitleTextBlock.LineHeight = titleLayout.LineHeight; visual.TitleTextBlock.LineHeight = titleFont * 1.12;
visual.TitleTextBlock.MinHeight = titleLayout.LineHeight * titleLayout.MaxLines; visual.TitleTextBlock.MinHeight = visual.TitleTextBlock.LineHeight * 2;
visual.TitleTextBlock.MaxLines = titleLayout.MaxLines; visual.TitleTextBlock.MaxLines = 2;
visual.TitleTextBlock.FontWeight = titleLayout.Weight;
} }
StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 20); StatusTextBlock.FontSize = Math.Clamp(titleFont, 10, 20);

View File

@@ -9,7 +9,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -1121,88 +1120,31 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text) var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text)
? "00°" ? "00°"
: TemperatureTextBlock.Text.Trim(); : TemperatureTextBlock.Text.Trim();
var temperatureGlyphCount = Math.Clamp(temperatureSample.Length, 3, 6);
var temperatureMaxWidth = Math.Max(28, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 4); var temperatureMaxWidth = Math.Max(28, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 4);
var rawTemperatureSize = Math.Clamp(Lerp(64, 92, iconGrowth) * topScale, 12, 320); var rawTemperatureSize = Math.Clamp(Lerp(64, 92, iconGrowth) * topScale, 12, 320);
var temperatureLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var fitTemperatureSize = temperatureMaxWidth / (temperatureGlyphCount * 0.62);
TemperatureTextBlock.Text, TemperatureTextBlock.FontSize = Math.Clamp(Math.Min(rawTemperatureSize, fitTemperatureSize), 9, 320);
temperatureMaxWidth, TemperatureTextBlock.FontWeight = ToVariableWeight(Lerp(300, 360, emphasis));
Math.Max(18, topZoneHeight * 0.84),
1,
1,
Math.Max(9, rawTemperatureSize * 0.42),
rawTemperatureSize,
[ToVariableWeight(Lerp(300, 360, emphasis))],
1.02);
TemperatureTextBlock.FontSize = temperatureLayout.FontSize;
TemperatureTextBlock.FontWeight = temperatureLayout.Weight;
TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.0 * topScale, -10, 0), 0, 0); TemperatureTextBlock.Margin = new Thickness(0, Math.Clamp(-2.0 * topScale, -10, 0), 0, 0);
TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 28, Math.Max(280, innerWidth * 0.68)); TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 28, Math.Max(280, innerWidth * 0.68));
var cityBadge = ComponentTypographyLayoutService.ResolveBadgeBox( CityInfoBadge.Padding = new Thickness(0);
innerWidth * 0.37, CityInfoBadge.CornerRadius = new CornerRadius(0);
Math.Max(16, topZoneHeight * 0.34),
preferredSizeScale: 0.28d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
CityInfoBadge.Padding = cityBadge.Padding;
CityInfoBadge.CornerRadius = new CornerRadius(cityBadge.Size / 2d);
CityInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.37, 34, 460);
LocationIcon.FontSize = Math.Clamp(13 * topScale, 6, 52); LocationIcon.FontSize = Math.Clamp(13 * topScale, 6, 52);
var cityLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( CityTextBlock.FontSize = Math.Clamp(18.5 * topScale, 7, 88);
CityTextBlock.Text, CityTextBlock.FontWeight = ToVariableWeight(Lerp(530, 620, emphasis));
Math.Max(24, CityInfoBadge.MaxWidth - cityBadge.Padding.Left - cityBadge.Padding.Right), CityTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.37, 34, 460);
Math.Max(12, topZoneHeight * 0.36),
1,
1,
6,
Math.Max(6, 18.5 * topScale),
[ToVariableWeight(Lerp(530, 620, emphasis))],
1.08);
CityTextBlock.FontSize = cityLayout.FontSize;
CityTextBlock.FontWeight = cityLayout.Weight;
CityTextBlock.LineHeight = cityLayout.LineHeight;
CityTextBlock.MaxWidth = CityInfoBadge.MaxWidth;
var conditionBadge = ComponentTypographyLayoutService.ResolveBadgeBox( ConditionInfoBadge.Padding = new Thickness(0);
innerWidth * 0.24, ConditionInfoBadge.CornerRadius = new CornerRadius(0);
Math.Max(16, topZoneHeight * 0.34),
preferredSizeScale: 0.26d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
ConditionInfoBadge.Padding = conditionBadge.Padding;
ConditionInfoBadge.CornerRadius = new CornerRadius(conditionBadge.Size / 2d);
ConditionInfoBadge.MaxWidth = Math.Clamp(innerWidth * 0.24, 26, 320);
ConditionIconStack.Spacing = Math.Clamp(8.5 * topScale, 1, 24); ConditionIconStack.Spacing = Math.Clamp(8.5 * topScale, 1, 24);
var conditionLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( ConditionTextBlock.FontSize = Math.Clamp(19 * topScale, 7, 78);
ConditionTextBlock.Text, RangeTextBlock.FontSize = Math.Clamp(21 * topScale, 7, 84);
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right), ConditionTextBlock.FontWeight = ToVariableWeight(Lerp(580, 660, emphasis));
Math.Max(12, topZoneHeight * 0.30), RangeTextBlock.FontWeight = ToVariableWeight(Lerp(600, 680, emphasis));
1, ConditionTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.24, 26, 320);
1, RangeTextBlock.MaxWidth = Math.Clamp(innerWidth * 0.31, 32, 360);
7,
Math.Max(6, 19 * topScale),
[ToVariableWeight(Lerp(580, 660, emphasis))],
1.06);
var rangeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
RangeTextBlock.Text,
Math.Max(24, ConditionInfoBadge.MaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right),
Math.Max(12, topZoneHeight * 0.30),
1,
1,
7,
Math.Max(6, 21 * topScale),
[ToVariableWeight(Lerp(600, 680, emphasis))],
1.06);
ConditionTextBlock.FontSize = conditionLayout.FontSize;
ConditionTextBlock.FontWeight = conditionLayout.Weight;
ConditionTextBlock.LineHeight = conditionLayout.LineHeight;
RangeTextBlock.FontSize = rangeLayout.FontSize;
RangeTextBlock.FontWeight = rangeLayout.Weight;
RangeTextBlock.LineHeight = rangeLayout.LineHeight;
ConditionTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
RangeTextBlock.MaxWidth = ConditionInfoBadge.MaxWidth;
BottomInfoStack.Spacing = Math.Clamp(2.0 * topScale, 0.4, 14); BottomInfoStack.Spacing = Math.Clamp(2.0 * topScale, 0.4, 14);
WeatherIconImage.Width = iconSize; WeatherIconImage.Width = iconSize;
@@ -1231,34 +1173,14 @@ public partial class MultiDayWeatherWidget : UserControl, IDesktopComponentWidge
for (var i = 0; i < _hourlyTimeBlocks.Length; i++) for (var i = 0; i < _hourlyTimeBlocks.Length; i++)
{ {
var hourlyTimeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( _hourlyTimeBlocks[i].FontSize = forecastLabelSize;
_hourlyTimeBlocks[i].Text, _hourlyTempBlocks[i].FontSize = forecastRangeSize;
Math.Clamp(hourlyCellWidth, 12, 260),
Math.Max(10, bottomZoneHeight * 0.34),
1,
1,
6,
forecastLabelSize,
[ToVariableWeight(Lerp(500, 600, emphasis))],
1.02);
var hourlyTempLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
_hourlyTempBlocks[i].Text,
Math.Clamp(hourlyCellWidth, 12, 260),
Math.Max(10, bottomZoneHeight * 0.42),
1,
1,
6,
forecastRangeSize,
[ToVariableWeight(Lerp(580, 690, emphasis))],
1.02);
_hourlyTimeBlocks[i].FontSize = hourlyTimeLayout.FontSize;
_hourlyTempBlocks[i].FontSize = hourlyTempLayout.FontSize;
_hourlyIconBlocks[i].Width = forecastIconSize; _hourlyIconBlocks[i].Width = forecastIconSize;
_hourlyIconBlocks[i].Height = forecastIconSize; _hourlyIconBlocks[i].Height = forecastIconSize;
_hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260); _hourlyTimeBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260);
_hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260); _hourlyTempBlocks[i].MaxWidth = Math.Clamp(hourlyCellWidth, 12, 260);
_hourlyTimeBlocks[i].FontWeight = hourlyTimeLayout.Weight; _hourlyTimeBlocks[i].FontWeight = ToVariableWeight(Lerp(500, 600, emphasis));
_hourlyTempBlocks[i].FontWeight = hourlyTempLayout.Weight; _hourlyTempBlocks[i].FontWeight = ToVariableWeight(Lerp(580, 690, emphasis));
_hourlyTimeBlocks[i].TextAlignment = TextAlignment.Center; _hourlyTimeBlocks[i].TextAlignment = TextAlignment.Center;
_hourlyTempBlocks[i].TextAlignment = TextAlignment.Center; _hourlyTempBlocks[i].TextAlignment = TextAlignment.Center;
if (_hourlyTimeBlocks[i].Parent is StackPanel hourlyStack) if (_hourlyTimeBlocks[i].Parent is StackPanel hourlyStack)

View File

@@ -12,7 +12,6 @@ using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using FluentIcons.Common; using FluentIcons.Common;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -124,7 +123,6 @@ public partial class MusicControlWidget : UserControl, IDesktopComponentWidget,
NextIcon.FontSize = Math.Clamp(18 * scale, 13, 24); NextIcon.FontSize = Math.Clamp(18 * scale, 13, 24);
FavoriteIcon.FontSize = Math.Clamp(16 * scale, 11, 21); FavoriteIcon.FontSize = Math.Clamp(16 * scale, 11, 21);
UpdateTypography();
UpdateProgressVisual(_progressRatio, _isProgressIndeterminate); UpdateProgressVisual(_progressRatio, _isProgressIndeterminate);
} }
@@ -420,7 +418,6 @@ public partial class MusicControlWidget : UserControl, IDesktopComponentWidget,
SetCoverImage(state.ThumbnailBytes); SetCoverImage(state.ThumbnailBytes);
ApplyActionButtonState(state); ApplyActionButtonState(state);
UpdateTypography();
UpdateSourceAppButtonTooltip(); UpdateSourceAppButtonTooltip();
} }
@@ -555,67 +552,6 @@ public partial class MusicControlWidget : UserControl, IDesktopComponentWidget,
}; };
} }
private void UpdateTypography()
{
var scale = ResolveScale();
var rootWidth = Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 10.5;
var rootHeight = Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 4.2;
var headerWidth = Math.Max(120, rootWidth - Math.Max(84, SourceAppButton.MinWidth) - 86);
var titleWidth = Math.Max(96, headerWidth);
var metaWidth = Math.Max(96, headerWidth);
var timelineWidth = Math.Max(52, rootWidth * 0.18);
var statusWidth = Math.Max(72, Math.Min(headerWidth, rootWidth * 0.26));
TitleTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
TitleTextBlock.Text,
titleWidth,
Math.Max(24, rootHeight * 0.12),
1,
12,
Math.Clamp(20 * scale, 12, 28),
FontWeight.SemiBold,
1.06d);
var artistMaxLines = ArtistTextBlock.MaxLines <= 0 ? 1 : ArtistTextBlock.MaxLines;
ArtistTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
ArtistTextBlock.Text,
metaWidth,
artistMaxLines > 1 ? Math.Max(32, rootHeight * 0.12) : Math.Max(20, rootHeight * 0.08),
artistMaxLines,
9,
Math.Clamp(14 * scale, 9, 18),
FontWeight.SemiBold,
1.06d);
PositionTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
PositionTextBlock.Text,
timelineWidth,
18,
1,
8,
Math.Clamp(13 * scale, 8, 15),
FontWeight.SemiBold,
1.05d);
DurationTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
DurationTextBlock.Text,
timelineWidth,
18,
1,
8,
Math.Clamp(13 * scale, 8, 15),
FontWeight.SemiBold,
1.05d);
StatusTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
StatusTextBlock.Text,
statusWidth,
18,
1,
8,
Math.Clamp(13 * scale, 8, 15),
FontWeight.Medium,
1.05d);
}
private string L(string key, string fallback) private string L(string key, string fallback)
{ {
return _localizationService.GetString(_languageCode, key, fallback); return _localizationService.GetString(_languageCode, key, fallback);

View File

@@ -5,8 +5,6 @@ using System.Threading.Tasks;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Media;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -75,6 +73,8 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
Resources["OfficeRecentDocumentsAccentSize"] = accentSize; Resources["OfficeRecentDocumentsAccentSize"] = accentSize;
Resources["OfficeRecentDocumentsAccentCornerRadius"] = new CornerRadius(accentSize / 2d); Resources["OfficeRecentDocumentsAccentCornerRadius"] = new CornerRadius(accentSize / 2d);
Resources["OfficeRecentDocumentsHeaderFontSize"] = Math.Clamp(18 * scale, 12, 24);
Resources["OfficeRecentDocumentsStatusFontSize"] = Math.Clamp(14 * scale, 10, 18);
Resources["OfficeRecentDocumentsDocumentSpacing"] = ComponentChromeCornerRadiusHelper.SafeValue(8 * scale, 4, 12, null, 0.40d); Resources["OfficeRecentDocumentsDocumentSpacing"] = ComponentChromeCornerRadiusHelper.SafeValue(8 * scale, 4, 12, null, 0.40d);
var cardWidth = Math.Clamp(130 * scale, 96, 180); var cardWidth = Math.Clamp(130 * scale, 96, 180);
@@ -83,7 +83,8 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
Resources["OfficeRecentDocumentsDocumentCardHeight"] = cardHeight; Resources["OfficeRecentDocumentsDocumentCardHeight"] = cardHeight;
Resources["OfficeRecentDocumentsCardCornerRadius"] = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 10, 24); Resources["OfficeRecentDocumentsCardCornerRadius"] = ComponentChromeCornerRadiusHelper.Scale(16 * scale, 10, 24);
Resources["OfficeRecentDocumentsCardPadding"] = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(10 * scale, 6, 16, null, 0.50d)); Resources["OfficeRecentDocumentsCardPadding"] = new Thickness(ComponentChromeCornerRadiusHelper.SafeValue(10 * scale, 6, 16, null, 0.50d));
UpdateTypographyResources(); Resources["OfficeRecentDocumentsDocumentTitleFontSize"] = Math.Clamp(12 * scale, 10, 18);
Resources["OfficeRecentDocumentsDocumentTimeFontSize"] = Math.Clamp(10 * scale, 8, 14);
} }
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode) public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
@@ -127,19 +128,16 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
{ {
StatusTextBlock.Text = "\u6682\u65e0\u6700\u8fd1\u6587\u6863"; StatusTextBlock.Text = "\u6682\u65e0\u6700\u8fd1\u6587\u6863";
StatusTextBlock.IsVisible = true; StatusTextBlock.IsVisible = true;
UpdateTypographyResources();
return; return;
} }
UpdateDisplay(); UpdateDisplay();
UpdateTypographyResources();
} }
catch (Exception ex) catch (Exception ex)
{ {
AppLogger.Warn("OfficeRecentDocsWidget", "Failed to load recent Office documents.", ex); AppLogger.Warn("OfficeRecentDocsWidget", "Failed to load recent Office documents.", ex);
StatusTextBlock.Text = "\u52a0\u8f7d\u5931\u8d25"; StatusTextBlock.Text = "\u52a0\u8f7d\u5931\u8d25";
StatusTextBlock.IsVisible = true; StatusTextBlock.IsVisible = true;
UpdateTypographyResources();
} }
finally finally
{ {
@@ -165,7 +163,6 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
}).ToList(); }).ToList();
DocumentsItemsControl.ItemsSource = displayItems; DocumentsItemsControl.ItemsSource = displayItems;
UpdateTypographyResources();
} }
private static string GetTimeAgo(DateTime dateTime) private static string GetTimeAgo(DateTime dateTime)
@@ -218,70 +215,4 @@ public partial class OfficeRecentDocumentsWidget : UserControl, IDesktopComponen
} }
} }
} }
private void UpdateTypographyResources()
{
var width = Bounds.Width > 1 ? Bounds.Width : 640d;
var cardWidth = (double?)Resources["OfficeRecentDocumentsDocumentCardWidth"] ?? 130d;
var cardHeight = (double?)Resources["OfficeRecentDocumentsDocumentCardHeight"] ?? 90d;
var cardPadding = (Thickness?)Resources["OfficeRecentDocumentsCardPadding"] ?? new Thickness(10);
var rootPadding = (Thickness?)Resources["OfficeRecentDocumentsRootPadding"] ?? new Thickness(12, 10, 12, 10);
var contentMargin = (Thickness?)Resources["OfficeRecentDocumentsContentMargin"] ?? new Thickness(16, 14, 16, 14);
var innerWidth = Math.Max(180, width - rootPadding.Left - rootPadding.Right - contentMargin.Left - contentMargin.Right);
var headerWidth = Math.Max(120, innerWidth * 0.48);
var statusWidth = Math.Max(120, innerWidth * 0.40);
Resources["OfficeRecentDocumentsHeaderFontSize"] = ComponentTypographyLayoutService.FitFontSize(
HeaderTextBlock.Text,
headerWidth,
24,
1,
12,
24,
FontWeight.SemiBold,
1.05d);
Resources["OfficeRecentDocumentsStatusFontSize"] = ComponentTypographyLayoutService.FitFontSize(
StatusTextBlock.Text,
statusWidth,
22,
1,
10,
18,
FontWeight.Normal,
1.06d);
var documentTexts = _documents.Count == 0
? new[] { "Sample Office Document" }
: _documents.Select(item => item.FileName).Where(text => !string.IsNullOrWhiteSpace(text)).ToArray();
var longestDocumentText = documentTexts.Length == 0
? "Sample Office Document"
: documentTexts.OrderByDescending(ComponentTypographyLayoutService.CountTextDisplayUnits).First();
var titleWidth = Math.Max(72, cardWidth - cardPadding.Left - cardPadding.Right);
var titleHeight = Math.Max(28, cardHeight - cardPadding.Top - cardPadding.Bottom - 18);
Resources["OfficeRecentDocumentsDocumentTitleFontSize"] = ComponentTypographyLayoutService.FitFontSize(
longestDocumentText,
titleWidth,
titleHeight,
2,
10,
18,
FontWeight.Medium,
1.08d);
var timeSamples = _documents.Count == 0
? new[] { "00/00" }
: _documents.Select(item => GetTimeAgo(item.LastModifiedTime)).ToArray();
var longestTimeText = timeSamples.OrderByDescending(ComponentTypographyLayoutService.CountTextDisplayUnits).First();
Resources["OfficeRecentDocumentsDocumentTimeFontSize"] = ComponentTypographyLayoutService.FitFontSize(
longestTimeText,
Math.Max(56, titleWidth * 0.72),
18,
1,
8,
14,
FontWeight.Normal,
1.06d);
}
} }

View File

@@ -10,7 +10,6 @@ using Avalonia.Media;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -109,8 +108,10 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
HintTextBlock.Margin = new Thickness(0, Math.Clamp(8 * contentScale, 4, 10), 0, 0); HintTextBlock.Margin = new Thickness(0, Math.Clamp(8 * contentScale, 4, 10), 0, 0);
WaveformBarsPanel.Spacing = Math.Clamp(3 * contentScale, 1.6, 3.4); WaveformBarsPanel.Spacing = Math.Clamp(3 * contentScale, 1.6, 3.4);
TitleTextBlock.FontSize = Math.Clamp(19 * contentScale, 12, 20);
TimerTextBlock.FontSize = Math.Clamp(66 * contentScale, 34, 66);
HintTextBlock.FontSize = Math.Clamp(13 * contentScale, 9, 13);
UpdateTypography();
UpdateWaveformVisual(); UpdateWaveformVisual();
} }
@@ -378,43 +379,49 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
PauseGlyphIcon.IsVisible = snapshot.State == AudioRecorderRuntimeState.Recording; PauseGlyphIcon.IsVisible = snapshot.State == AudioRecorderRuntimeState.Recording;
PlayGlyphIcon.IsVisible = snapshot.State == AudioRecorderRuntimeState.Paused; PlayGlyphIcon.IsVisible = snapshot.State == AudioRecorderRuntimeState.Paused;
string hintText;
if (!isSupported) if (!isSupported)
{ {
hintText = L("recording.widget.hint.unsupported", "Microphone is unavailable"); HintTextBlock.Text = L("recording.widget.hint.unsupported", "Microphone is unavailable");
return;
} }
else if (snapshot.State == AudioRecorderRuntimeState.Recording)
if (snapshot.State == AudioRecorderRuntimeState.Recording)
{ {
hintText = L("recording.widget.hint.recording", "Recording"); HintTextBlock.Text = L("recording.widget.hint.recording", "Recording");
return;
} }
else if (snapshot.State == AudioRecorderRuntimeState.Paused)
if (snapshot.State == AudioRecorderRuntimeState.Paused)
{ {
hintText = L("recording.widget.hint.paused", "Paused"); HintTextBlock.Text = L("recording.widget.hint.paused", "Paused");
return;
} }
else if (snapshot.State == AudioRecorderRuntimeState.Error)
if (snapshot.State == AudioRecorderRuntimeState.Error)
{ {
hintText = string.IsNullOrWhiteSpace(snapshot.LastError) HintTextBlock.Text = string.IsNullOrWhiteSpace(snapshot.LastError)
? L("recording.widget.hint.error", "Recording failed") ? L("recording.widget.hint.error", "Recording failed")
: snapshot.LastError; : snapshot.LastError;
return;
} }
else
{
if (!string.IsNullOrWhiteSpace(snapshot.LastSavedFilePath) && if (!string.IsNullOrWhiteSpace(snapshot.LastSavedFilePath) &&
!string.Equals(snapshot.LastSavedFilePath, _lastSavedFilePath, StringComparison.OrdinalIgnoreCase)) !string.Equals(snapshot.LastSavedFilePath, _lastSavedFilePath, StringComparison.OrdinalIgnoreCase))
{ {
_lastSavedFilePath = snapshot.LastSavedFilePath; _lastSavedFilePath = snapshot.LastSavedFilePath;
} }
hintText = !string.IsNullOrWhiteSpace(_lastSavedFilePath) if (!string.IsNullOrWhiteSpace(_lastSavedFilePath))
? string.Format( {
var fileName = Path.GetFileName(_lastSavedFilePath);
HintTextBlock.Text = string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
L("recording.widget.hint.saved_format", "Saved {0}"), L("recording.widget.hint.saved_format", "Saved {0}"),
Path.GetFileName(_lastSavedFilePath)) fileName);
: L("recording.widget.hint.ready", "Tap red button to record"); return;
} }
HintTextBlock.Text = hintText; HintTextBlock.Text = L("recording.widget.hint.ready", "Tap red button to record");
UpdateTypography();
} }
private bool TryStartRecordingWithMonitoringHandoff() private bool TryStartRecordingWithMonitoringHandoff()
@@ -567,51 +574,6 @@ public partial class RecordingWidget : UserControl, IDesktopComponentWidget, IDe
return duration.ToString(@"mm\:ss", CultureInfo.InvariantCulture); return duration.ToString(@"mm\:ss", CultureInfo.InvariantCulture);
} }
private void UpdateTypography()
{
var contentWidth = RecorderContentGrid.Bounds.Width > 1 ? RecorderContentGrid.Bounds.Width : 252d;
var contentHeight = RecorderContentGrid.Bounds.Height > 1 ? RecorderContentGrid.Bounds.Height : 240d;
var timerWidth = Math.Max(88, contentWidth * 0.84);
var timerHeight = Math.Max(34, contentHeight * 0.24);
var hintWidth = Math.Max(120, contentWidth * 0.86);
var hintHeight = Math.Max(24, contentHeight * 0.12);
TitleTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
TitleTextBlock.Text,
Math.Max(96, contentWidth * 0.62),
20,
1,
12,
20,
FontWeight.SemiBold,
1.05d);
TimerTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
TimerTextBlock.Text,
timerWidth,
timerHeight,
1,
34,
66,
FontWeight.SemiBold,
1.0d);
var hintLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
HintTextBlock.Text,
hintWidth,
hintHeight,
1,
2,
9,
13,
[FontWeight.Medium, FontWeight.Normal],
1.10d);
HintTextBlock.FontSize = hintLayout.FontSize;
HintTextBlock.FontWeight = hintLayout.Weight;
HintTextBlock.MaxLines = hintLayout.MaxLines;
HintTextBlock.TextWrapping = hintLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
}
private static IBrush CreateBrush(string colorHex) private static IBrush CreateBrush(string colorHex)
{ {
return new SolidColorBrush(Color.Parse(colorHex)); return new SolidColorBrush(Color.Parse(colorHex));

View File

@@ -9,7 +9,6 @@ using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using FluentIcons.Avalonia; using FluentIcons.Avalonia;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk; using LanMountainDesktop.PluginSdk;
@@ -365,6 +364,11 @@ public partial class RemovableStorageWidget : UserControl, IDesktopComponentWidg
IconBadge.CornerRadius = new CornerRadius(badgeSize * 0.5); IconBadge.CornerRadius = new CornerRadius(badgeSize * 0.5);
DriveIcon.FontSize = Math.Clamp(24 * scale, 20, 32); DriveIcon.FontSize = Math.Clamp(24 * scale, 20, 32);
DriveNameTextBlock.FontSize = Math.Clamp(16 * scale, 13, 24);
DriveDetailTextBlock.FontSize = Math.Clamp(11.5 * scale, 10, 16);
StatusTextBlock.FontSize = Math.Clamp(12 * scale, 10, 17);
StatusTextBlock.MaxWidth = Math.Max(96, width - (RootBorder.Padding.Left + RootBorder.Padding.Right));
var buttonHeight = Math.Clamp(42 * scale, 38, 54); var buttonHeight = Math.Clamp(42 * scale, 38, 54);
var buttonPadding = Math.Clamp(14 * scale, 10, 20); var buttonPadding = Math.Clamp(14 * scale, 10, 20);
var buttonCornerRadius = Math.Clamp(buttonHeight * 0.5, 18, 999); var buttonCornerRadius = Math.Clamp(buttonHeight * 0.5, 18, 999);
@@ -379,14 +383,14 @@ public partial class RemovableStorageWidget : UserControl, IDesktopComponentWidg
OpenButtonIcon.FontSize = Math.Clamp(16 * scale, 14, 20); OpenButtonIcon.FontSize = Math.Clamp(16 * scale, 14, 20);
EjectButtonIcon.FontSize = Math.Clamp(16 * scale, 14, 20); EjectButtonIcon.FontSize = Math.Clamp(16 * scale, 14, 20);
OpenButtonTextBlock.FontSize = Math.Clamp(13 * scale, 11.5, 18);
EjectButtonTextBlock.FontSize = Math.Clamp(13 * scale, 11.5, 18);
AccentOrb.Width = Math.Clamp(width * 0.44, 96, 176); AccentOrb.Width = Math.Clamp(width * 0.44, 96, 176);
AccentOrb.Height = AccentOrb.Width; AccentOrb.Height = AccentOrb.Width;
AccentOrb.CornerRadius = new CornerRadius(AccentOrb.Width * 0.5); AccentOrb.CornerRadius = new CornerRadius(AccentOrb.Width * 0.5);
AccentGlow.Height = Math.Clamp(76 * scale, 52, 110); AccentGlow.Height = Math.Clamp(76 * scale, 52, 110);
AccentGlow.CornerRadius = new CornerRadius(AccentGlow.Height * 0.5); AccentGlow.CornerRadius = new CornerRadius(AccentGlow.Height * 0.5);
UpdateTypography();
} }
private RemovableStorageDrive? GetSelectedDrive() private RemovableStorageDrive? GetSelectedDrive()
@@ -518,67 +522,6 @@ public partial class RemovableStorageWidget : UserControl, IDesktopComponentWidg
return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.72, 2.2); return Math.Clamp(Math.Min(cellScale, Math.Min(widthScale, heightScale)), 0.72, 2.2);
} }
private void UpdateTypography()
{
var scale = ResolveScale();
var width = Bounds.Width > 1 ? Bounds.Width : 220d;
var rootPadding = RootBorder.Padding;
var headerWidth = Math.Max(96, width - rootPadding.Left - rootPadding.Right - Math.Max(44, IconBadge.Width) - HeaderGrid.ColumnSpacing);
var statusWidth = Math.Max(120, width - rootPadding.Left - rootPadding.Right);
var buttonWidth = Math.Max(88, width - rootPadding.Left - rootPadding.Right);
DriveNameTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
DriveNameTextBlock.Text,
headerWidth,
22,
1,
13,
24,
FontWeight.SemiBold,
1.05d);
DriveDetailTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
DriveDetailTextBlock.Text,
headerWidth,
18,
1,
10,
16,
FontWeight.Normal,
1.05d);
StatusTextBlock.FontSize = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
StatusTextBlock.Text,
statusWidth,
Math.Max(28, 40 * scale),
1,
3,
10,
17,
[FontWeight.Medium, FontWeight.Normal],
1.10d).FontSize;
StatusTextBlock.MaxWidth = statusWidth;
StatusTextBlock.MaxLines = 3;
StatusTextBlock.TextWrapping = TextWrapping.Wrap;
OpenButtonTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
OpenButtonTextBlock.Text,
buttonWidth - 32,
Math.Max(18, OpenButton.Height - 8),
1,
11,
18,
FontWeight.SemiBold,
1.04d);
EjectButtonTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
EjectButtonTextBlock.Text,
buttonWidth - 32,
Math.Max(18, EjectButton.Height - 8),
1,
11,
18,
FontWeight.SemiBold,
1.04d);
}
private string L(string key, string fallback) private string L(string key, string fallback)
{ {
return _localizationService.GetString(_languageCode, key, fallback); return _localizationService.GetString(_languageCode, key, fallback);

View File

@@ -15,7 +15,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -676,32 +675,9 @@ public partial class Stcn24ForumWidget : UserControl, IDesktopComponentWidget, I
visual.AvatarHost.Height = avatarSize; visual.AvatarHost.Height = avatarSize;
visual.AvatarHost.CornerRadius = new CornerRadius(avatarSize / 2d); visual.AvatarHost.CornerRadius = new CornerRadius(avatarSize / 2d);
var avatarGlyphBox = ComponentTypographyLayoutService.ResolveGlyphBox( visual.AvatarFallbackText.FontSize = avatarFont;
avatarSize, visual.TitleTextBlock.FontSize = titleFont;
avatarSize,
preferredSizeScale: 0.60d,
minSize: 12,
maxSize: 18);
visual.AvatarFallbackText.Margin = avatarGlyphBox.Margin;
visual.AvatarFallbackText.FontSize = Math.Min(avatarFont, Math.Max(avatarGlyphBox.Width * 0.55d, 10));
visual.AvatarFallbackText.MaxWidth = avatarGlyphBox.Width;
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
visual.TitleTextBlock.Text,
titleMaxWidth,
Math.Max(avatarSize, rowPaddingVertical * 2d + 18),
minLines: 1,
maxLines: ComponentTypographyLayoutService.CountTextDisplayUnits(visual.TitleTextBlock.Text) > 24 ? 2 : 1,
minFontSize: Math.Clamp(titleFont * 0.72, 10, 14),
maxFontSize: titleFont,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Bold },
lineHeightFactor: 1.08d,
fontFamily: MiSansFontFamily);
visual.TitleTextBlock.FontSize = titleLayout.FontSize;
visual.TitleTextBlock.LineHeight = titleLayout.LineHeight;
visual.TitleTextBlock.MaxWidth = titleMaxWidth; visual.TitleTextBlock.MaxWidth = titleMaxWidth;
visual.TitleTextBlock.MaxLines = titleLayout.MaxLines;
visual.TitleTextBlock.FontWeight = titleLayout.Weight;
} }
StatusTextBlock.FontSize = Math.Clamp(14 * softScale, 10, 18); StatusTextBlock.FontSize = Math.Clamp(14 * softScale, 10, 18);

View File

@@ -6,7 +6,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -285,88 +284,6 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
ApplyVariableWeights(scale); ApplyVariableWeights(scale);
ApplyLocalizedLabels(); ApplyLocalizedLabels();
var contentWidth = Math.Max(120, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 8) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(78, (Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 3) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
TitleTextBlock.Text,
Math.Max(120, contentWidth * 0.44),
Math.Max(18, contentHeight * 0.22),
1,
1,
9,
Math.Clamp(20 * scale, 9, 20),
[TitleTextBlock.FontWeight],
1.05);
TitleTextBlock.FontSize = titleLayout.FontSize;
TitleTextBlock.FontWeight = titleLayout.Weight;
TitleTextBlock.MaxLines = 1;
TitleTextBlock.TextWrapping = TextWrapping.NoWrap;
TitleTextBlock.LineHeight = titleLayout.LineHeight;
var modeBadgeBox = ComponentTypographyLayoutService.ResolveBadgeBox(
Math.Max(64, contentWidth * 0.24),
Math.Max(20, contentHeight * 0.14),
preferredSizeScale: 0.48d,
minSize: 18,
maxSize: 42,
insetScale: 0.18d);
ModeBadgeBorder.Padding = modeBadgeBox.Padding;
ModeBadgeBorder.CornerRadius = new CornerRadius(Math.Clamp(modeBadgeBox.Size * 0.36, 5, 12));
var modeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ModeTextBlock.Text,
Math.Max(54, modeBadgeBox.Width),
Math.Max(18, modeBadgeBox.Height),
1,
1,
8,
Math.Clamp(16 * scale, 8, 16),
[ModeTextBlock.FontWeight],
1.02);
ModeTextBlock.FontSize = modeLayout.FontSize;
ModeTextBlock.FontWeight = modeLayout.Weight;
ModeTextBlock.MaxLines = 1;
ModeTextBlock.TextWrapping = TextWrapping.NoWrap;
ModeTextBlock.LineHeight = modeLayout.LineHeight;
foreach (var block in new[] { SustainedReasonTextBlock, TimeReasonTextBlock, SegmentReasonTextBlock })
{
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
Math.Max(84, contentWidth * 0.34),
Math.Max(18, contentHeight * 0.14),
1,
_isUltraCompactMode ? 1 : 2,
9,
Math.Clamp(18 * scale, 9, 18),
[block.FontWeight],
1.05);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = layout.MaxLines;
block.TextWrapping = layout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
foreach (var block in new[] { SustainedMetricTextBlock, TimeMetricTextBlock, SegmentMetricTextBlock, TotalLossTextBlock, ScoreTextBlock })
{
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
Math.Max(72, contentWidth * 0.22),
Math.Max(16, contentHeight * 0.10),
1,
1,
8,
Math.Clamp(16 * scale, 8, 16),
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
} }
private void ApplyTypographyByBackground(Color panelColor) private void ApplyTypographyByBackground(Color panelColor)

View File

@@ -5,7 +5,6 @@ using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -252,77 +251,6 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
LayoutGrid.ColumnSpacing = hideStatusLabel LayoutGrid.ColumnSpacing = hideStatusLabel
? Math.Clamp(6 * scale, 4, 10) ? Math.Clamp(6 * scale, 4, 10)
: Math.Clamp(10 * scale, 7, 14); : Math.Clamp(10 * scale, 7, 14);
var availableWidth = Math.Max(72, (width > 0 ? width : _currentCellSize * 4) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var availableHeight = Math.Max(34, (height > 0 ? height : _currentCellSize * 2) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var statusTitleWidth = Math.Max(50, availableWidth * 0.32);
var statusValueWidth = Math.Max(78, availableWidth - statusTitleWidth - Math.Clamp(6 * scale, 3, 8));
var valueHeight = Math.Max(18, availableHeight * 0.44);
var statusTitleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
StatusTitleTextBlock.Text,
statusTitleWidth,
valueHeight,
1,
1,
9,
Math.Clamp(18 * scale, 9, 18),
[StatusTitleTextBlock.FontWeight],
1.04);
StatusTitleTextBlock.FontSize = statusTitleLayout.FontSize;
StatusTitleTextBlock.FontWeight = statusTitleLayout.Weight;
StatusTitleTextBlock.MaxLines = 1;
StatusTitleTextBlock.TextWrapping = TextWrapping.NoWrap;
StatusTitleTextBlock.LineHeight = statusTitleLayout.LineHeight;
var statusValueLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
StatusValueTextBlock.Text,
statusValueWidth,
valueHeight,
1,
1,
12,
Math.Clamp(34 * scale, 12, 34),
[StatusValueTextBlock.FontWeight],
1.04);
StatusValueTextBlock.FontSize = statusValueLayout.FontSize;
StatusValueTextBlock.FontWeight = statusValueLayout.Weight;
StatusValueTextBlock.MaxLines = 1;
StatusValueTextBlock.TextWrapping = TextWrapping.NoWrap;
StatusValueTextBlock.LineHeight = statusValueLayout.LineHeight;
var noiseValueLines = _showDisplayDb && _showDbfs ? 2 : 1;
var noiseValueLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
NoiseValueTextBlock.Text,
Math.Max(92, availableWidth * 0.48),
Math.Max(22, availableHeight * 0.56),
1,
noiseValueLines,
12,
Math.Clamp(38 * scale, 12, 38),
[NoiseValueTextBlock.FontWeight],
1.06);
NoiseValueTextBlock.FontSize = noiseValueLayout.FontSize;
NoiseValueTextBlock.FontWeight = noiseValueLayout.Weight;
NoiseValueTextBlock.MaxLines = noiseValueLayout.MaxLines;
NoiseValueTextBlock.TextWrapping = noiseValueLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
NoiseValueTextBlock.LineHeight = noiseValueLayout.LineHeight;
var noiseSubValueLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
NoiseSubValueTextBlock.Text,
Math.Max(72, availableWidth * 0.34),
Math.Max(18, availableHeight * 0.24),
1,
1,
9,
Math.Clamp(18 * scale, 9, 18),
[NoiseSubValueTextBlock.FontWeight],
1.04);
NoiseSubValueTextBlock.FontSize = noiseSubValueLayout.FontSize;
NoiseSubValueTextBlock.FontWeight = noiseSubValueLayout.Weight;
NoiseSubValueTextBlock.MaxLines = 1;
NoiseSubValueTextBlock.TextWrapping = TextWrapping.NoWrap;
NoiseSubValueTextBlock.LineHeight = noiseSubValueLayout.LineHeight;
} }
private string ResolveStatusText(StudyAnalyticsSnapshot snapshot) private string ResolveStatusText(StudyAnalyticsSnapshot snapshot)

View File

@@ -5,7 +5,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -311,92 +310,6 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
ApplyVariableWeights(scale); ApplyVariableWeights(scale);
ApplyLocalizedLabels(); ApplyLocalizedLabels();
var contentWidth = Math.Max(120, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 8) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(78, (Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 3) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
TitleTextBlock.Text,
Math.Max(120, contentWidth * 0.38),
Math.Max(18, contentHeight * 0.18),
1,
1,
9,
Math.Clamp(20 * scale, 9, 20),
[TitleTextBlock.FontWeight],
1.05);
TitleTextBlock.FontSize = titleLayout.FontSize;
TitleTextBlock.FontWeight = titleLayout.Weight;
TitleTextBlock.MaxLines = 1;
TitleTextBlock.TextWrapping = TextWrapping.NoWrap;
TitleTextBlock.LineHeight = titleLayout.LineHeight;
var modeBadgeBox = ComponentTypographyLayoutService.ResolveBadgeBox(
Math.Max(64, contentWidth * 0.22),
Math.Max(20, contentHeight * 0.14),
preferredSizeScale: 0.46d,
minSize: 18,
maxSize: 42,
insetScale: 0.18d);
ModeBadgeBorder.Padding = modeBadgeBox.Padding;
ModeBadgeBorder.CornerRadius = new CornerRadius(Math.Clamp(modeBadgeBox.Size * 0.36, 4, 12));
var modeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ModeTextBlock.Text,
Math.Max(52, modeBadgeBox.Width),
Math.Max(18, modeBadgeBox.Height),
1,
1,
8,
Math.Clamp(16 * scale, 8, 16),
[ModeTextBlock.FontWeight],
1.02);
ModeTextBlock.FontSize = modeLayout.FontSize;
ModeTextBlock.FontWeight = modeLayout.Weight;
ModeTextBlock.MaxLines = 1;
ModeTextBlock.TextWrapping = TextWrapping.NoWrap;
ModeTextBlock.LineHeight = modeLayout.LineHeight;
foreach (var block in new[] { DensityValueTextBlock, CountValueTextBlock, DurationValueTextBlock })
{
var minFont = block == DensityValueTextBlock ? 18 : 10;
var maxFont = block == DensityValueTextBlock ? Math.Clamp(94 * scale, 18, 94) : Math.Clamp(36 * scale, 10, 36);
var maxWidth = block == DensityValueTextBlock ? Math.Max(86, contentWidth * 0.24) : Math.Max(64, contentWidth * 0.18);
var maxHeight = block == DensityValueTextBlock ? Math.Max(24, contentHeight * 0.26) : Math.Max(18, contentHeight * 0.18);
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
maxWidth,
maxHeight,
1,
1,
minFont,
maxFont,
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
foreach (var block in new[] { DensityUnitTextBlock, DensityLevelTextBlock, CountLabelTextBlock, DurationLabelTextBlock, ThresholdTextBlock })
{
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
Math.Max(64, contentWidth * 0.18),
Math.Max(16, contentHeight * 0.14),
1,
1,
8,
Math.Clamp(18 * scale, 8, 18),
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
} }
private void ApplyTypographyByBackground(Color panelColor) private void ApplyTypographyByBackground(Color panelColor)

View File

@@ -5,7 +5,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -129,50 +128,6 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
XLeftTextBlock.FontSize = axisFontSize; XLeftTextBlock.FontSize = axisFontSize;
XCenterTextBlock.FontSize = axisFontSize; XCenterTextBlock.FontSize = axisFontSize;
XRightTextBlock.FontSize = axisFontSize; XRightTextBlock.FontSize = axisFontSize;
var contentWidth = Math.Max(110, (_currentCellSize * 4) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(72, (_currentCellSize * 2) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var statusLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
StatusTextBlock.Text,
Math.Max(72, contentWidth * 0.34),
Math.Max(18, contentHeight * 0.18),
1,
1,
12,
Math.Clamp(30 * scale, 12, 30),
[StatusTextBlock.FontWeight],
1.03);
StatusTextBlock.FontSize = statusLayout.FontSize;
StatusTextBlock.FontWeight = statusLayout.Weight;
StatusTextBlock.MaxLines = 1;
StatusTextBlock.TextWrapping = TextWrapping.NoWrap;
StatusTextBlock.LineHeight = statusLayout.LineHeight;
var realtimeValueLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
RealtimeValueTextBlock.Text,
Math.Max(82, contentWidth * 0.40),
Math.Max(20, contentHeight * 0.22),
1,
1,
12,
Math.Clamp(34 * scale, 12, 34),
[RealtimeValueTextBlock.FontWeight],
1.03);
RealtimeValueTextBlock.FontSize = realtimeValueLayout.FontSize;
RealtimeValueTextBlock.FontWeight = realtimeValueLayout.Weight;
RealtimeValueTextBlock.MaxLines = 1;
RealtimeValueTextBlock.TextWrapping = TextWrapping.NoWrap;
RealtimeValueTextBlock.LineHeight = realtimeValueLayout.LineHeight;
var badgeBox = ComponentTypographyLayoutService.ResolveBadgeBox(
Math.Max(64, contentWidth * 0.20),
Math.Max(20, contentHeight * 0.12),
preferredSizeScale: 0.46d,
minSize: 18,
maxSize: 40,
insetScale: 0.18d);
StatusBadgeBorder.Padding = badgeBox.Padding;
StatusBadgeBorder.CornerRadius = new CornerRadius(Math.Clamp(badgeBox.Size * 0.36, 5, 12));
} }
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode) public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)

View File

@@ -6,7 +6,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -359,85 +358,6 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
SummaryTextBlock.IsVisible = true; SummaryTextBlock.IsVisible = true;
ApplyVariableWeights(scale); ApplyVariableWeights(scale);
var contentWidth = Math.Max(120, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 8) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(78, (Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 3) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
TitleTextBlock.Text,
Math.Max(120, contentWidth * 0.38),
Math.Max(18, contentHeight * 0.18),
1,
1,
9,
Math.Clamp(22 * scale, 9, 22),
[TitleTextBlock.FontWeight],
1.05);
TitleTextBlock.FontSize = titleLayout.FontSize;
TitleTextBlock.FontWeight = titleLayout.Weight;
TitleTextBlock.MaxLines = 1;
TitleTextBlock.TextWrapping = TextWrapping.NoWrap;
TitleTextBlock.LineHeight = titleLayout.LineHeight;
var summaryLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
SummaryTextBlock.Text,
Math.Max(104, contentWidth * 0.44),
Math.Max(18, contentHeight * 0.16),
1,
_isUltraCompactMode ? 1 : 2,
8,
Math.Clamp(20 * scale, 8, 20),
[SummaryTextBlock.FontWeight],
1.06);
SummaryTextBlock.FontSize = summaryLayout.FontSize;
SummaryTextBlock.FontWeight = summaryLayout.Weight;
SummaryTextBlock.MaxLines = summaryLayout.MaxLines;
SummaryTextBlock.TextWrapping = summaryLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
SummaryTextBlock.LineHeight = summaryLayout.LineHeight;
var modeBadgeBox = ComponentTypographyLayoutService.ResolveBadgeBox(
Math.Max(64, contentWidth * 0.22),
Math.Max(20, contentHeight * 0.14),
preferredSizeScale: 0.46d,
minSize: 18,
maxSize: 42,
insetScale: 0.18d);
ModeBadgeBorder.Padding = modeBadgeBox.Padding;
ModeBadgeBorder.CornerRadius = new CornerRadius(Math.Clamp(modeBadgeBox.Size * 0.36, 4, 12));
var modeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ModeTextBlock.Text,
Math.Max(52, modeBadgeBox.Width),
Math.Max(18, modeBadgeBox.Height),
1,
1,
8,
Math.Clamp(18 * scale, 8, 18),
[ModeTextBlock.FontWeight],
1.02);
ModeTextBlock.FontSize = modeLayout.FontSize;
ModeTextBlock.FontWeight = modeLayout.Weight;
ModeTextBlock.MaxLines = 1;
ModeTextBlock.TextWrapping = TextWrapping.NoWrap;
ModeTextBlock.LineHeight = modeLayout.LineHeight;
foreach (var block in new[] { YExtremeTextBlock, YNoisyTextBlock, YNormalTextBlock, YQuietTextBlock, XLeftTextBlock, XCenterTextBlock, XRightTextBlock })
{
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
Math.Max(36, contentWidth * 0.12),
Math.Max(14, contentHeight * 0.08),
1,
1,
8,
Math.Clamp(16 * scale, 8, 16),
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
} }
private void ApplyTypographyByBackground(Color panelColor) private void ApplyTypographyByBackground(Color panelColor)

View File

@@ -6,7 +6,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -337,92 +336,6 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
ApplyVariableWeights(scale); ApplyVariableWeights(scale);
ApplyLocalizedLabels(); ApplyLocalizedLabels();
var contentWidth = Math.Max(140, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 8) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(96, (Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 4) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
TitleTextBlock.Text,
Math.Max(120, contentWidth * 0.42),
Math.Max(18, contentHeight * 0.14),
1,
1,
9,
Math.Clamp(30 * scale * labelFactor, 9, 30),
[TitleTextBlock.FontWeight],
1.05);
TitleTextBlock.FontSize = titleLayout.FontSize;
TitleTextBlock.FontWeight = titleLayout.Weight;
TitleTextBlock.MaxLines = 1;
TitleTextBlock.TextWrapping = TextWrapping.NoWrap;
TitleTextBlock.LineHeight = titleLayout.LineHeight;
var modeBadgeBox = ComponentTypographyLayoutService.ResolveBadgeBox(
Math.Max(64, contentWidth * 0.22),
Math.Max(20, contentHeight * 0.12),
preferredSizeScale: 0.46d,
minSize: 18,
maxSize: 42,
insetScale: 0.18d);
ModeBadgeBorder.Padding = modeBadgeBox.Padding;
ModeBadgeBorder.CornerRadius = new CornerRadius(Math.Clamp(modeBadgeBox.Size * 0.36, 5, 14));
var modeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ModeTextBlock.Text,
Math.Max(52, modeBadgeBox.Width),
Math.Max(18, modeBadgeBox.Height),
1,
1,
8,
Math.Clamp(22 * scale * labelFactor, 8, 22),
[ModeTextBlock.FontWeight],
1.02);
ModeTextBlock.FontSize = modeLayout.FontSize;
ModeTextBlock.FontWeight = modeLayout.Weight;
ModeTextBlock.MaxLines = 1;
ModeTextBlock.TextWrapping = TextWrapping.NoWrap;
ModeTextBlock.LineHeight = modeLayout.LineHeight;
foreach (var block in new[] { CurrentLabelTextBlock, AverageLabelTextBlock, MinimumLabelTextBlock, MaximumLabelTextBlock })
{
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
Math.Max(64, contentWidth * 0.16),
Math.Max(14, contentHeight * 0.10),
1,
1,
8,
Math.Clamp(22 * scale * labelFactor, 8, 22),
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
foreach (var block in new[] { CurrentScoreTextBlock, AverageValueTextBlock, MinimumValueTextBlock, MaximumValueTextBlock })
{
var minFont = block == CurrentScoreTextBlock ? 22 : 11;
var maxFont = block == CurrentScoreTextBlock ? Math.Clamp(190 * scale * headlineFactor, 22, 190) : Math.Clamp(64 * scale * statFactor, 11, 64);
var maxWidth = block == CurrentScoreTextBlock ? Math.Max(96, contentWidth * 0.30) : Math.Max(72, contentWidth * 0.18);
var maxHeight = block == CurrentScoreTextBlock ? Math.Max(30, contentHeight * 0.24) : Math.Max(20, contentHeight * 0.14);
var layout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
block.Text,
maxWidth,
maxHeight,
1,
1,
minFont,
maxFont,
[block.FontWeight],
1.02);
block.FontSize = layout.FontSize;
block.FontWeight = layout.Weight;
block.MaxLines = 1;
block.TextWrapping = TextWrapping.NoWrap;
block.LineHeight = layout.LineHeight;
}
} }
private void PushRealtimeScore(double score, DateTimeOffset now) private void PushRealtimeScore(double score, DateTimeOffset now)

View File

@@ -4,7 +4,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -294,40 +293,6 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
ActionIcon.Height = Math.Clamp(buttonSize * 0.44, 14, 30); ActionIcon.Height = Math.Clamp(buttonSize * 0.44, 14, 30);
SecondaryTextBlock.IsVisible = !_isUltraCompactMode; SecondaryTextBlock.IsVisible = !_isUltraCompactMode;
var contentWidth = Math.Max(96, (Bounds.Width > 1 ? Bounds.Width : _currentCellSize * 4) - RootBorder.Padding.Left - RootBorder.Padding.Right);
var contentHeight = Math.Max(44, (Bounds.Height > 1 ? Bounds.Height : _currentCellSize * 2) - RootBorder.Padding.Top - RootBorder.Padding.Bottom);
var primaryLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
PrimaryTextBlock.Text,
Math.Max(72, contentWidth * 0.58),
Math.Max(18, contentHeight * 0.28),
1,
1,
10,
Math.Clamp(30 * scale, 10, 30),
[PrimaryTextBlock.FontWeight],
1.04);
PrimaryTextBlock.FontSize = primaryLayout.FontSize;
PrimaryTextBlock.FontWeight = primaryLayout.Weight;
PrimaryTextBlock.MaxLines = 1;
PrimaryTextBlock.TextWrapping = TextWrapping.NoWrap;
PrimaryTextBlock.LineHeight = primaryLayout.LineHeight;
var secondaryLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
SecondaryTextBlock.Text,
Math.Max(64, contentWidth * 0.58),
Math.Max(16, contentHeight * 0.22),
1,
_isUltraCompactMode ? 1 : 2,
8,
Math.Clamp(18 * scale, 8, 18),
[SecondaryTextBlock.FontWeight],
1.04);
SecondaryTextBlock.FontSize = secondaryLayout.FontSize;
SecondaryTextBlock.FontWeight = secondaryLayout.Weight;
SecondaryTextBlock.MaxLines = secondaryLayout.MaxLines;
SecondaryTextBlock.TextWrapping = secondaryLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap;
SecondaryTextBlock.LineHeight = secondaryLayout.LineHeight;
} }
private void ApplyTypographyByBackground(Color panelColor) private void ApplyTypographyByBackground(Color panelColor)

View File

@@ -9,7 +9,6 @@ using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using FluentIcons.Avalonia; using FluentIcons.Avalonia;
using FluentIcons.Common; using FluentIcons.Common;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
using LanMountainDesktop.Theme; using LanMountainDesktop.Theme;
@@ -196,7 +195,6 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
StatusTextBlock.Text = _transientStatus ?? L("study.session_history.empty", "No session history"); StatusTextBlock.Text = _transientStatus ?? L("study.session_history.empty", "No session history");
StatusTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, SecondaryColorCandidates, MinTextContrast); StatusTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, SecondaryColorCandidates, MinTextContrast);
ApplyHistoryTypographyLayout();
UpdateDialogVisual(snapshot, panelColor); UpdateDialogVisual(snapshot, panelColor);
return; return;
} }
@@ -221,7 +219,6 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
StatusTextBlock.Text = _transientStatus ?? string.Empty; StatusTextBlock.Text = _transientStatus ?? string.Empty;
StatusTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, SecondaryColorCandidates, MinTextContrast); StatusTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, SecondaryColorCandidates, MinTextContrast);
ApplyHistoryTypographyLayout();
UpdateDialogVisual(snapshot, panelColor); UpdateDialogVisual(snapshot, panelColor);
} }
@@ -264,25 +261,15 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
{ {
Spacing = _isUltraCompactMode ? 0 : 2 Spacing = _isUltraCompactMode ? 0 : 2
}; };
var rowTitleTextBlock = new TextBlock textStack.Children.Add(new TextBlock
{ {
Text = entry.Label, Text = entry.Label,
FontSize = Math.Clamp(12 * (_isCompactMode ? 0.92 : 1.0), 10, 17),
FontWeight = FontWeight.SemiBold,
MaxLines = 1,
TextTrimming = TextTrimming.CharacterEllipsis,
Foreground = rowPrimaryBrush Foreground = rowPrimaryBrush
}; });
ApplyTextLayout(
rowTitleTextBlock,
ComponentTypographyLayoutService.FitAdaptiveTextLayout(
entry.Label,
ResolveHistoryContentWidth(),
_isUltraCompactMode ? 18 : 22,
1,
1,
10,
17,
new[] { FontWeight.SemiBold, FontWeight.Medium },
1.08d),
TextWrapping.NoWrap);
textStack.Children.Add(rowTitleTextBlock);
if (!_isUltraCompactMode) if (!_isUltraCompactMode)
{ {
@@ -294,25 +281,14 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
FormatDuration(entry.Duration), FormatDuration(entry.Duration),
entry.AverageScore); entry.AverageScore);
var metaTextBlock = new TextBlock textStack.Children.Add(new TextBlock
{ {
Text = metaText, Text = metaText,
FontSize = Math.Clamp(10.5 * (_isCompactMode ? 0.94 : 1.0), 9, 14),
MaxLines = 1,
TextTrimming = TextTrimming.CharacterEllipsis,
Foreground = rowSecondaryBrush Foreground = rowSecondaryBrush
}; });
ApplyTextLayout(
metaTextBlock,
ComponentTypographyLayoutService.FitAdaptiveTextLayout(
metaText,
ResolveHistoryContentWidth(),
_isUltraCompactMode ? 16 : 20,
1,
1,
9,
14,
new[] { FontWeight.Normal, FontWeight.Medium },
1.08d),
TextWrapping.NoWrap);
textStack.Children.Add(metaTextBlock);
} }
rowGrid.Children.Add(textStack); rowGrid.Children.Add(textStack);
@@ -590,8 +566,6 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
DialogConfirmButton.Content = L("study.session_history.dialog.delete_confirm", "Delete"); DialogConfirmButton.Content = L("study.session_history.dialog.delete_confirm", "Delete");
DialogCancelButton.Content = L("study.session_history.rename_cancel", "Cancel rename"); DialogCancelButton.Content = L("study.session_history.rename_cancel", "Cancel rename");
} }
ApplyHistoryTypographyLayout();
} }
private void SetTransientStatus(string status, double seconds = 2.2) private void SetTransientStatus(string status, double seconds = 2.2)
@@ -625,6 +599,8 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
? Math.Clamp(4 * scale, 2, 6) ? Math.Clamp(4 * scale, 2, 6)
: Math.Clamp(7 * scale, 4, 10); : Math.Clamp(7 * scale, 4, 10);
TitleTextBlock.FontSize = Math.Clamp(13 * scale, 10, 22);
StatusTextBlock.FontSize = Math.Clamp(11 * scale, 9, 18);
SessionListPanel.Spacing = _isUltraCompactMode SessionListPanel.Spacing = _isUltraCompactMode
? Math.Clamp(4 * scale, 2, 5) ? Math.Clamp(4 * scale, 2, 5)
: Math.Clamp(6 * scale, 3, 8); : Math.Clamp(6 * scale, 3, 8);
@@ -636,108 +612,13 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
DialogCardBorder.Padding = new Thickness( DialogCardBorder.Padding = new Thickness(
ComponentChromeCornerRadiusHelper.SafeValue(12 * scale, 9, 20), ComponentChromeCornerRadiusHelper.SafeValue(12 * scale, 9, 20),
ComponentChromeCornerRadiusHelper.SafeValue(11 * scale, 8, 18)); ComponentChromeCornerRadiusHelper.SafeValue(11 * scale, 8, 18));
DialogTitleTextBlock.FontSize = Math.Clamp(14 * scale, 11, 20);
DialogMessageTextBlock.FontSize = Math.Clamp(12 * scale, 10, 17);
DialogRenameTextBox.FontSize = Math.Clamp(11.5 * scale, 10, 16);
DialogCancelButton.FontSize = Math.Clamp(11 * scale, 10, 16); DialogCancelButton.FontSize = Math.Clamp(11 * scale, 10, 16);
DialogConfirmButton.FontSize = Math.Clamp(11 * scale, 10, 16); DialogConfirmButton.FontSize = Math.Clamp(11 * scale, 10, 16);
DialogCancelButton.Height = Math.Clamp(30 * scale, 26, 38); DialogCancelButton.Height = Math.Clamp(30 * scale, 26, 38);
DialogConfirmButton.Height = Math.Clamp(30 * scale, 26, 38); DialogConfirmButton.Height = Math.Clamp(30 * scale, 26, 38);
ApplyHistoryTypographyLayout();
}
private void ApplyHistoryTypographyLayout()
{
var contentWidth = ResolveHistoryContentWidth();
var titleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
TitleTextBlock.Text,
contentWidth,
_isUltraCompactMode ? 18 : 24,
1,
1,
10,
22,
new[] { FontWeight.SemiBold, FontWeight.Medium },
1.08d);
ApplyTextLayout(TitleTextBlock, titleLayout, TextWrapping.NoWrap);
var statusLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
StatusTextBlock.Text,
contentWidth,
_isUltraCompactMode ? 18 : 28,
1,
_isUltraCompactMode ? 1 : 2,
9,
18,
new[] { FontWeight.Normal, FontWeight.Medium },
1.10d);
ApplyTextLayout(StatusTextBlock, statusLayout, statusLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap);
if (!DialogOverlayBorder.IsVisible)
{
return;
}
var dialogWidth = DialogCardBorder.Bounds.Width > 1
? Math.Max(1, DialogCardBorder.Bounds.Width - DialogCardBorder.Padding.Left - DialogCardBorder.Padding.Right)
: Math.Max(180, contentWidth);
var dialogTitleLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
DialogTitleTextBlock.Text,
dialogWidth,
_isUltraCompactMode ? 18 : 24,
1,
1,
11,
20,
new[] { FontWeight.SemiBold, FontWeight.Medium },
1.08d);
ApplyTextLayout(DialogTitleTextBlock, dialogTitleLayout, TextWrapping.NoWrap);
var dialogMessageLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
DialogMessageTextBlock.Text,
dialogWidth,
_isUltraCompactMode ? 42 : 56,
1,
3,
10,
17,
new[] { FontWeight.Normal, FontWeight.Medium },
1.12d);
ApplyTextLayout(DialogMessageTextBlock, dialogMessageLayout, dialogMessageLayout.MaxLines > 1 ? TextWrapping.Wrap : TextWrapping.NoWrap);
var renameLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
DialogRenameTextBox.Text ?? DialogRenameTextBox.Watermark,
dialogWidth,
_isUltraCompactMode ? 30 : 36,
1,
1,
10,
16,
new[] { FontWeight.Normal },
1.08d);
DialogRenameTextBox.FontSize = renameLayout.FontSize;
DialogRenameTextBox.FontWeight = renameLayout.Weight;
}
private double ResolveHistoryContentWidth()
{
if (Bounds.Width <= 1)
{
return Math.Max(90, _currentCellSize * 4.5);
}
var reservedWidth = _isUltraCompactMode ? 126 : 150;
return Math.Max(90, Bounds.Width - reservedWidth);
}
private static void ApplyTextLayout(TextBlock textBlock, ComponentAdaptiveTextLayout layout, TextWrapping wrapping)
{
textBlock.FontSize = layout.FontSize;
textBlock.FontWeight = layout.Weight;
textBlock.LineHeight = layout.LineHeight;
textBlock.MaxLines = layout.MaxLines;
textBlock.TextWrapping = wrapping;
textBlock.TextTrimming = TextTrimming.CharacterEllipsis;
} }
private static StudySessionHistoryEntry? FindHistoryEntry(IReadOnlyList<StudySessionHistoryEntry> history, string? sessionId) private static StudySessionHistoryEntry? FindHistoryEntry(IReadOnlyList<StudySessionHistoryEntry> history, string? sessionId)

View File

@@ -7,7 +7,6 @@ using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
namespace LanMountainDesktop.Views.Components; namespace LanMountainDesktop.Views.Components;
@@ -123,7 +122,6 @@ public partial class TimerWidget : UserControl, IDesktopComponentWidget
MainNumberTextBlock.Text = current.ToString(CultureInfo.InvariantCulture); MainNumberTextBlock.Text = current.ToString(CultureInfo.InvariantCulture);
NextNumberTextBlock.Text = next.ToString(CultureInfo.InvariantCulture); NextNumberTextBlock.Text = next.ToString(CultureInfo.InvariantCulture);
NextNextNumberTextBlock.Text = nextNext.ToString(CultureInfo.InvariantCulture); NextNextNumberTextBlock.Text = nextNext.ToString(CultureInfo.InvariantCulture);
UpdateTypography();
} }
private void UpdateHandGeometry() private void UpdateHandGeometry()
@@ -208,7 +206,6 @@ public partial class TimerWidget : UserControl, IDesktopComponentWidget
PlayButtonBorder.Height = Math.Clamp(42 * scale, 28, 58); PlayButtonBorder.Height = Math.Clamp(42 * scale, 28, 58);
PlayButtonBorder.CornerRadius = new CornerRadius(PlayButtonBorder.Width / 2d); PlayButtonBorder.CornerRadius = new CornerRadius(PlayButtonBorder.Width / 2d);
UpdateTypography();
ApplyModeVisualIfNeeded(); ApplyModeVisualIfNeeded();
} }
@@ -220,51 +217,6 @@ public partial class TimerWidget : UserControl, IDesktopComponentWidget
return Math.Clamp(Math.Min(cellScale, Math.Min(heightScale, widthScale) * 1.05), 0.58, 1.95); return Math.Clamp(Math.Min(cellScale, Math.Min(heightScale, widthScale) * 1.05), 0.58, 1.95);
} }
private void UpdateTypography()
{
var panelWidth = TimerPanelBorder.Bounds.Width > 1 ? TimerPanelBorder.Bounds.Width : 224d;
var panelHeight = TimerPanelBorder.Bounds.Height > 1 ? TimerPanelBorder.Bounds.Height : 224d;
var leftColumnWidth = Math.Max(72, panelWidth * 0.38);
var leftColumnHeight = Math.Max(120, panelHeight - 36);
TopNumberTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
TopNumberTextBlock.Text,
leftColumnWidth,
Math.Max(20, leftColumnHeight * 0.20),
1,
18,
38,
FontWeight.SemiBold,
1.02d);
MainNumberTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
MainNumberTextBlock.Text,
leftColumnWidth,
Math.Max(28, leftColumnHeight * 0.30),
1,
28,
64,
FontWeight.Bold,
1.00d);
NextNumberTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
NextNumberTextBlock.Text,
leftColumnWidth,
Math.Max(18, leftColumnHeight * 0.16),
1,
14,
34,
FontWeight.Medium,
1.02d);
NextNextNumberTextBlock.FontSize = ComponentTypographyLayoutService.FitFontSize(
NextNextNumberTextBlock.Text,
leftColumnWidth,
Math.Max(16, leftColumnHeight * 0.12),
1,
12,
26,
FontWeight.Medium,
1.02d);
}
private bool ResolveIsNightMode() private bool ResolveIsNightMode()
{ {
if (ActualThemeVariant == ThemeVariant.Dark) if (ActualThemeVariant == ThemeVariant.Dark)

View File

@@ -10,7 +10,6 @@ using Avalonia.Controls.Shapes;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -219,36 +218,14 @@ public partial class WeatherClockWidget : UserControl, IDesktopComponentWidget,
} }
var leftWidthFactor = Math.Clamp(leftContentWidth / 122d, 0.48, 1.35); var leftWidthFactor = Math.Clamp(leftContentWidth / 122d, 0.48, 1.35);
var timeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( TimeTextBlock.FontSize = Math.Clamp((metrics.PrimaryTemperatureFont * 0.74) * scale * compactFactor * leftWidthFactor, 10, 62);
TimeTextBlock.Text, DateTextBlock.FontSize = Math.Clamp(metrics.SecondaryTextFont * scale * compactFactor * leftWidthFactor, 8, 30);
leftContentWidth,
Math.Max(12, contentHeight * 0.42),
1,
1,
10,
Math.Clamp((metrics.PrimaryTemperatureFont * 0.74) * scale * compactFactor * leftWidthFactor, 10, 62),
[ToVariableWeight(Lerp(620, 760, Math.Clamp((scale - 0.68) / 1.35, 0, 1)))],
1.04);
var dateLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
DateTextBlock.Text,
Math.Max(12, leftContentWidth),
Math.Max(10, contentHeight * 0.24),
1,
1,
8,
Math.Clamp(metrics.SecondaryTextFont * scale * compactFactor * leftWidthFactor, 8, 30),
[ToVariableWeight(Lerp(540, 680, Math.Clamp((scale - 0.68) / 1.35, 0, 1)))],
1.04);
TimeTextBlock.FontSize = timeLayout.FontSize;
DateTextBlock.FontSize = dateLayout.FontSize;
var weatherIconSize = Math.Clamp(metrics.IconFont * scale * compactFactor * leftWidthFactor, 9, 32); var weatherIconSize = Math.Clamp(metrics.IconFont * scale * compactFactor * leftWidthFactor, 9, 32);
WeatherIconImage.Width = weatherIconSize; WeatherIconImage.Width = weatherIconSize;
WeatherIconImage.Height = weatherIconSize; WeatherIconImage.Height = weatherIconSize;
TimeTextBlock.FontWeight = timeLayout.Weight; TimeTextBlock.FontWeight = ToVariableWeight(Lerp(620, 760, Math.Clamp((scale - 0.68) / 1.35, 0, 1)));
DateTextBlock.FontWeight = dateLayout.Weight; DateTextBlock.FontWeight = ToVariableWeight(Lerp(540, 680, Math.Clamp((scale - 0.68) / 1.35, 0, 1)));
TimeTextBlock.LineHeight = timeLayout.LineHeight;
DateTextBlock.LineHeight = dateLayout.LineHeight;
LeftStack.Width = leftContentWidth; LeftStack.Width = leftContentWidth;
LeftStack.MaxWidth = leftContentWidth; LeftStack.MaxWidth = leftContentWidth;

View File

@@ -11,7 +11,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Host.Abstractions; using LanMountainDesktop.Host.Abstractions;
using LanMountainDesktop.Models; using LanMountainDesktop.Models;
@@ -926,21 +925,12 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text) var temperatureSample = string.IsNullOrWhiteSpace(TemperatureTextBlock.Text)
? "00°" ? "00°"
: TemperatureTextBlock.Text.Trim(); : TemperatureTextBlock.Text.Trim();
var temperatureGlyphCount = Math.Clamp(ComponentTypographyLayoutService.CountTextDisplayUnits(temperatureSample), 3, 6); var temperatureGlyphCount = Math.Clamp(temperatureSample.Length, 3, 6);
var temperatureMaxWidth = Math.Max(34, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 2); var temperatureMaxWidth = Math.Max(34, innerWidth - iconSize - TopRowGrid.ColumnSpacing - 2);
var rawTemperatureSize = Math.Clamp(Lerp(94, 118, iconGrowth) * topScale, 22, 340); var rawTemperatureSize = Math.Clamp(Lerp(94, 118, iconGrowth) * topScale, 22, 340);
var temperatureLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( var fitTemperatureSize = temperatureMaxWidth / (temperatureGlyphCount * 0.62);
TemperatureTextBlock.Text, TemperatureTextBlock.FontSize = Math.Clamp(Math.Min(rawTemperatureSize, fitTemperatureSize), 10, 340);
temperatureMaxWidth, TemperatureTextBlock.FontWeight = ToVariableWeight(Lerp(300, 360, emphasis));
Math.Max(18, topZoneHeight * 0.84),
1,
1,
Math.Max(10, rawTemperatureSize * 0.42),
rawTemperatureSize,
[ToVariableWeight(Lerp(300, 360, emphasis))],
1.02);
TemperatureTextBlock.FontSize = temperatureLayout.FontSize;
TemperatureTextBlock.FontWeight = temperatureLayout.Weight;
TemperatureTextBlock.Margin = new Thickness(Math.Clamp(-1.4 * topScale, -6, 0), Math.Clamp(-7.6 * topScale, -16, -1), 0, 0); TemperatureTextBlock.Margin = new Thickness(Math.Clamp(-1.4 * topScale, -6, 0), Math.Clamp(-7.6 * topScale, -16, -1), 0, 0);
TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 34, Math.Max(34, innerWidth * 0.76)); TemperatureTextBlock.MaxWidth = Math.Clamp(temperatureMaxWidth, 34, Math.Max(34, innerWidth * 0.76));
@@ -971,76 +961,26 @@ public partial class WeatherWidget : UserControl, IDesktopComponentWidget, IDesk
} }
var infoFontWeight = ToVariableWeight(Lerp(580, 690, emphasis)); var infoFontWeight = ToVariableWeight(Lerp(580, 690, emphasis));
var conditionBadge = ComponentTypographyLayoutService.ResolveBadgeBox( ConditionTextBlock.FontSize = Math.Max(6, infoFontSize * 0.96);
bottomTextMaxWidth, ConditionTextBlock.FontWeight = infoFontWeight;
Math.Max(16, bottomZoneHeight * 0.34), ConditionTextBlock.LineHeight = ConditionTextBlock.FontSize * infoLineHeightFactor;
preferredSizeScale: 0.26d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
ConditionStack.Margin = new Thickness(
conditionBadge.Padding.Left,
conditionBadge.Padding.Top,
conditionBadge.Padding.Right,
conditionBadge.Padding.Bottom);
var conditionContentMaxWidth = Math.Max(24, bottomTextMaxWidth - conditionBadge.Padding.Left - conditionBadge.Padding.Right);
var conditionLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
ConditionTextBlock.Text,
conditionContentMaxWidth,
Math.Max(12, bottomZoneHeight * 0.30),
1,
1,
7,
Math.Max(6, infoFontSize * 0.96),
[infoFontWeight],
infoLineHeightFactor);
var rangeLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
RangeTextBlock.Text,
conditionContentMaxWidth,
Math.Max(12, bottomZoneHeight * 0.30),
1,
1,
7,
Math.Max(6, infoFontSize * 1.03),
[infoFontWeight],
infoLineHeightFactor);
ConditionTextBlock.FontSize = conditionLayout.FontSize;
ConditionTextBlock.FontWeight = conditionLayout.Weight;
ConditionTextBlock.LineHeight = conditionLayout.LineHeight;
ConditionTextBlock.MaxWidth = bottomTextMaxWidth; ConditionTextBlock.MaxWidth = bottomTextMaxWidth;
RangeTextBlock.FontSize = rangeLayout.FontSize; RangeTextBlock.FontSize = Math.Max(6, infoFontSize * 1.03);
RangeTextBlock.FontWeight = rangeLayout.Weight; RangeTextBlock.FontWeight = infoFontWeight;
RangeTextBlock.LineHeight = rangeLayout.LineHeight; RangeTextBlock.LineHeight = RangeTextBlock.FontSize * infoLineHeightFactor;
RangeTextBlock.MaxWidth = bottomTextMaxWidth; RangeTextBlock.MaxWidth = bottomTextMaxWidth;
var cityBadge = ComponentTypographyLayoutService.ResolveBadgeBox( CityInfoBadge.Padding = new Thickness(0);
bottomTextMaxWidth, CityInfoBadge.CornerRadius = new CornerRadius(0);
Math.Max(16, bottomZoneHeight * 0.38),
preferredSizeScale: 0.28d,
minSize: 10,
maxSize: 24,
insetScale: 0.18d);
CityInfoBadge.Padding = cityBadge.Padding;
CityInfoBadge.CornerRadius = new CornerRadius(cityBadge.Size / 2d);
CityInfoBadge.MaxWidth = bottomTextMaxWidth; CityInfoBadge.MaxWidth = bottomTextMaxWidth;
LocationIcon.FontSize = Math.Clamp( LocationIcon.FontSize = Math.Clamp(
12 * bottomScale, 12 * bottomScale,
6, 6,
34); 34);
LocationIcon.FontSize = Math.Min(LocationIcon.FontSize, infoFontSize * 0.72); LocationIcon.FontSize = Math.Min(LocationIcon.FontSize, infoFontSize * 0.72);
var cityLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout( CityTextBlock.FontSize = Math.Max(6, infoFontSize * 0.84);
CityTextBlock.Text, CityTextBlock.FontWeight = ToVariableWeight(Lerp(500, 620, emphasis));
Math.Max(24, bottomTextMaxWidth - cityBadge.Padding.Left - cityBadge.Padding.Right), CityTextBlock.LineHeight = CityTextBlock.FontSize * infoLineHeightFactor;
Math.Max(12, bottomZoneHeight * 0.36),
1,
1,
6,
Math.Max(6, infoFontSize * 0.84),
[ToVariableWeight(Lerp(500, 620, emphasis))],
infoLineHeightFactor);
CityTextBlock.FontSize = cityLayout.FontSize;
CityTextBlock.FontWeight = cityLayout.Weight;
CityTextBlock.LineHeight = cityLayout.LineHeight;
CityTextBlock.MaxWidth = bottomTextMaxWidth; CityTextBlock.MaxWidth = bottomTextMaxWidth;
} }

View File

@@ -8,7 +8,6 @@ using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using LanMountainDesktop.DesktopComponents.Runtime;
using LanMountainDesktop.ComponentSystem; using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Services; using LanMountainDesktop.Services;
@@ -102,7 +101,6 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
private TimeZoneService? _timeZoneService; private TimeZoneService? _timeZoneService;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private double _currentCellSize = BaseCellSize; private double _currentCellSize = BaseCellSize;
private double _layoutScale = 1d;
private DateTime _nextLanguageProbeUtc = DateTime.MinValue; private DateTime _nextLanguageProbeUtc = DateTime.MinValue;
private string _secondHandMode = ClockSecondHandMode.Tick; private string _secondHandMode = ClockSecondHandMode.Tick;
private bool _isNightVisual = true; private bool _isNightVisual = true;
@@ -165,16 +163,14 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
{ {
_currentCellSize = Math.Max(1, cellSize); _currentCellSize = Math.Max(1, cellSize);
var scale = ResolveScale(); var scale = ResolveScale();
var chromeScale = ComponentChromeCornerRadiusHelper.ResolveScale();
_layoutScale = Math.Clamp(scale * (0.9d + (chromeScale * 0.1d)), 0.58d, 2.0d);
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 horizontalPadding = Math.Clamp(10 * _layoutScale, 4, 26); var horizontalPadding = Math.Clamp(10 * scale, 4, 26);
var verticalPadding = Math.Clamp(8 * _layoutScale, 3, 22); var verticalPadding = Math.Clamp(8 * scale, 3, 22);
RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(horizontalPadding, verticalPadding, null, 0.55d); RootBorder.Padding = ComponentChromeCornerRadiusHelper.SafeThickness(horizontalPadding, verticalPadding, null, 0.55d);
RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(24 * _layoutScale, 10, 46); RootBorder.CornerRadius = ComponentChromeCornerRadiusHelper.Scale(24 * scale, 10, 46);
var usableWidth = Math.Max(48, totalWidth - horizontalPadding * 2); var usableWidth = Math.Max(48, totalWidth - horizontalPadding * 2);
var usableHeight = Math.Max(28, totalHeight - verticalPadding * 2); var usableHeight = Math.Max(28, totalHeight - verticalPadding * 2);
@@ -183,8 +179,11 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
ClockHostGrid.ColumnSpacing = columnSpacing; ClockHostGrid.ColumnSpacing = columnSpacing;
var widthPerClock = Math.Max(18, (usableWidth - columnSpacing * 3) / WorldClockTimeZoneCatalog.ClockCount); var widthPerClock = Math.Max(18, (usableWidth - columnSpacing * 3) / WorldClockTimeZoneCatalog.ClockCount);
var textSpacing = Math.Clamp(2.8 * _layoutScale, 1, 7); var secondaryFont = Math.Clamp(10.5 * scale * (widthPerClock / 46d), 7, 18);
var estimatedTextHeight = Math.Clamp(78 * _layoutScale, 42, 128); var cityFont = Math.Clamp(secondaryFont * 1.42, 9, 24);
var textSpacing = Math.Clamp(2.8 * scale, 1, 7);
var estimatedTextHeight = cityFont * 1.2 + secondaryFont * 2.35 + textSpacing * 3;
var dialSize = Math.Clamp(Math.Min(widthPerClock, usableHeight - estimatedTextHeight), 18, 108); var dialSize = Math.Clamp(Math.Min(widthPerClock, usableHeight - estimatedTextHeight), 18, 108);
if (dialSize < 18) if (dialSize < 18)
{ {
@@ -198,13 +197,15 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
entry.DialBorder.Height = dialSize; entry.DialBorder.Height = dialSize;
entry.DialBorder.CornerRadius = new CornerRadius(dialSize / 2d); entry.DialBorder.CornerRadius = new CornerRadius(dialSize / 2d);
entry.CityTextBlock.FontSize = cityFont;
entry.DayTextBlock.FontSize = secondaryFont;
entry.OffsetTextBlock.FontSize = secondaryFont;
var maxTextWidth = Math.Max(16, widthPerClock + 10); var maxTextWidth = Math.Max(16, widthPerClock + 10);
entry.CityTextBlock.MaxWidth = maxTextWidth; entry.CityTextBlock.MaxWidth = maxTextWidth;
entry.DayTextBlock.MaxWidth = maxTextWidth; entry.DayTextBlock.MaxWidth = maxTextWidth;
entry.OffsetTextBlock.MaxWidth = maxTextWidth; entry.OffsetTextBlock.MaxWidth = maxTextWidth;
} }
RefreshDialArtwork(_isNightVisual);
} }
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e) private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
@@ -475,7 +476,6 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
private static void BuildDialTicks(ClockEntryVisual entry, bool isNight) private static void BuildDialTicks(ClockEntryVisual entry, bool isNight)
{ {
entry.TickCanvas.Children.Clear(); entry.TickCanvas.Children.Clear();
var scale = Math.Clamp(entry.Host.Spacing / 3d, 0.78d, 1.22d);
var majorColor = isNight ? "#E3E7F2" : "#2D3341"; var majorColor = isNight ? "#E3E7F2" : "#2D3341";
var minorColor = isNight ? "#9EA7B8" : "#9AA4B3"; var minorColor = isNight ? "#9EA7B8" : "#9AA4B3";
@@ -483,8 +483,8 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
{ {
var isMajor = i % 5 == 0; var isMajor = i % 5 == 0;
var angle = (i * 6 - 90) * Math.PI / 180d; var angle = (i * 6 - 90) * Math.PI / 180d;
var outerRadius = DialCenter - (6.5 * scale); var outerRadius = DialCenter - 6.5;
var innerRadius = outerRadius - (isMajor ? 9 * scale : 4.5 * scale); var innerRadius = outerRadius - (isMajor ? 9 : 4.5);
var x1 = DialCenter + Math.Cos(angle) * innerRadius; var x1 = DialCenter + Math.Cos(angle) * innerRadius;
var y1 = DialCenter + Math.Sin(angle) * innerRadius; var y1 = DialCenter + Math.Sin(angle) * innerRadius;
@@ -496,7 +496,7 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
StartPoint = new Point(x1, y1), StartPoint = new Point(x1, y1),
EndPoint = new Point(x2, y2), EndPoint = new Point(x2, y2),
Stroke = CreateBrush(isMajor ? majorColor : minorColor), Stroke = CreateBrush(isMajor ? majorColor : minorColor),
StrokeThickness = (isMajor ? 1.9 : 0.8) * scale, StrokeThickness = isMajor ? 1.9 : 0.8,
StrokeLineCap = PenLineCap.Round StrokeLineCap = PenLineCap.Round
}); });
} }
@@ -505,9 +505,8 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
private static void BuildDialNumbers(ClockEntryVisual entry, bool isNight) private static void BuildDialNumbers(ClockEntryVisual entry, bool isNight)
{ {
entry.NumberCanvas.Children.Clear(); entry.NumberCanvas.Children.Clear();
var scale = Math.Clamp(entry.Host.Spacing / 3d, 0.78d, 1.22d);
var numberColor = isNight ? "#F2F5FB" : "#1B202A"; var numberColor = isNight ? "#F2F5FB" : "#1B202A";
var radius = 36 * scale; var radius = 36;
for (var number = 1; number <= 12; number++) for (var number = 1; number <= 12; number++)
{ {
var angle = (number * 30 - 90) * Math.PI / 180d; var angle = (number * 30 - 90) * Math.PI / 180d;
@@ -515,37 +514,22 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
var y = DialCenter + Math.Sin(angle) * radius; var y = DialCenter + Math.Sin(angle) * radius;
var text = number.ToString(CultureInfo.InvariantCulture); var text = number.ToString(CultureInfo.InvariantCulture);
var isDoubleDigit = number >= 10; var isDoubleDigit = number >= 10;
var glyphBox = ComponentTypographyLayoutService.ResolveGlyphBox( var width = isDoubleDigit ? 14 : 10;
isDoubleDigit ? 16 * scale : 12 * scale, var height = 12;
12 * scale,
preferredSizeScale: 0.98d,
minSize: 8,
maxSize: 16,
insetScale: 0d);
var fontSize = ComponentTypographyLayoutService.FitFontSize(
text,
glyphBox.Width,
glyphBox.Height,
maxLines: 1,
minFontSize: 7 * scale,
maxFontSize: 11 * scale,
weight: FontWeight.SemiBold,
lineHeightFactor: 1d,
fontFamily: MiSansFontFamily);
var numberText = new TextBlock var numberText = new TextBlock
{ {
Text = text, Text = text,
Width = glyphBox.Width, Width = width,
Height = glyphBox.Height, Height = height,
FontFamily = MiSansFontFamily, FontFamily = MiSansFontFamily,
FontSize = fontSize, FontSize = 9,
FontWeight = FontWeight.SemiBold, FontWeight = FontWeight.SemiBold,
Foreground = CreateBrush(numberColor), Foreground = CreateBrush(numberColor),
TextAlignment = TextAlignment.Center TextAlignment = TextAlignment.Center
}; };
Canvas.SetLeft(numberText, x - glyphBox.Width / 2d); Canvas.SetLeft(numberText, x - width / 2d);
Canvas.SetTop(numberText, y - glyphBox.Height / 2d); Canvas.SetTop(numberText, y - height / 2d);
entry.NumberCanvas.Children.Add(numberText); entry.NumberCanvas.Children.Add(numberText);
} }
} }
@@ -600,69 +584,18 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
var minuteAngle = minuteValue * 6d; var minuteAngle = minuteValue * 6d;
var secondAngle = secondValue * 6d; var secondAngle = secondValue * 6d;
SetHandGeometry(entry.HourHand, hourAngle, forwardLength: 24 * _layoutScale, backwardLength: 4.8 * _layoutScale); SetHandGeometry(entry.HourHand, hourAngle, forwardLength: 24, backwardLength: 4.8);
SetHandGeometry(entry.MinuteHand, minuteAngle, forwardLength: 33 * _layoutScale, backwardLength: 6 * _layoutScale); SetHandGeometry(entry.MinuteHand, minuteAngle, forwardLength: 33, backwardLength: 6);
SetHandGeometry(entry.SecondHand, secondAngle, forwardLength: 37 * _layoutScale, backwardLength: 8.5 * _layoutScale); SetHandGeometry(entry.SecondHand, secondAngle, forwardLength: 37, backwardLength: 8.5);
entry.CityTextBlock.Text = ResolveCityName(zone); entry.CityTextBlock.Text = ResolveCityName(zone);
entry.DayTextBlock.Text = ResolveRelativeDayLabel((zonedNow.Date - baseNow.Date).Days); entry.DayTextBlock.Text = ResolveRelativeDayLabel((zonedNow.Date - baseNow.Date).Days);
var offsetDelta = zone.GetUtcOffset(utcNow) - baseOffset; var offsetDelta = zone.GetUtcOffset(utcNow) - baseOffset;
entry.OffsetTextBlock.Text = ResolveOffsetLabel(offsetDelta); entry.OffsetTextBlock.Text = ResolveOffsetLabel(offsetDelta);
ApplyEntryTypography(entry);
} }
} }
private void ApplyEntryTypography(ClockEntryVisual entry)
{
var hostWidth = entry.Host.Bounds.Width > 1 ? entry.Host.Bounds.Width : Math.Max(18, _currentCellSize * 0.74);
var hostHeight = entry.Host.Bounds.Height > 1 ? entry.Host.Bounds.Height : Math.Max(18, _currentCellSize * 1.7);
var textWidth = Math.Max(16, hostWidth);
var cityHeight = Math.Clamp(hostHeight * 0.18, 12, 28);
var secondaryHeight = Math.Clamp(hostHeight * 0.14, 10, 22);
var cityLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
entry.CityTextBlock.Text,
textWidth,
cityHeight,
minLines: 1,
maxLines: 1,
minFontSize: 9,
maxFontSize: 24,
weightCandidates: new[] { FontWeight.SemiBold, FontWeight.Medium },
lineHeightFactor: 1d,
fontFamily: MiSansFontFamily);
var dayLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
entry.DayTextBlock.Text,
textWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 8,
maxFontSize: 18,
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 1d,
fontFamily: MiSansFontFamily);
var offsetLayout = ComponentTypographyLayoutService.FitAdaptiveTextLayout(
entry.OffsetTextBlock.Text,
textWidth,
secondaryHeight,
minLines: 1,
maxLines: 1,
minFontSize: 8,
maxFontSize: 18,
weightCandidates: new[] { FontWeight.Medium, FontWeight.Normal },
lineHeightFactor: 1d,
fontFamily: MiSansFontFamily);
entry.CityTextBlock.FontSize = cityLayout.FontSize;
entry.CityTextBlock.FontWeight = cityLayout.Weight;
entry.DayTextBlock.FontSize = dayLayout.FontSize;
entry.DayTextBlock.FontWeight = dayLayout.Weight;
entry.OffsetTextBlock.FontSize = offsetLayout.FontSize;
entry.OffsetTextBlock.FontWeight = offsetLayout.Weight;
}
private static void ApplyDialTheme(ClockEntryVisual entry, bool isNight) private static void ApplyDialTheme(ClockEntryVisual entry, bool isNight)
{ {
if (entry.IsNightApplied.HasValue && entry.IsNightApplied.Value == isNight) if (entry.IsNightApplied.HasValue && entry.IsNightApplied.Value == isNight)
@@ -682,21 +615,6 @@ public partial class WorldClockWidget : UserControl, IDesktopComponentWidget, IT
BuildDialNumbers(entry, isNight); BuildDialNumbers(entry, isNight);
} }
private void RefreshDialArtwork(bool isNight)
{
for (var index = 0; index < _entryVisuals.Length; index++)
{
var entry = _entryVisuals[index];
if (entry is null)
{
continue;
}
BuildDialTicks(entry, isNight);
BuildDialNumbers(entry, isNight);
}
}
private void ProbeLanguageCodeIfNeeded(DateTime utcNow) private void ProbeLanguageCodeIfNeeded(DateTime utcNow)
{ {
if (utcNow < _nextLanguageProbeUtc) if (utcNow < _nextLanguageProbeUtc)