mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-21 16:14:28 +08:00
0.3.3
噪音监测组件优化
This commit is contained in:
@@ -12,18 +12,24 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="18"
|
||||
Padding="14,10">
|
||||
<Grid ColumnDefinitions="*,Auto"
|
||||
<Grid x:Name="LayoutGrid"
|
||||
ColumnDefinitions="*,Auto"
|
||||
ColumnSpacing="10">
|
||||
<StackPanel Spacing="2"
|
||||
<StackPanel x:Name="LeftStatusStack"
|
||||
Spacing="2"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock x:Name="StatusTitleTextBlock"
|
||||
Text="环境状态"
|
||||
Text="Environment"
|
||||
FontSize="11"
|
||||
MaxLines="1"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}" />
|
||||
<TextBlock x:Name="StatusValueTextBlock"
|
||||
Text="安静"
|
||||
Text="Quiet"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
MaxLines="1"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
</StackPanel>
|
||||
|
||||
@@ -35,12 +41,16 @@
|
||||
Text="--"
|
||||
FontSize="22"
|
||||
FontWeight="SemiBold"
|
||||
MaxLines="1"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextAlignment="Right"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}" />
|
||||
<TextBlock x:Name="NoiseSubValueTextBlock"
|
||||
Text=""
|
||||
FontSize="12"
|
||||
MaxLines="1"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextAlignment="Right"
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{DynamicResource AdaptiveTextSecondaryBrush}"
|
||||
|
||||
@@ -54,6 +54,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
|
||||
StatusValueTextBlock.FontSize = Math.Clamp(20 * scale, 12, 34);
|
||||
NoiseValueTextBlock.FontSize = Math.Clamp(22 * scale, 12, 38);
|
||||
NoiseSubValueTextBlock.FontSize = Math.Clamp(12 * scale, 9, 18);
|
||||
UpdateAdaptiveLayout();
|
||||
}
|
||||
|
||||
public void SetDesktopPageContext(bool isOnActivePage, bool isEditMode)
|
||||
@@ -87,6 +88,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
|
||||
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ApplyCellSize(_currentCellSize);
|
||||
UpdateAdaptiveLayout();
|
||||
}
|
||||
|
||||
private void OnUiTimerTick(object? sender, EventArgs e)
|
||||
@@ -129,6 +131,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
|
||||
{
|
||||
NoiseValueTextBlock.Text = L("study.environment.value.unavailable", "--");
|
||||
NoiseSubValueTextBlock.IsVisible = false;
|
||||
UpdateAdaptiveLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -151,6 +154,28 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
|
||||
? FormatDisplayDb(realtimePoint.DisplayDb)
|
||||
: FormatDbfs(realtimePoint.Dbfs);
|
||||
NoiseSubValueTextBlock.IsVisible = false;
|
||||
UpdateAdaptiveLayout();
|
||||
}
|
||||
|
||||
private void UpdateAdaptiveLayout()
|
||||
{
|
||||
var scale = Math.Clamp(_currentCellSize / 48d, 0.82, 2.2);
|
||||
var width = Bounds.Width;
|
||||
var height = Bounds.Height;
|
||||
var showingDualNoiseLines = _showDisplayDb && _showDbfs;
|
||||
|
||||
// Collapse the "Environment" label when space is tight so core values remain readable.
|
||||
var collapseByCell = _currentCellSize <= 40;
|
||||
var collapseByBounds =
|
||||
(width > 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;
|
||||
}
|
||||
|
||||
@@ -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<Color> backgroundSamples,
|
||||
double minContrast,
|
||||
byte preferredAlpha)
|
||||
IReadOnlyList<Color> 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<Color> 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()
|
||||
|
||||
Reference in New Issue
Block a user