自习功能可关闭
This commit is contained in:
lincube
2026-03-31 01:16:53 +08:00
parent 2dc729c9db
commit 9045624105
13 changed files with 215 additions and 10 deletions

View File

@@ -205,6 +205,8 @@
"settings.weather.footer_hint": "Desktop weather widgets will reuse the location and alert exclusion settings configured here.", "settings.weather.footer_hint": "Desktop weather widgets will reuse the location and alert exclusion settings configured here.",
"settings.study.title": "Study", "settings.study.title": "Study",
"settings.study.description": "Configure study environment monitoring, focus timer, and alert settings.", "settings.study.description": "Configure study environment monitoring, focus timer, and alert settings.",
"settings.study.master_switch_header": "Study Feature",
"settings.study.master_switch_desc": "Enable study environment monitoring and focus timer. When disabled, related components will not collect any data.",
"settings.study.noise_header": "Noise Monitoring", "settings.study.noise_header": "Noise Monitoring",
"settings.study.noise_description": "Configure microphone sampling rate and noise scoring sensitivity.", "settings.study.noise_description": "Configure microphone sampling rate and noise scoring sensitivity.",
"settings.study.sampling_rate_label": "Sampling Rate", "settings.study.sampling_rate_label": "Sampling Rate",
@@ -883,6 +885,8 @@
"recording.widget.hint.saved_format": "Saved {0}", "recording.widget.hint.saved_format": "Saved {0}",
"recording.widget.save_picker_title": "Save recording file", "recording.widget.save_picker_title": "Save recording file",
"recording.widget.save_picker_type": "WAV audio", "recording.widget.save_picker_type": "WAV audio",
"study.widget.disabled_title": "Study Feature Disabled",
"study.widget.disabled_hint": "Please enable in Settings",
"study.environment.status_label": "Environment", "study.environment.status_label": "Environment",
"study.environment.status.initializing": "Initializing", "study.environment.status.initializing": "Initializing",
"study.environment.status.ready": "Ready", "study.environment.status.ready": "Ready",

View File

@@ -208,6 +208,8 @@
"settings.weather.location_saved_format": "天气位置已保存:{0}", "settings.weather.location_saved_format": "天气位置已保存:{0}",
"settings.study.title": "自习", "settings.study.title": "自习",
"settings.study.description": "配置自习环境监测、专注计时和提醒设置。", "settings.study.description": "配置自习环境监测、专注计时和提醒设置。",
"settings.study.master_switch_header": "自习功能",
"settings.study.master_switch_desc": "启用自习环境监测和专注计时功能。关闭后,相关组件将不会采集任何数据。",
"settings.study.noise_header": "噪音监测", "settings.study.noise_header": "噪音监测",
"settings.study.noise_description": "配置麦克风采集频率和噪音评分敏感度。", "settings.study.noise_description": "配置麦克风采集频率和噪音评分敏感度。",
"settings.study.sampling_rate_label": "采集频率", "settings.study.sampling_rate_label": "采集频率",
@@ -876,6 +878,8 @@
"recording.widget.hint.saved_format": "已保存 {0}", "recording.widget.hint.saved_format": "已保存 {0}",
"recording.widget.save_picker_title": "保存录音文件", "recording.widget.save_picker_title": "保存录音文件",
"recording.widget.save_picker_type": "WAV 音频", "recording.widget.save_picker_type": "WAV 音频",
"study.widget.disabled_title": "自习功能未启用",
"study.widget.disabled_hint": "请在设置中开启",
"study.environment.status_label": "环境状态", "study.environment.status_label": "环境状态",
"study.environment.status.initializing": "初始化中", "study.environment.status.initializing": "初始化中",
"study.environment.status.ready": "待机", "study.environment.status.ready": "待机",

View File

@@ -120,6 +120,8 @@ public sealed class AppSettingsSnapshot
#region Study Settings #region Study Settings
public bool StudyEnabled { get; set; } = true;
public int? StudyFrameMs { get; set; } public int? StudyFrameMs { get; set; }
public double? StudyScoreThresholdDbfs { get; set; } public double? StudyScoreThresholdDbfs { get; set; }

View File

