From a81ee5b3db6e57b9631ce40608fc91844545b4ee Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 27 Dec 2025 22:41:24 +0800 Subject: [PATCH] =?UTF-8?q?add:PPT=E6=97=B6=E9=97=B4=E8=83=B6=E5=9B=8A?= =?UTF-8?q?=EF=BC=88=E6=B2=A1=E5=81=9A=E5=AE=8C=E4=B8=8D=E5=A5=BD=E7=9C=8B?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/Converters.cs | 17 + Ink Canvas/Helpers/PPTUIManager.cs | 2 + Ink Canvas/MainWindow.xaml | 30 + Ink Canvas/MainWindow.xaml.cs | 149 +++- Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 15 + Ink Canvas/Resources/Settings.cs | 4 + Ink Canvas/Windows/PPTTimeCapsule.xaml | 191 +++++ Ink Canvas/Windows/PPTTimeCapsule.xaml.cs | 670 ++++++++++++++++++ 8 files changed, 1076 insertions(+), 2 deletions(-) create mode 100644 Ink Canvas/Windows/PPTTimeCapsule.xaml create mode 100644 Ink Canvas/Windows/PPTTimeCapsule.xaml.cs diff --git a/Ink Canvas/Helpers/Converters.cs b/Ink Canvas/Helpers/Converters.cs index 31feedb0..4736f6ce 100644 --- a/Ink Canvas/Helpers/Converters.cs +++ b/Ink Canvas/Helpers/Converters.cs @@ -135,4 +135,21 @@ namespace Ink_Canvas.Converter return Visibility.Visible; } } + + public class RippleEffectTranslationConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is double d) + { + return -d / 2; + } + return 0.0; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return null; + } + } } diff --git a/Ink Canvas/Helpers/PPTUIManager.cs b/Ink Canvas/Helpers/PPTUIManager.cs index 7b6c355d..aa91ab25 100644 --- a/Ink Canvas/Helpers/PPTUIManager.cs +++ b/Ink Canvas/Helpers/PPTUIManager.cs @@ -101,6 +101,7 @@ namespace Ink_Canvas.Helpers UpdateNavigationPanelsVisibility(); UpdateNavigationButtonStyles(); + _mainWindow.UpdatePPTTimeCapsuleVisibility(); if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper) { // 设置为画板模式,允许全屏操作 @@ -120,6 +121,7 @@ namespace Ink_Canvas.Helpers _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible; _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; HideAllNavigationPanels(); + _mainWindow.UpdatePPTTimeCapsuleVisibility(); if (MainWindow.Settings.Advanced.IsEnableAvoidFullScreenHelper) { // 恢复为非画板模式,重新启用全屏限制 diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 33a4c38b..2eb6247b 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2323,6 +2323,27 @@ + + + + + + + + + + + + + @@ -10298,6 +10319,15 @@ Height="200"> + + + + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index f5ae8694..8f62e33c 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -698,14 +698,30 @@ namespace Ink_Canvas if (TimerControl != null && MinimizedTimerControl != null) { MinimizedTimerControl.SetParentControl(TimerControl); + + // 设置PPT时间胶囊的父控件 + if (PPTTimeCapsule != null) + { + PPTTimeCapsule.SetParentControl(TimerControl); + } TimerControl.ShowMinimizedRequested += (s, args) => { if (TimerContainer != null && MinimizedTimerContainer != null && MinimizedTimerControl != null) { TimerContainer.Visibility = Visibility.Collapsed; - MinimizedTimerContainer.Visibility = Visibility.Visible; - MinimizedTimerControl.Visibility = Visibility.Visible; + + if (Settings.PowerPointSettings.EnablePPTTimeCapsule && + BtnPPTSlideShowEnd.Visibility == Visibility.Visible && + PPTTimeCapsule != null) + { + MinimizedTimerContainer.Visibility = Visibility.Collapsed; + } + else + { + MinimizedTimerContainer.Visibility = Visibility.Visible; + MinimizedTimerControl.Visibility = Visibility.Visible; + } } }; @@ -716,6 +732,24 @@ namespace Ink_Canvas MinimizedTimerContainer.Visibility = Visibility.Collapsed; MinimizedTimerControl.Visibility = Visibility.Collapsed; } + + // 如果启用了PPT时间胶囊,停止倒计时显示 + if (Settings.PowerPointSettings.EnablePPTTimeCapsule && PPTTimeCapsule != null) + { + PPTTimeCapsule.StopCountdown(); + } + }; + + // 监听计时器完成事件 + TimerControl.TimerCompleted += (s, args) => + { + // 如果启用了PPT时间胶囊且在PPT模式下,触发完成动画 + if (Settings.PowerPointSettings.EnablePPTTimeCapsule && + BtnPPTSlideShowEnd.Visibility == Visibility.Visible && + PPTTimeCapsule != null) + { + PPTTimeCapsule.OnTimerCompleted(); + } }; } }), DispatcherPriority.Loaded); @@ -2580,6 +2614,54 @@ namespace Ink_Canvas } } + private void ToggleSwitchEnablePPTTimeCapsule_Toggled(object sender, RoutedEventArgs e) + { + try + { + if (!isLoaded) return; + var toggle = sender as ToggleSwitch; + Settings.PowerPointSettings.EnablePPTTimeCapsule = toggle != null && toggle.IsOn; + SaveSettingsToFile(); + + // 如果当前在PPT放映模式,需要立即更新时间胶囊的显示状态 + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + UpdatePPTTimeCapsuleVisibility(); + } + + LogHelper.WriteLogToFile($"PPT时间显示胶囊已{(Settings.PowerPointSettings.EnablePPTTimeCapsule ? "启用" : "禁用")}", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换PPT时间显示胶囊时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void ComboBoxPPTTimeCapsulePosition_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + try + { + if (!isLoaded) return; + if (ComboBoxPPTTimeCapsulePosition != null) + { + Settings.PowerPointSettings.PPTTimeCapsulePosition = ComboBoxPPTTimeCapsulePosition.SelectedIndex; + SaveSettingsToFile(); + + // 如果当前在PPT放映模式,需要立即更新时间胶囊的位置 + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + UpdatePPTTimeCapsulePosition(); + } + + LogHelper.WriteLogToFile($"PPT时间胶囊位置已更改为: {ComboBoxPPTTimeCapsulePosition.SelectedIndex}", LogHelper.LogType.Event); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更改PPT时间胶囊位置时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + /// /// 更新PPT模式下手势按钮的显示状态 /// @@ -2603,6 +2685,69 @@ namespace Ink_Canvas LogHelper.WriteLogToFile($"更新PPT模式下手势按钮显示状态时出错: {ex.Message}", LogHelper.LogType.Error); } } + + /// + /// 更新PPT时间胶囊的显示状态 + /// + public void UpdatePPTTimeCapsuleVisibility() + { + try + { + if (PPTTimeCapsuleContainer == null || PPTTimeCapsule == null) return; + + if (Settings.PowerPointSettings.EnablePPTTimeCapsule && + BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + PPTTimeCapsuleContainer.Visibility = Visibility.Visible; + UpdatePPTTimeCapsulePosition(); + } + else + { + PPTTimeCapsuleContainer.Visibility = Visibility.Collapsed; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新PPT时间胶囊显示状态时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 更新PPT时间胶囊的位置 + /// + private void UpdatePPTTimeCapsulePosition() + { + try + { + if (PPTTimeCapsuleContainer == null) return; + + int position = Settings.PowerPointSettings.PPTTimeCapsulePosition; + // 0-左上角, 1-右上角, 2-顶部居中 + switch (position) + { + case 0: // 左上角 + PPTTimeCapsuleContainer.HorizontalAlignment = HorizontalAlignment.Left; + PPTTimeCapsuleContainer.VerticalAlignment = VerticalAlignment.Top; + PPTTimeCapsuleContainer.Margin = new Thickness(20, 20, 0, 0); + break; + case 1: // 右上角 + PPTTimeCapsuleContainer.HorizontalAlignment = HorizontalAlignment.Right; + PPTTimeCapsuleContainer.VerticalAlignment = VerticalAlignment.Top; + PPTTimeCapsuleContainer.Margin = new Thickness(0, 20, 20, 0); + break; + case 2: // 顶部居中 + PPTTimeCapsuleContainer.HorizontalAlignment = HorizontalAlignment.Center; + PPTTimeCapsuleContainer.VerticalAlignment = VerticalAlignment.Top; + PPTTimeCapsuleContainer.Margin = new Thickness(0, 20, 0, 0); + break; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新PPT时间胶囊位置时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + #endregion diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 55ee9522..876b7672 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -480,6 +480,21 @@ namespace Ink_Canvas ToggleSwitchNotifyPreviousPage.IsOn = Settings.PowerPointSettings.IsNotifyPreviousPage; + // PPT时间显示胶囊设置 + if (ToggleSwitchEnablePPTTimeCapsule != null) + { + ToggleSwitchEnablePPTTimeCapsule.IsOn = Settings.PowerPointSettings.EnablePPTTimeCapsule; + } + if (ComboBoxPPTTimeCapsulePosition != null) + { + int position = Settings.PowerPointSettings.PPTTimeCapsulePosition; + if (position < 0 || position > 2) + { + position = 1; // 默认右上角 + } + ComboBoxPPTTimeCapsulePosition.SelectedIndex = position; + } + // -- new -- ToggleSwitchShowPPTButton.IsOn = Settings.PowerPointSettings.ShowPPTButton; diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index d9658b05..02a2cee4 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -333,6 +333,10 @@ namespace Ink_Canvas public bool ShowGestureButtonInSlideShow { get; set; } = false; [JsonProperty("skipAnimationsWhenGoNext")] public bool SkipAnimationsWhenGoNext { get; set; } = false; + [JsonProperty("enablePPTTimeCapsule")] + public bool EnablePPTTimeCapsule { get; set; } = true; + [JsonProperty("pptTimeCapsulePosition")] + public int PPTTimeCapsulePosition { get; set; } = 1; } public class Automation diff --git a/Ink Canvas/Windows/PPTTimeCapsule.xaml b/Ink Canvas/Windows/PPTTimeCapsule.xaml new file mode 100644 index 00000000..b442cde9 --- /dev/null +++ b/Ink Canvas/Windows/PPTTimeCapsule.xaml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ink Canvas/Windows/PPTTimeCapsule.xaml.cs b/Ink Canvas/Windows/PPTTimeCapsule.xaml.cs new file mode 100644 index 00000000..5f7e8974 --- /dev/null +++ b/Ink Canvas/Windows/PPTTimeCapsule.xaml.cs @@ -0,0 +1,670 @@ +using Ink_Canvas.Helpers; +using Microsoft.Win32; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Timers; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Threading; +using File = System.IO.File; + +namespace Ink_Canvas.Windows +{ + /// + /// PPT时间显示胶囊控件 + /// + public partial class PPTTimeCapsule : UserControl + { + private System.Timers.Timer timeUpdateTimer; + private System.Timers.Timer countdownUpdateTimer; // 倒计时更新定时器(参考MinimizedTimerControl) + private DateTime lastTime = DateTime.MinValue; + private TimerControl parentControl; // 父计时器控件引用(参考MinimizedTimerControl) + private bool wasTimerRunning = false; // 上次检查时计时器是否运行 + private bool isOvertime = false; + private Storyboard capsuleExpandStoryboard; + private Storyboard capsuleShrinkStoryboard; + private Storyboard colonBlinkStoryboard; + private double originalCapsuleWidth = 0; + + public PPTTimeCapsule() + { + InitializeComponent(); + InitializeTimers(); + ApplyTheme(); + + // 监听主题变化 + SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; + + Loaded += PPTTimeCapsule_Loaded; + Unloaded += PPTTimeCapsule_Unloaded; + IsVisibleChanged += PPTTimeCapsule_IsVisibleChanged; + } + + private void PPTTimeCapsule_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (Visibility == Visibility.Visible) + { + ApplyTheme(); + } + } + + private void PPTTimeCapsule_Loaded(object sender, RoutedEventArgs e) + { + // 记录初始宽度 + if (MainCapsule != null && originalCapsuleWidth == 0) + { + originalCapsuleWidth = MainCapsule.ActualWidth > 0 ? MainCapsule.ActualWidth : 120; + } + UpdateTimeDisplay(); + StartTimeUpdate(); + } + + private void PPTTimeCapsule_Unloaded(object sender, RoutedEventArgs e) + { + StopTimeUpdate(); + SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; + + if (countdownUpdateTimer != null) + { + countdownUpdateTimer.Stop(); + countdownUpdateTimer.Dispose(); + } + } + + private void InitializeTimers() + { + // 时间更新定时器(每秒更新) + timeUpdateTimer = new System.Timers.Timer(1000); + timeUpdateTimer.Elapsed += TimeUpdateTimer_Elapsed; + + // 倒计时更新定时器 + countdownUpdateTimer = new System.Timers.Timer(100); + countdownUpdateTimer.Elapsed += CountdownUpdateTimer_Elapsed; + } + + private void TimeUpdateTimer_Elapsed(object sender, ElapsedEventArgs e) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + UpdateTimeDisplay(); + }), DispatcherPriority.Normal); + } + + private void CountdownUpdateTimer_Elapsed(object sender, ElapsedEventArgs e) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + if (this.Visibility != Visibility.Visible) + { + return; + } + + UpdateCountdownDisplay(); + }), DispatcherPriority.Normal); + } + + private void StartTimeUpdate() + { + if (timeUpdateTimer != null && !timeUpdateTimer.Enabled) + { + timeUpdateTimer.Start(); + } + if (countdownUpdateTimer != null && !countdownUpdateTimer.Enabled) + { + countdownUpdateTimer.Start(); + } + // 启动冒号闪动动画 + StartColonBlinkAnimation(); + } + + private void StopTimeUpdate() + { + if (timeUpdateTimer != null) + { + timeUpdateTimer.Stop(); + } + if (countdownUpdateTimer != null) + { + countdownUpdateTimer.Stop(); + } + // 停止冒号闪动动画 + StopColonBlinkAnimation(); + } + + /// + /// 设置父计时器控件 + /// + public void SetParentControl(TimerControl parent) + { + parentControl = parent; + if (parentControl != null) + { + UpdateCountdownDisplay(); + } + } + + private void UpdateTimeDisplay() + { + DateTime now = DateTime.Now; + + // 检查小时是否改变 + if (lastTime != DateTime.MinValue && lastTime.Hour != now.Hour) + { + // 先更新数字内容 + SetDigitDisplay("Hour1Display", now.Hour / 10); + SetDigitDisplay("Hour2Display", now.Hour % 10); + + // 重置Transform位置到上方 + HourContentTransform.Y = -40; + HourPanel.Opacity = 0; + + // 播放小时滚动动画:从上方滚入 + PlayHourScrollAnimation(); + } + else if (lastTime == DateTime.MinValue) + { + // 首次加载,直接更新显示 + SetDigitDisplay("Hour1Display", now.Hour / 10); + SetDigitDisplay("Hour2Display", now.Hour % 10); + } + + // 检查分钟是否改变 + if (lastTime != DateTime.MinValue && lastTime.Minute != now.Minute) + { + // 先更新数字内容 + SetDigitDisplay("Minute1Display", now.Minute / 10); + SetDigitDisplay("Minute2Display", now.Minute % 10); + + // 重置Transform位置到上方 + MinuteContentTransform.Y = -40; + MinutePanel.Opacity = 0; + + // 播放分钟滚动动画:从上方滚入 + PlayMinuteScrollAnimation(); + } + else if (lastTime == DateTime.MinValue) + { + // 首次加载,直接更新显示 + SetDigitDisplay("Minute1Display", now.Minute / 10); + SetDigitDisplay("Minute2Display", now.Minute % 10); + } + + lastTime = now; + } + + private void PlayHourScrollAnimation() + { + // 新时间从上方滚入(-25到0) + var scrollAnimation = (Storyboard)Resources["HourScrollAnimation"]; + scrollAnimation.Begin(); + } + + private void PlayMinuteScrollAnimation() + { + // 新时间从上方滚入(-25到0) + var scrollAnimation = (Storyboard)Resources["MinuteScrollAnimation"]; + scrollAnimation.Begin(); + } + + /// + /// 根据数字值设置SVG数字显示 + /// + /// Path控件的名称 + /// 要显示的数字(0-9) + private void SetDigitDisplay(string pathName, int digit) + { + var path = this.FindName(pathName) as System.Windows.Shapes.Path; + if (path != null) + { + digit = Math.Max(0, Math.Min(9, digit)); + + string resourceKey = $"Digit{digit}"; + var geometry = this.FindResource(resourceKey) as Geometry; + if (geometry != null) + { + path.Data = geometry; + } + } + } + + /// + /// 设置SVG数字的填充颜色 + /// + /// Path控件的名称 + /// 填充颜色 + private void SetDigitFill(string pathName, Color color) + { + var path = this.FindName(pathName) as System.Windows.Shapes.Path; + if (path != null) + { + path.Fill = new SolidColorBrush(color); + } + } + + private void StartColonBlinkAnimation() + { + try + { + if (colonBlinkStoryboard == null) + { + colonBlinkStoryboard = (Storyboard)Resources["ColonBlinkAnimation"]; + } + if (colonBlinkStoryboard != null) + { + colonBlinkStoryboard.Begin(ColonDisplay, true); // true表示HandoffBehavior.SnapshotAndReplace + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"启动冒号闪动动画失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void StopColonBlinkAnimation() + { + try + { + if (colonBlinkStoryboard != null) + { + colonBlinkStoryboard.Stop(ColonDisplay); + // 恢复冒号透明度 + if (ColonDisplay != null) + { + ColonDisplay.Opacity = 1.0; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"停止冒号闪动动画失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + + /// + /// 停止倒计时 + /// + public void StopCountdown() + { + bool wasRunning = wasTimerRunning; + wasTimerRunning = false; + CountdownPanel.Visibility = Visibility.Collapsed; + + // 重置超时状态 + isOvertime = false; + // 根据主题恢复倒计时文本颜色 + ApplyTheme(); + + // 播放胶囊缩短动画 + if (wasRunning) + { + PlayCapsuleShrinkAnimation(); + } + } + + private void UpdateCountdownDisplay() + { + if (parentControl == null) return; + + // 检查计时器是否正在运行(参考MinimizedTimerControl) + bool isRunning = parentControl.IsTimerRunning; + + // 如果状态改变,更新UI + if (isRunning != wasTimerRunning) + { + wasTimerRunning = isRunning; + if (isRunning) + { + // 计时器开始运行,显示倒计时面板并播放伸长动画 + CountdownPanel.Visibility = Visibility.Visible; + // 确保倒计时文本使用主题颜色 + ApplyTheme(); + PlayCapsuleExpandAnimation(); + } + else + { + // 计时器停止,隐藏倒计时面板并播放缩短动画 + CountdownPanel.Visibility = Visibility.Collapsed; + PlayCapsuleShrinkAnimation(); + isOvertime = false; + // 根据主题恢复倒计时文本颜色 + ApplyTheme(); + return; + } + } + + // 如果计时器未运行,不更新显示 + if (!isRunning) + { + return; + } + + // 直接从parentControl获取剩余时间 + var remainingTime = parentControl.GetRemainingTime(); + if (!remainingTime.HasValue) + { + // 如果无法获取剩余时间(可能是暂停状态),不更新显示 + return; + } + + var timeSpan = remainingTime.Value; + bool isOvertimeMode = timeSpan.TotalSeconds < 0; + + // 处理超时状态 + if (isOvertimeMode) + { + if (!isOvertime) + { + isOvertime = true; + OnTimerOvertime(); + } + + // 确保倒计时文本为红色(如果启用了超时红色文本设置) + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null && MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true) + { + CountdownText.Foreground = new SolidColorBrush(Colors.Red); + } + + // 显示超时时间 + var overtimeSpan = -timeSpan; + if (overtimeSpan.TotalHours >= 1) + { + int hours = (int)overtimeSpan.TotalHours; + CountdownText.Text = $"{hours:D2}:{overtimeSpan.Minutes:D2}:{overtimeSpan.Seconds:D2}"; + } + else + { + CountdownText.Text = $"{overtimeSpan.Minutes:D2}:{overtimeSpan.Seconds:D2}"; + } + } + else + { + // 正常倒计时 + if (isOvertime) + { + // 从超时状态恢复 + isOvertime = false; + // 根据主题恢复倒计时文本颜色(如果未启用超时红色文本) + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow == null || MainWindow.Settings.RandSettings?.EnableOvertimeRedText != true) + { + ApplyTheme(); + } + } + else + { + // 确保正常倒计时时使用主题颜色(如果未启用超时红色文本) + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow == null || MainWindow.Settings.RandSettings?.EnableOvertimeRedText != true) + { + // 检查当前颜色是否是红色,如果不是红色,则应用主题 + if (CountdownText.Foreground is SolidColorBrush brush && brush.Color != Colors.Red) + { + ApplyTheme(); + } + } + } + + if (timeSpan.TotalHours >= 1) + { + int hours = (int)timeSpan.TotalHours; + CountdownText.Text = $"{hours:D2}:{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}"; + } + else + { + CountdownText.Text = $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}"; + } + } + } + + private void PlayCapsuleExpandAnimation() + { + try + { + if (MainCapsule != null) + { + // 记录原始宽度 + if (originalCapsuleWidth == 0) + { + originalCapsuleWidth = MainCapsule.ActualWidth > 0 ? MainCapsule.ActualWidth : 120; + } + + // 计算目标宽度(根据倒计时文本长度估算) + double targetWidth = originalCapsuleWidth + 80; // 增加约80像素用于显示倒计时 + + if (capsuleExpandStoryboard == null) + { + capsuleExpandStoryboard = (Storyboard)Resources["CapsuleExpandAnimation"]; + } + + if (capsuleExpandStoryboard != null) + { + // 设置动画的目标值 + var animation = capsuleExpandStoryboard.Children[0] as DoubleAnimation; + if (animation != null) + { + animation.From = originalCapsuleWidth; + animation.To = targetWidth; + } + + capsuleExpandStoryboard.Begin(); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"播放胶囊伸长动画失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void PlayCapsuleShrinkAnimation() + { + try + { + if (MainCapsule != null && originalCapsuleWidth > 0) + { + if (capsuleShrinkStoryboard == null) + { + capsuleShrinkStoryboard = (Storyboard)Resources["CapsuleShrinkAnimation"]; + } + + if (capsuleShrinkStoryboard != null) + { + // 设置动画的目标值 + var animation = capsuleShrinkStoryboard.Children[0] as DoubleAnimation; + if (animation != null) + { + animation.From = MainCapsule.ActualWidth; + animation.To = originalCapsuleWidth; + } + + capsuleShrinkStoryboard.Begin(); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"播放胶囊缩短动画失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void OnTimerOvertime() + { + // 改变倒计时文字颜色为红色 + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null && MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true) + { + CountdownText.Foreground = new SolidColorBrush(Colors.Red); + } + } + + /// + /// 处理计时器完成事件 + /// + public void OnTimerCompleted() + { + // 确保在UI线程上执行 + if (!Dispatcher.CheckAccess()) + { + Dispatcher.BeginInvoke(new Action(() => OnTimerCompleted()), DispatcherPriority.Normal); + return; + } + + // 停止倒计时 + StopCountdown(); + } + + private void MainCapsule_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + // 点击恢复主计时器窗口(不重置计时器) + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null) + { + // 显示主计时器窗口 + var timerContainer = mainWindow.FindName("TimerContainer") as FrameworkElement; + if (timerContainer != null) + { + timerContainer.Visibility = Visibility.Visible; + } + + // 隐藏最小化计时器容器 + var minimizedContainer = mainWindow.FindName("MinimizedTimerContainer") as FrameworkElement; + if (minimizedContainer != null) + { + minimizedContainer.Visibility = Visibility.Collapsed; + } + + if (mainWindow.TimerControl != null) + { + mainWindow.TimerControl.UpdateActivityTime(); + + mainWindow.TimerControl.CloseRequested -= TimerControl_CloseRequested; + mainWindow.TimerControl.CloseRequested += TimerControl_CloseRequested; + } + } + } + + private void TimerControl_CloseRequested(object sender, EventArgs e) + { + // 当计时器窗口关闭时,隐藏TimerContainer + var mainWindow = Application.Current.MainWindow as MainWindow; + if (mainWindow != null) + { + var timerContainer = mainWindow.FindName("TimerContainer") as FrameworkElement; + if (timerContainer != null) + { + timerContainer.Visibility = Visibility.Collapsed; + } + + var minimizedContainer = mainWindow.FindName("MinimizedTimerContainer") as FrameworkElement; + if (minimizedContainer != null) + { + minimizedContainer.Visibility = Visibility.Collapsed; + } + } + } + + private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + ApplyTheme(); + }), DispatcherPriority.Normal); + } + + private void ApplyTheme() + { + try + { + // 检测系统主题 + bool isDarkTheme = IsDarkTheme(); + + if (isDarkTheme) + { + // 深色主题:使用80%不透明度的深色背景 + CapsuleBackgroundBrush.Color = Color.FromArgb(204, 32, 32, 32); // #CC202020,约80%不透明度 + SetDigitFill("Hour1Display", Colors.White); + SetDigitFill("Hour2Display", Colors.White); + SetDigitFill("Minute1Display", Colors.White); + SetDigitFill("Minute2Display", Colors.White); + ColonDisplay.Foreground = new SolidColorBrush(Colors.White); + CountdownText.Foreground = new SolidColorBrush(Color.FromArgb(200, 255, 255, 255)); + } + else + { + // 浅色主题:使用80%不透明度的白色背景 + CapsuleBackgroundBrush.Color = Color.FromArgb(204, 255, 255, 255); // #CCFFFFFF,约80%不透明度 + SetDigitFill("Hour1Display", Colors.Black); + SetDigitFill("Hour2Display", Colors.Black); + SetDigitFill("Minute1Display", Colors.Black); + SetDigitFill("Minute2Display", Colors.Black); + ColonDisplay.Foreground = new SolidColorBrush(Colors.Black); + CountdownText.Foreground = new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用PPT时间胶囊主题失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private bool IsDarkTheme() + { + try + { + string settingsPath = Path.Combine(App.RootPath, "Configs", "Settings.json"); + if (File.Exists(settingsPath)) + { + string jsonText = File.ReadAllText(settingsPath); + var settings = JsonConvert.DeserializeObject(jsonText); + + if (settings?.Appearance != null) + { + if (settings.Appearance.Theme == 1) + { + return true; + } + else if (settings.Appearance.Theme == 0) + { + return false; + } + else + { + int systemTheme = (int)Microsoft.Win32.Registry.GetValue( + @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", + "AppsUseLightTheme", 1); + return systemTheme == 0; + } + } + } + + int fallbackTheme = (int)Microsoft.Win32.Registry.GetValue( + @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", + "AppsUseLightTheme", 1); + return fallbackTheme == 0; + } + catch + { + return false; + } + } + + /// + /// 获取当前倒计时状态 + /// + public bool IsCountdownRunning => parentControl != null && parentControl.IsTimerRunning; + + /// + /// 获取是否超时 + /// + public bool IsOvertime => isOvertime; + } +} +