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;
}
}