From 2a41b8c016719adb7c50f7530b7ac428e49859d9 Mon Sep 17 00:00:00 2001 From: lincube Date: Wed, 4 Mar 2026 13:04:54 +0800 Subject: [PATCH] 0.3.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 噪音监测组件优化 --- .../Components/StudyEnvironmentWidget.axaml | 18 ++- .../StudyEnvironmentWidget.axaml.cs | 27 +++- .../Components/StudyNoiseCurveWidget.axaml.cs | 129 +++++++++++------- 3 files changed, 121 insertions(+), 53 deletions(-) diff --git a/LanMontainDesktop/Views/Components/StudyEnvironmentWidget.axaml b/LanMontainDesktop/Views/Components/StudyEnvironmentWidget.axaml index 0191ee3..42f9f8c 100644 --- a/LanMontainDesktop/Views/Components/StudyEnvironmentWidget.axaml +++ b/LanMontainDesktop/Views/Components/StudyEnvironmentWidget.axaml @@ -12,18 +12,24 @@ BorderThickness="1" CornerRadius="18" Padding="14,10"> - - @@ -35,12 +41,16 @@ Text="--" FontSize="22" FontWeight="SemiBold" + MaxLines="1" + TextTrimming="CharacterEllipsis" TextAlignment="Right" HorizontalAlignment="Right" Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" /> 0 && width < (showingDualNoiseLines ? 230 : 200)) || + (height > 0 && height < (showingDualNoiseLines ? 102 : 82)); + var hideStatusLabel = collapseByCell || collapseByBounds; + + StatusTitleTextBlock.IsVisible = !hideStatusLabel; + LeftStatusStack.Spacing = hideStatusLabel ? 0 : Math.Clamp(2 * scale, 1, 4); + LayoutGrid.ColumnSpacing = hideStatusLabel + ? Math.Clamp(6 * scale, 4, 10) + : Math.Clamp(10 * scale, 7, 14); } private string ResolveStatusText(StudyAnalyticsSnapshot snapshot) @@ -239,7 +264,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg private IBrush TryResolveThemeBrush(string resourceKey, string fallbackHex) { - if (TryGetResource(resourceKey, null, out var resource) && resource is IBrush brush) + if (Resources.TryGetResource(resourceKey, ActualThemeVariant, out var resource) && resource is IBrush brush) { return brush; } diff --git a/LanMontainDesktop/Views/Components/StudyNoiseCurveWidget.axaml.cs b/LanMontainDesktop/Views/Components/StudyNoiseCurveWidget.axaml.cs index ba1d18f..96741bb 100644 --- a/LanMontainDesktop/Views/Components/StudyNoiseCurveWidget.axaml.cs +++ b/LanMontainDesktop/Views/Components/StudyNoiseCurveWidget.axaml.cs @@ -14,15 +14,39 @@ namespace LanMontainDesktop.Views.Components; public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget { private const double NormalTextMinContrast = 4.5; - private const double LargeTextMinContrast = 3.0; + private const double LargeTextMinContrast = 4.5; - private static readonly Color[] LightToneCandidates = + // Prefer cool-toned colors first (not plain white), then dark variants when background is bright. + private static readonly Color[] ValueToneCandidates = { + Color.Parse("#FFEAF5FF"), + Color.Parse("#FFDCEEFF"), + Color.Parse("#FFCEE6FA"), + Color.Parse("#FF1A2D42"), + Color.Parse("#FF233A54"), Color.Parse("#FFFFFFFF"), - Color.Parse("#FFF8FCFF"), - Color.Parse("#FFF0F7FF"), - Color.Parse("#FFE8F3FF"), - Color.Parse("#FFE0EEFF") + Color.Parse("#FF101C2A") + }; + + private static readonly Color[] AxisToneCandidates = + { + Color.Parse("#FFC7D9EC"), + Color.Parse("#FFBAD0E8"), + Color.Parse("#FFD9E8F6"), + Color.Parse("#FF2C445F"), + Color.Parse("#FF35516F"), + Color.Parse("#FFEAF3FA"), + Color.Parse("#FF1A2C40") + }; + + private static readonly Color[] StatusTextToneCandidates = + { + Color.Parse("#FFF5FAFF"), + Color.Parse("#FFE6F1FB"), + Color.Parse("#FF18283A"), + Color.Parse("#FF122032"), + Color.Parse("#FFFFFFFF"), + Color.Parse("#FF111B29") }; private static readonly Color DarkSubstrate = Color.Parse("#FF0B1220"); @@ -66,8 +90,10 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge ReloadLanguageCode(); ApplyCellSize(_currentCellSize); ApplyDefaultXAxisLabels(); - ApplyTypographyByBackground(ResolvePanelBackgroundColor()); - ApplyStatusBadgeStyle(StatusVisualKind.Default, ResolvePanelBackgroundColor()); + + var panelColor = ResolvePanelBackgroundColor(); + ApplyTypographyByBackground(panelColor); + ApplyStatusBadgeStyle(StatusVisualKind.Default, panelColor); } public void ApplyCellSize(double cellSize) @@ -226,18 +252,18 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge private void ApplyTypographyByBackground(Color panelColor) { var samples = BuildPanelBackgroundSamples(panelColor); - var primaryBrush = CreateAdaptiveLightBrush(samples, LargeTextMinContrast, preferredAlpha: 0xF6); - var secondaryBrush = CreateAdaptiveLightBrush(samples, NormalTextMinContrast, preferredAlpha: 0xDF); + var valueBrush = CreateAdaptiveBrush(samples, ValueToneCandidates, LargeTextMinContrast); + var axisBrush = CreateAdaptiveBrush(samples, AxisToneCandidates, NormalTextMinContrast); - RealtimeValueTextBlock.Foreground = primaryBrush; - YTopTextBlock.Foreground = secondaryBrush; - YUpperTextBlock.Foreground = secondaryBrush; - YMiddleTextBlock.Foreground = secondaryBrush; - YLowerTextBlock.Foreground = secondaryBrush; - YBottomTextBlock.Foreground = secondaryBrush; - XLeftTextBlock.Foreground = secondaryBrush; - XCenterTextBlock.Foreground = secondaryBrush; - XRightTextBlock.Foreground = secondaryBrush; + RealtimeValueTextBlock.Foreground = valueBrush; + YTopTextBlock.Foreground = axisBrush; + YUpperTextBlock.Foreground = axisBrush; + YMiddleTextBlock.Foreground = axisBrush; + YLowerTextBlock.Foreground = axisBrush; + YBottomTextBlock.Foreground = axisBrush; + XLeftTextBlock.Foreground = axisBrush; + XCenterTextBlock.Foreground = axisBrush; + XRightTextBlock.Foreground = axisBrush; } private void ApplyStatusBadgeStyle(StatusVisualKind kind, Color panelColor) @@ -262,7 +288,7 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge StatusBadgeBorder.Background = new SolidColorBrush(badgeColor); StatusBadgeBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(0x96, 0xFF, 0xFF, 0xFF)); - StatusTextBlock.Foreground = CreateAdaptiveLightBrush(new[] { badgeComposite }, NormalTextMinContrast, preferredAlpha: 0xFF); + StatusTextBlock.Foreground = CreateAdaptiveBrush(new[] { badgeComposite }, StatusTextToneCandidates, NormalTextMinContrast); } private static StatusVisualKind ResolveStatusVisualKind(StudyAnalyticsSnapshot snapshot) @@ -294,7 +320,7 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge return solidBackground.Color; } - if (TryGetResource("AdaptiveGlassStrongBackgroundBrush", null, out var resource) && + if (Resources.TryGetResource("AdaptiveGlassStrongBackgroundBrush", ActualThemeVariant, out var resource) && resource is ISolidColorBrush solidBrush) { return solidBrush.Color; @@ -319,33 +345,40 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge }; } - private static SolidColorBrush CreateAdaptiveLightBrush( + private static SolidColorBrush CreateAdaptiveBrush( IReadOnlyList backgroundSamples, - double minContrast, - byte preferredAlpha) + IReadOnlyList colorCandidates, + double minContrast) { - var alphaCandidates = new byte[] + if (colorCandidates.Count == 0) { - preferredAlpha, - (byte)Math.Clamp(preferredAlpha + 20, 0, 255), - 0xFF - }; + return new SolidColorBrush(Color.Parse("#FFFFFFFF")); + } - for (var alphaIndex = 0; alphaIndex < alphaCandidates.Length; alphaIndex++) + for (var i = 0; i < colorCandidates.Count; i++) { - var alpha = alphaCandidates[alphaIndex]; - for (var toneIndex = 0; toneIndex < LightToneCandidates.Length; toneIndex++) + var candidate = colorCandidates[i]; + if (MinContrastRatio(candidate, backgroundSamples) >= minContrast) { - var tone = LightToneCandidates[toneIndex]; - var candidate = Color.FromArgb(alpha, tone.R, tone.G, tone.B); - if (MinContrastRatio(candidate, backgroundSamples) >= minContrast) - { - return new SolidColorBrush(candidate); - } + return new SolidColorBrush(candidate); } } - return new SolidColorBrush(Color.Parse("#FFFFFFFF")); + // If none reaches the target, pick the highest-contrast candidate. + var best = colorCandidates[0]; + var bestContrast = MinContrastRatio(best, backgroundSamples); + for (var i = 1; i < colorCandidates.Count; i++) + { + var candidate = colorCandidates[i]; + var contrast = MinContrastRatio(candidate, backgroundSamples); + if (contrast > bestContrast) + { + best = candidate; + bestContrast = contrast; + } + } + + return new SolidColorBrush(best); } private static double MinContrastRatio(Color foreground, IReadOnlyList backgrounds) @@ -423,49 +456,49 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge var centerSeconds = Math.Round(duration / 2d, MidpointRounding.AwayFromZero); XLeftTextBlock.Text = $"-{leftSeconds:0}s"; XCenterTextBlock.Text = $"-{centerSeconds:0}s"; - XRightTextBlock.Text = L("study.noise_curve.axis.now", "现在"); + XRightTextBlock.Text = L("study.noise_curve.axis.now", "Now"); } private void ApplyDefaultXAxisLabels() { XLeftTextBlock.Text = "-12s"; XCenterTextBlock.Text = "-6s"; - XRightTextBlock.Text = L("study.noise_curve.axis.now", "现在"); + XRightTextBlock.Text = L("study.noise_curve.axis.now", "Now"); } private string ResolveStatusText(StudyAnalyticsSnapshot snapshot) { if (snapshot.State == StudyAnalyticsRuntimeState.Unsupported) { - return L("study.environment.status.unsupported", "不支持"); + return L("study.environment.status.unsupported", "Unsupported"); } if (snapshot.State == StudyAnalyticsRuntimeState.Error || snapshot.StreamStatus == NoiseStreamStatus.Error) { - return L("study.environment.status.error", "错误"); + return L("study.environment.status.error", "Error"); } if (snapshot.State == StudyAnalyticsRuntimeState.Paused) { - return L("study.environment.status.paused", "已暂停"); + return L("study.environment.status.paused", "Paused"); } if (snapshot.StreamStatus == NoiseStreamStatus.Noisy) { - return L("study.environment.status.noisy", "嘈杂"); + return L("study.environment.status.noisy", "Noisy"); } if (snapshot.State == StudyAnalyticsRuntimeState.Running && snapshot.StreamStatus == NoiseStreamStatus.Quiet) { - return L("study.environment.status.quiet", "安静"); + return L("study.environment.status.quiet", "Quiet"); } if (snapshot.State == StudyAnalyticsRuntimeState.Ready) { - return L("study.environment.status.ready", "待机"); + return L("study.environment.status.ready", "Ready"); } - return L("study.environment.status.initializing", "初始化中"); + return L("study.environment.status.initializing", "Initializing"); } private void ReloadLanguageCode()