Files
LanMountainDesktop/LanMontainDesktop/Views/Components/StudyEnvironmentWidget.axaml.cs

250 lines
7.6 KiB
C#
Raw Normal View History

2026-03-04 11:28:59 +08:00
using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using LanMontainDesktop.Models;
using LanMontainDesktop.Services;
namespace LanMontainDesktop.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);
}
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);
}
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;
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;
}
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 (TryGetResource(resourceKey, null, out var resource) && resource is IBrush brush)
{
return brush;
}
return CreateBrush(fallbackHex);
}
}