mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-21 16:14:28 +08:00
0.3.5
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using LanMountainDesktop.Models;
|
||||
using LanMountainDesktop.Services;
|
||||
|
||||
namespace LanMountainDesktop.Views.Components;
|
||||
|
||||
public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidget, IDesktopPageVisibilityAwareComponentWidget
|
||||
{
|
||||
private readonly IStudyAnalyticsService _studyAnalyticsService = StudyAnalyticsServiceFactory.CreateDefault();
|
||||
private readonly AppSettingsService _settingsService = new();
|
||||
private readonly LocalizationService _localizationService = new();
|
||||
private readonly DispatcherTimer _uiTimer = new()
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(250)
|
||||
};
|
||||
|
||||
private double _currentCellSize = 48;
|
||||
private bool _showDisplayDb = true;
|
||||
private bool _showDbfs;
|
||||
private string _languageCode = "zh-CN";
|
||||
private bool _isAttached;
|
||||
private bool _isOnActivePage = true;
|
||||
|
||||
public StudyEnvironmentWidget()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_uiTimer.Tick += OnUiTimerTick;
|
||||
AttachedToVisualTree += OnAttachedToVisualTree;
|
||||
DetachedFromVisualTree += OnDetachedFromVisualTree;
|
||||
SizeChanged += OnSizeChanged;
|
||||
|
||||
ReloadDisplaySettings();
|
||||
ApplyCellSize(_currentCellSize);
|
||||
RefreshVisual();
|
||||
}
|
||||
|
||||
public void ApplyCellSize(double cellSize)
|
||||
{
|
||||
_currentCellSize = Math.Max(1, cellSize);
|
||||
var scale = Math.Clamp(_currentCellSize / 48d, 0.82, 2.2);
|
||||
|
||||
RootBorder.CornerRadius = new CornerRadius(Math.Clamp(_currentCellSize * 0.34, 10, 28));
|
||||
RootBorder.Padding = new Thickness(
|
||||
Math.Clamp(14 * scale, 8, 20),
|
||||
Math.Clamp(10 * scale, 6, 16));
|
||||
|
||||
StatusTitleTextBlock.FontSize = Math.Clamp(11 * scale, 9, 18);
|
||||
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)
|
||||
{
|
||||
_ = isEditMode;
|
||||
_isOnActivePage = isOnActivePage;
|
||||
UpdateTimerState();
|
||||
}
|
||||
|
||||
public void RefreshFromSettings()
|
||||
{
|
||||
ReloadDisplaySettings();
|
||||
RefreshVisual();
|
||||
}
|
||||
|
||||
private void OnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_isAttached = true;
|
||||
ReloadDisplaySettings();
|
||||
_ = _studyAnalyticsService.StartOrResumeMonitoring();
|
||||
UpdateTimerState();
|
||||
RefreshVisual();
|
||||
}
|
||||
|
||||
private void OnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
_isAttached = false;
|
||||
_uiTimer.Stop();
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ApplyCellSize(_currentCellSize);
|
||||
UpdateAdaptiveLayout();
|
||||
}
|
||||
|
||||
private void OnUiTimerTick(object? sender, EventArgs e)
|
||||
{
|
||||
RefreshVisual();
|
||||
}
|
||||
|
||||
private void UpdateTimerState()
|
||||
{
|
||||
if (_isAttached && _isOnActivePage)
|
||||
{
|
||||
_uiTimer.Start();
|
||||
return;
|
||||
}
|
||||
|
||||
_uiTimer.Stop();
|
||||
}
|
||||
|
||||
private void ReloadDisplaySettings()
|
||||
{
|
||||
var snapshot = _settingsService.Load();
|
||||
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
|
||||
_showDisplayDb = snapshot.StudyEnvironmentShowDisplayDb;
|
||||
_showDbfs = snapshot.StudyEnvironmentShowDbfs;
|
||||
if (!_showDisplayDb && !_showDbfs)
|
||||
{
|
||||
_showDisplayDb = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshVisual()
|
||||
{
|
||||
var snapshot = _studyAnalyticsService.GetSnapshot();
|
||||
|
||||
StatusTitleTextBlock.Text = L("study.environment.status_label", "Environment");
|
||||
StatusValueTextBlock.Text = ResolveStatusText(snapshot);
|
||||
StatusValueTextBlock.Foreground = ResolveStatusBrush(snapshot);
|
||||
|
||||
if (snapshot.LatestRealtimePoint is not { } realtimePoint)
|
||||
{
|
||||
NoiseValueTextBlock.Text = L("study.environment.value.unavailable", "--");
|
||||
NoiseSubValueTextBlock.IsVisible = false;
|
||||
UpdateAdaptiveLayout();
|
||||
return;
|
||||
}
|
||||
|
||||
var showDisplay = _showDisplayDb;
|
||||
var showDbfs = _showDbfs;
|
||||
if (!showDisplay && !showDbfs)
|
||||
{
|
||||
showDisplay = true;
|
||||
}
|
||||
|
||||
if (showDisplay && showDbfs)
|
||||
{
|
||||
NoiseValueTextBlock.Text = FormatDisplayDb(realtimePoint.DisplayDb);
|
||||
NoiseSubValueTextBlock.Text = FormatDbfs(realtimePoint.Dbfs);
|
||||
NoiseSubValueTextBlock.IsVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
NoiseValueTextBlock.Text = showDisplay
|
||||
? 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)
|
||||
{
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Unsupported)
|
||||
{
|
||||
return L("study.environment.status.unsupported", "Unsupported");
|
||||
}
|
||||
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Error || snapshot.StreamStatus == NoiseStreamStatus.Error)
|
||||
{
|
||||
return L("study.environment.status.error", "Error");
|
||||
}
|
||||
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Paused)
|
||||
{
|
||||
return L("study.environment.status.paused", "Paused");
|
||||
}
|
||||
|
||||
if (snapshot.StreamStatus == NoiseStreamStatus.Noisy)
|
||||
{
|
||||
return L("study.environment.status.noisy", "Noisy");
|
||||
}
|
||||
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Running && snapshot.StreamStatus == NoiseStreamStatus.Quiet)
|
||||
{
|
||||
return L("study.environment.status.quiet", "Quiet");
|
||||
}
|
||||
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Ready)
|
||||
{
|
||||
return L("study.environment.status.ready", "Ready");
|
||||
}
|
||||
|
||||
return L("study.environment.status.initializing", "Initializing");
|
||||
}
|
||||
|
||||
private IBrush ResolveStatusBrush(StudyAnalyticsSnapshot snapshot)
|
||||
{
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Unsupported ||
|
||||
snapshot.State == StudyAnalyticsRuntimeState.Error ||
|
||||
snapshot.StreamStatus == NoiseStreamStatus.Error)
|
||||
{
|
||||
return CreateBrush("#FFFF7B7B");
|
||||
}
|
||||
|
||||
if (snapshot.StreamStatus == NoiseStreamStatus.Noisy)
|
||||
{
|
||||
return CreateBrush("#FFFFB14A");
|
||||
}
|
||||
|
||||
if (snapshot.State == StudyAnalyticsRuntimeState.Running &&
|
||||
snapshot.StreamStatus == NoiseStreamStatus.Quiet)
|
||||
{
|
||||
return CreateBrush("#FF6FD7A2");
|
||||
}
|
||||
|
||||
return TryResolveThemeBrush("AdaptiveTextPrimaryBrush", "#FFEFF3FF");
|
||||
}
|
||||
|
||||
private string FormatDisplayDb(double value)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
L("study.environment.value.display_format", "{0:F1} dB"),
|
||||
value);
|
||||
}
|
||||
|
||||
private string FormatDbfs(double value)
|
||||
{
|
||||
return string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
L("study.environment.value.dbfs_format", "{0:F1} dBFS"),
|
||||
value);
|
||||
}
|
||||
|
||||
private string L(string key, string fallback)
|
||||
{
|
||||
return _localizationService.GetString(_languageCode, key, fallback);
|
||||
}
|
||||
|
||||
private static SolidColorBrush CreateBrush(string hexColor)
|
||||
{
|
||||
return new SolidColorBrush(Color.Parse(hexColor));
|
||||
}
|
||||
|
||||
private IBrush TryResolveThemeBrush(string resourceKey, string fallbackHex)
|
||||
{
|
||||
if (Resources.TryGetResource(resourceKey, ActualThemeVariant, out var resource) && resource is IBrush brush)
|
||||
{
|
||||
return brush;
|
||||
}
|
||||
|
||||
return CreateBrush(fallbackHex);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user