@@ -2342,6 +2342,27 @@ public sealed partial class StudySettingsPageViewModel : ViewModelBase
_isInitializing = false; _isInitializing = false;
} }
#region Properties - Master Switch
[ObservableProperty]
private string _masterSwitchHeader = string.Empty;
[ObservableProperty]
private string _masterSwitchDescription = string.Empty;
[ObservableProperty]
private bool _studyEnabled = true;
partial void OnStudyEnabledChanged(bool value)
{
if (!_isInitializing)
{
SaveMasterSwitch();
}
}
#endregion
#region Properties - Noise Monitoring #region Properties - Noise Monitoring
[ObservableProperty] [ObservableProperty]
@@ -2686,6 +2707,9 @@ public sealed partial class StudySettingsPageViewModel : ViewModelBase
{ {
var appSnapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App); var appSnapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
// Master switch
StudyEnabled = appSnapshot.StudyEnabled;
// Noise settings // Noise settings
SamplingRateMs = appSnapshot.StudyFrameMs is > 0 ? appSnapshot.StudyFrameMs.Value : 50; SamplingRateMs = appSnapshot.StudyFrameMs is > 0 ? appSnapshot.StudyFrameMs.Value : 50;
NoiseSensitivityDbfs = appSnapshot.StudyScoreThresholdDbfs ?? -50; NoiseSensitivityDbfs = appSnapshot.StudyScoreThresholdDbfs ?? -50;
@@ -2718,6 +2742,14 @@ public sealed partial class StudySettingsPageViewModel : ViewModelBase
UpdateAvgWindowSecText(); UpdateAvgWindowSecText();
} }
private void SaveMasterSwitch()
{
var appSnapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
appSnapshot.StudyEnabled = StudyEnabled;
_settingsFacade.Settings.SaveSnapshot(SettingsScope.App, appSnapshot,
changedKeys: [nameof(AppSettingsSnapshot.StudyEnabled)]);
}
private void SaveNoiseSettings() private void SaveNoiseSettings()
{ {
var appSnapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App); var appSnapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
@@ -2796,6 +2828,9 @@ public sealed partial class StudySettingsPageViewModel : ViewModelBase
private void RefreshLocalizedText() private void RefreshLocalizedText()
{ {
MasterSwitchHeader = L("settings.study.master_switch_header", "自习功能");
MasterSwitchDescription = L("settings.study.master_switch_desc", "启用自习环境监测和专注计时功能。关闭后,相关组件将不会采集任何数据。");
NoiseMonitoringHeader = L("settings.study.noise_header", "噪音监测"); NoiseMonitoringHeader = L("settings.study.noise_header", "噪音监测");
NoiseMonitoringDescription = L("settings.study.noise_description", "配置麦克风采集频率和噪音评分敏感度。"); NoiseMonitoringDescription = L("settings.study.noise_description", "配置麦克风采集频率和噪音评分敏感度。");
SamplingRateLabel = L("settings.study.sampling_rate_label", "采集频率"); SamplingRateLabel = L("settings.study.sampling_rate_label", "采集频率");

View File

@@ -53,6 +53,7 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _studyEnabled = true;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private readonly record struct DeductionMetrics( private readonly record struct DeductionMetrics(
@@ -98,7 +99,10 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
{ {
_isAttached = true; _isAttached = true;
ReloadLanguageCode(); ReloadLanguageCode();
_ = _studyAnalyticsService.StartOrResumeMonitoring(); if (_studyEnabled)
{
_ = _studyAnalyticsService.StartOrResumeMonitoring();
}
UpdateTimerState(); UpdateTimerState();
RefreshVisual(); RefreshVisual();
} }
@@ -142,10 +146,20 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
private void RefreshVisual() private void RefreshVisual()
{ {
var snapshot = _studyAnalyticsService.GetSnapshot();
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
if (!_studyEnabled)
{
ModeTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
ApplyModeBadgeColor(panelColor, Color.Parse("#FF9AA0A6"));
ApplyLocalizedLabels();
ApplyUnavailableMetrics();
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot();
var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running; var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running;
var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null; var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null;
var isSessionView = isSessionRunning || isSessionReport; var isSessionView = isSessionRunning || isSessionReport;
@@ -595,6 +609,7 @@ public partial class StudyDeductionReasonsWidget : UserControl, IDesktopComponen
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private void ApplyVariableFontFamily() private void ApplyVariableFontFamily()

View File

@@ -30,6 +30,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
private bool _isAttached; private bool _isAttached;
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
private bool _isDisposed; private bool _isDisposed;
private bool _studyEnabled = true;
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
public StudyEnvironmentWidget() public StudyEnvironmentWidget()
@@ -132,6 +133,13 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
if (_isAttached) if (_isAttached)
{ {
_monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease(); _monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease();
@@ -147,6 +155,7 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
var appSnapshot = _appSettingsService.Load(); var appSnapshot = _appSettingsService.Load();
var componentSnapshot = _componentSettingsService.Load(); var componentSnapshot = _componentSettingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(appSnapshot.LanguageCode);
_studyEnabled = appSnapshot.StudyEnabled;
_showDisplayDb = componentSnapshot.StudyEnvironmentShowDisplayDb; _showDisplayDb = componentSnapshot.StudyEnvironmentShowDisplayDb;
_showDbfs = componentSnapshot.StudyEnvironmentShowDbfs; _showDbfs = componentSnapshot.StudyEnvironmentShowDbfs;
_componentColorScheme = componentSnapshot.ColorSchemeSource; _componentColorScheme = componentSnapshot.ColorSchemeSource;
@@ -158,6 +167,17 @@ public partial class StudyEnvironmentWidget : UserControl, IDesktopComponentWidg
private void RefreshVisual() private void RefreshVisual()
{ {
if (!_studyEnabled)
{
StatusTitleTextBlock.Text = L("study.widget.disabled_title", "自习功能未启用");
StatusValueTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
StatusValueTextBlock.Foreground = TryResolveThemeBrush("AdaptiveTextSecondaryBrush", "#FF9AA0A6");
NoiseValueTextBlock.Text = "--";
NoiseSubValueTextBlock.IsVisible = false;
UpdateAdaptiveLayout();
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot(); var snapshot = _studyAnalyticsService.GetSnapshot();
var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null; var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null;

View File

@@ -53,6 +53,7 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _studyEnabled = true;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
@@ -151,6 +152,13 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
var shouldMonitor = _isAttached && _isOnActivePage; var shouldMonitor = _isAttached && _isOnActivePage;
if (shouldMonitor) if (shouldMonitor)
{ {
@@ -164,11 +172,21 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
private void RefreshVisual() private void RefreshVisual()
{ {
var snapshot = _studyAnalyticsService.GetSnapshot();
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
ApplyLocalizedLabels(); ApplyLocalizedLabels();
if (!_studyEnabled)
{
ModeTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
ApplyModeBadgeColor(panelColor, Color.Parse("#FF9AA0A6"));
DensityValueTextBlock.Text = "--";
DensityUnitTextBlock.Text = "";
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot();
var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running; var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running;
var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null; var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null;
var isSessionView = isSessionRunning || isSessionReport; var isSessionView = isSessionRunning || isSessionReport;
@@ -528,6 +546,7 @@ public partial class StudyInterruptDensityWidget : UserControl, IDesktopComponen
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private void ApplyVariableFontFamily() private void ApplyVariableFontFamily()

View File

@@ -70,6 +70,7 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
private bool _isOnActivePage = true; private bool _isOnActivePage = true;
private bool _isSubscribed; private bool _isSubscribed;
private bool _isDisposed; private bool _isDisposed;
private bool _studyEnabled = true;
private int _framesSinceCompaction; private int _framesSinceCompaction;
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
@@ -263,6 +264,13 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
if (_isAttached) if (_isAttached)
{ {
_monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease(); _monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease();
@@ -278,6 +286,15 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
if (!_studyEnabled)
{
StatusTextBlock.Text = L("study.widget.disabled_title", "自习功能未启用");
RealtimeValueTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
ApplyStatusBadgeStyle(StatusVisualKind.Default, panelColor);
ChartControl.UpdateSeries([]);
return;
}
var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null; var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null;
if (isSessionReport && snapshot.LastSessionReport is not null) if (isSessionReport && snapshot.LastSessionReport is not null)
{ {
@@ -578,6 +595,7 @@ public partial class StudyNoiseCurveWidget : UserControl, IDesktopComponentWidge
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private string L(string key, string fallback) private string L(string key, string fallback)

View File

@@ -56,6 +56,7 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
private bool _isDisposed; private bool _isDisposed;
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _studyEnabled = true;
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
private readonly record struct DistributionStats( private readonly record struct DistributionStats(
@@ -157,6 +158,13 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
if (_isAttached) if (_isAttached)
{ {
_monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease(); _monitoringLease ??= _monitoringLeaseCoordinator.AcquireLease();
@@ -169,13 +177,23 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
private void RefreshVisual() private void RefreshVisual()
{ {
var snapshot = _studyAnalyticsService.GetSnapshot();
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
TitleTextBlock.Text = L("study.noise_distribution.title", "Noise Level Distribution"); TitleTextBlock.Text = L("study.noise_distribution.title", "Noise Level Distribution");
ApplyLocalizedAxisLabels(); ApplyLocalizedAxisLabels();
if (!_studyEnabled)
{
ModeTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
ApplyModeBadgeColor(panelColor, Color.Parse("#FF9AA0A6"));
ChartControl.UpdateSeries([], 45);
SummaryTextBlock.Text = "--";
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot();
var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running; var isSessionRunning = snapshot.Session.State == StudySessionRuntimeState.Running;
var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null; var isSessionReport = snapshot.DataMode == StudyDataMode.SessionReport && snapshot.LastSessionReport is not null;
var isSessionView = isSessionRunning || isSessionReport; var isSessionView = isSessionRunning || isSessionReport;
@@ -570,6 +588,7 @@ public partial class StudyNoiseDistributionWidget : UserControl, IDesktopCompone
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private void ApplyVariableFontFamily() private void ApplyVariableFontFamily()

View File

@@ -57,6 +57,7 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _isExpandedMode; private bool _isExpandedMode;
private bool _studyEnabled = true;
private string _languageCode = "zh-CN"; private string _languageCode = "zh-CN";
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
@@ -140,6 +141,13 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
var shouldMonitor = _isAttached && _isOnActivePage; var shouldMonitor = _isAttached && _isOnActivePage;
if (shouldMonitor) if (shouldMonitor)
{ {
@@ -153,12 +161,22 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
private void RefreshVisual() private void RefreshVisual()
{ {
var snapshot = _studyAnalyticsService.GetSnapshot();
ApplyLocalizedLabels(); ApplyLocalizedLabels();
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
if (!_studyEnabled)
{
TitleTextBlock.Text = L("study.widget.disabled_title", "自习功能未启用");
ModeTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
CurrentScoreTextBlock.Text = "--";
CurrentLabelTextBlock.Text = "";
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot();
var realtimeScore = ComputeRealtimeScore(snapshot); var realtimeScore = ComputeRealtimeScore(snapshot);
if (snapshot.DataMode == StudyDataMode.Realtime && realtimeScore is { } score) if (snapshot.DataMode == StudyDataMode.Realtime && realtimeScore is { } score)
{ {
@@ -676,6 +694,7 @@ public partial class StudyScoreOverviewWidget : UserControl, IDesktopComponentWi
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private void ApplyVariableFontFamily() private void ApplyVariableFontFamily()

View File

@@ -64,6 +64,7 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
private bool _isDisposed; private bool _isDisposed;
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _studyEnabled = true;
private IDisposable? _monitoringLease; private IDisposable? _monitoringLease;
private string? _transientMessage; private string? _transientMessage;
private DateTimeOffset _transientMessageExpireAt; private DateTimeOffset _transientMessageExpireAt;
@@ -147,6 +148,13 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
private void UpdateMonitoringLeaseState() private void UpdateMonitoringLeaseState()
{ {
if (!_studyEnabled)
{
_monitoringLease?.Dispose();
_monitoringLease = null;
return;
}
var shouldMonitor = _isAttached && _isOnActivePage; var shouldMonitor = _isAttached && _isOnActivePage;
if (shouldMonitor) if (shouldMonitor)
{ {
@@ -193,11 +201,21 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
private void RefreshVisual() private void RefreshVisual()
{ {
var snapshot = _studyAnalyticsService.GetSnapshot();
var now = DateTimeOffset.UtcNow; var now = DateTimeOffset.UtcNow;
var panelColor = ResolvePanelBackgroundColor(); var panelColor = ResolvePanelBackgroundColor();
ApplyTypographyByBackground(panelColor); ApplyTypographyByBackground(panelColor);
if (!_studyEnabled)
{
PrimaryTextBlock.Text = L("study.widget.disabled_title", "自习功能未启用");
SecondaryTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
ActionIcon.Kind = MaterialIconKind.Settings;
ApplyActionBadgeStyle(panelColor, Color.Parse("#FF9AA0A6"));
return;
}
var snapshot = _studyAnalyticsService.GetSnapshot();
if (_transientMessage is not null && now > _transientMessageExpireAt) if (_transientMessage is not null && now > _transientMessageExpireAt)
{ {
_transientMessage = null; _transientMessage = null;
@@ -469,6 +487,7 @@ public partial class StudySessionControlWidget : UserControl, IDesktopComponentW
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private string L(string key, string fallback) private string L(string key, string fallback)

View File

@@ -58,6 +58,7 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
private bool _isDisposed; private bool _isDisposed;
private bool _isCompactMode; private bool _isCompactMode;
private bool _isUltraCompactMode; private bool _isUltraCompactMode;
private bool _studyEnabled = true;
private string? _loadingSessionId; private string? _loadingSessionId;
private HistoryDialogMode _dialogMode; private HistoryDialogMode _dialogMode;
private string? _dialogSessionId; private string? _dialogSessionId;
@@ -179,6 +180,19 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
TitleTextBlock.Text = L("study.session_history.title", "Session History"); TitleTextBlock.Text = L("study.session_history.title", "Session History");
TitleTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, PrimaryColorCandidates, MinTextContrast); TitleTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, PrimaryColorCandidates, MinTextContrast);
if (!_studyEnabled)
{
if (_dialogMode != HistoryDialogMode.None)
{
CloseDialog();
}
SessionListPanel.Children.Clear();
StatusTextBlock.Text = L("study.widget.disabled_hint", "请在设置中开启");
StatusTextBlock.Foreground = CreateAdaptiveBrush(panelSamples, SecondaryColorCandidates, MinTextContrast);
return;
}
if (_transientStatus is not null && DateTimeOffset.UtcNow > _transientStatusExpireAt) if (_transientStatus is not null && DateTimeOffset.UtcNow > _transientStatusExpireAt)
{ {
_transientStatus = null; _transientStatus = null;
@@ -581,6 +595,7 @@ public partial class StudySessionHistoryWidget : UserControl, IDesktopComponentW
{ {
var snapshot = _settingsService.Load(); var snapshot = _settingsService.Load();
_languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode); _languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
_studyEnabled = snapshot.StudyEnabled;
} }
private void UpdateAdaptiveLayout() private void UpdateAdaptiveLayout()

View File

@@ -8,10 +8,23 @@
<ScrollViewer VerticalScrollBarVisibility="Auto"> <ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Classes="settings-page-container settings-page-animated"> <StackPanel Classes="settings-page-container settings-page-animated">
<!-- 总开关 -->
<ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding MasterSwitchHeader}"
Description="{Binding MasterSwitchDescription}">
<ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="DataHistogram" />
</ui:SettingsExpander.IconSource>
<ui:SettingsExpander.Footer>
<ToggleSwitch IsChecked="{Binding StudyEnabled}" />
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<!-- 噪音监测设置 --> <!-- 噪音监测设置 -->
<ui:SettingsExpander Classes="settings-expander-card" <ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding NoiseMonitoringHeader}" Header="{Binding NoiseMonitoringHeader}"
Description="{Binding NoiseMonitoringDescription}"> Description="{Binding NoiseMonitoringDescription}"
IsEnabled="{Binding StudyEnabled}">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Mic" /> <fi:SymbolIconSource Symbol="Mic" />
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>
@@ -77,7 +90,8 @@
<!-- 专注计时设置 --> <!-- 专注计时设置 -->
<ui:SettingsExpander Classes="settings-expander-card" <ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding FocusTimerHeader}" Header="{Binding FocusTimerHeader}"
Description="{Binding FocusTimerDescription}"> Description="{Binding FocusTimerDescription}"
IsEnabled="{Binding StudyEnabled}">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Timer" /> <fi:SymbolIconSource Symbol="Timer" />
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>
@@ -214,7 +228,8 @@
<!-- 提醒设置 --> <!-- 提醒设置 -->
<ui:SettingsExpander Classes="settings-expander-card" <ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding AlertHeader}" Header="{Binding AlertHeader}"
Description="{Binding AlertDescription}"> Description="{Binding AlertDescription}"
IsEnabled="{Binding StudyEnabled}">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Alert" /> <fi:SymbolIconSource Symbol="Alert" />
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>
@@ -255,7 +270,8 @@
<!-- 显示设置 --> <!-- 显示设置 -->
<ui:SettingsExpander Classes="settings-expander-card" <ui:SettingsExpander Classes="settings-expander-card"
Header="{Binding DisplayHeader}" Header="{Binding DisplayHeader}"
Description="{Binding DisplayDescription}"> Description="{Binding DisplayDescription}"
IsEnabled="{Binding StudyEnabled}">
<ui:SettingsExpander.IconSource> <ui:SettingsExpander.IconSource>
<fi:SymbolIconSource Symbol="Eye" /> <fi:SymbolIconSource Symbol="Eye" />
</ui:SettingsExpander.IconSource> </ui:SettingsExpander.IconSource>