6802476afa
将计时器窗口整合至主窗口,优化全屏计时逻辑
478 lines
16 KiB
C#
478 lines
16 KiB
C#
using System;
|
||
using System.Runtime.InteropServices;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Input;
|
||
using System.Windows.Interop;
|
||
using System.Windows.Media;
|
||
using System.Windows.Shapes;
|
||
using System.Windows.Media.Animation;
|
||
|
||
namespace Ink_Canvas.Windows
|
||
{
|
||
public partial class MinimizedTimerWindow : Window
|
||
{
|
||
private TimerControl parentControl;
|
||
private System.Timers.Timer updateTimer;
|
||
private bool isMouseOver = false;
|
||
private bool isDragging = false;
|
||
private Point lastMousePosition;
|
||
|
||
public MinimizedTimerWindow(TimerControl parent)
|
||
{
|
||
InitializeComponent();
|
||
parentControl = parent;
|
||
|
||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||
if (mainWindow != null)
|
||
{
|
||
this.Left = mainWindow.Left;
|
||
this.Top = mainWindow.Top;
|
||
}
|
||
|
||
ScaleWindowForResolution();
|
||
|
||
updateTimer = new System.Timers.Timer(100);
|
||
updateTimer.Elapsed += UpdateTimer_Elapsed;
|
||
updateTimer.Start();
|
||
|
||
parentControl.TimerCompleted += ParentWindow_TimerCompleted;
|
||
|
||
// 应用主题
|
||
ApplyTheme();
|
||
|
||
// 确保窗口置顶
|
||
Loaded += MinimizedTimerWindow_Loaded;
|
||
}
|
||
|
||
private void MinimizedTimerWindow_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// 使用延迟确保窗口完全加载后再应用置顶
|
||
Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
ApplyTopmost();
|
||
}), System.Windows.Threading.DispatcherPriority.Loaded);
|
||
}
|
||
|
||
#region Win32 API 声明和置顶管理
|
||
[DllImport("user32.dll")]
|
||
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||
|
||
[DllImport("user32.dll")]
|
||
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||
|
||
private const int GWL_EXSTYLE = -20;
|
||
private const int WS_EX_TOPMOST = 0x00000008;
|
||
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||
private const uint SWP_NOMOVE = 0x0002;
|
||
private const uint SWP_NOSIZE = 0x0001;
|
||
private const uint SWP_NOACTIVATE = 0x0010;
|
||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||
|
||
/// <summary>
|
||
/// 应用最小化窗口置顶
|
||
/// </summary>
|
||
private void ApplyTopmost()
|
||
{
|
||
try
|
||
{
|
||
var hwnd = new WindowInteropHelper(this).Handle;
|
||
if (hwnd == IntPtr.Zero) return;
|
||
|
||
// 设置WPF的Topmost属性
|
||
Topmost = true;
|
||
|
||
// 使用Win32 API强制置顶
|
||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||
|
||
// 使用SetWindowPos确保窗口在最顶层
|
||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"应用最小化窗口置顶失败: {ex.Message}");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 根据屏幕分辨率和 DPI 缩放窗口大小(保持原始尺寸,使用Transform缩放)
|
||
/// </summary>
|
||
private void ScaleWindowForResolution()
|
||
{
|
||
try
|
||
{
|
||
// 获取屏幕尺寸(考虑 DPI 缩放)
|
||
double screenWidth = SystemParameters.PrimaryScreenWidth;
|
||
double screenHeight = SystemParameters.PrimaryScreenHeight;
|
||
|
||
// 基准分辨率(1920x1080)
|
||
const double baseWidth = 1920.0;
|
||
const double baseHeight = 1080.0;
|
||
|
||
// 计算缩放比例(使用较小的比例以保持比例)
|
||
double scaleX = screenWidth / baseWidth;
|
||
double scaleY = screenHeight / baseHeight;
|
||
double scale = Math.Min(scaleX, scaleY);
|
||
|
||
// 限制最小和最大缩放,避免过小或过大
|
||
scale = Math.Max(0.5, Math.Min(2.0, scale));
|
||
|
||
// 应用缩放变换到整个窗口内容
|
||
var scaleTransform = this.FindName("WindowScaleTransform") as ScaleTransform;
|
||
if (scaleTransform != null)
|
||
{
|
||
scaleTransform.ScaleX = scale;
|
||
scaleTransform.ScaleY = scale;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"缩放窗口大小时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||
{
|
||
if (parentControl != null)
|
||
{
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
if (ShouldCloseWindow())
|
||
{
|
||
this.Close();
|
||
return;
|
||
}
|
||
|
||
UpdateTimeDisplay();
|
||
});
|
||
}
|
||
}
|
||
|
||
private bool ShouldCloseWindow()
|
||
{
|
||
if (parentControl == null) return true;
|
||
|
||
if (MainWindow.Settings.RandSettings?.EnableOvertimeCountUp == true)
|
||
{
|
||
if (parentControl.IsTimerRunning)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var remainingTime = parentControl.GetRemainingTime();
|
||
if (remainingTime.HasValue && remainingTime.Value.TotalSeconds < 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return !parentControl.IsTimerRunning;
|
||
}
|
||
}
|
||
|
||
private void UpdateTimeDisplay()
|
||
{
|
||
if (parentControl == null) return;
|
||
|
||
var remainingTime = parentControl.GetRemainingTime();
|
||
if (remainingTime.HasValue)
|
||
{
|
||
var timeSpan = remainingTime.Value;
|
||
bool isOvertimeMode = timeSpan.TotalSeconds < 0;
|
||
bool shouldShowRed = isOvertimeMode && MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true;
|
||
|
||
int hours, minutes, seconds;
|
||
|
||
if (isOvertimeMode)
|
||
{
|
||
var totalTimeSpan = parentControl.GetTotalTimeSpan();
|
||
if (totalTimeSpan.HasValue)
|
||
{
|
||
var elapsedTime = parentControl.GetElapsedTime();
|
||
if (elapsedTime.HasValue)
|
||
{
|
||
var overtimeSpan = elapsedTime.Value - totalTimeSpan.Value;
|
||
hours = (int)overtimeSpan.TotalHours;
|
||
minutes = overtimeSpan.Minutes;
|
||
seconds = overtimeSpan.Seconds;
|
||
}
|
||
else
|
||
{
|
||
hours = 0;
|
||
minutes = 0;
|
||
seconds = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hours = 0;
|
||
minutes = 0;
|
||
seconds = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hours = (int)timeSpan.TotalHours;
|
||
minutes = timeSpan.Minutes;
|
||
seconds = timeSpan.Seconds;
|
||
}
|
||
|
||
SetDigitDisplay("MinHour1Display", Math.Abs(hours / 10) % 10, shouldShowRed);
|
||
SetDigitDisplay("MinHour2Display", (hours % 10 + 10) % 10, shouldShowRed);
|
||
|
||
SetDigitDisplay("MinMinute1Display", minutes / 10, shouldShowRed);
|
||
SetDigitDisplay("MinMinute2Display", minutes % 10, shouldShowRed);
|
||
|
||
SetDigitDisplay("MinSecond1Display", seconds / 10, shouldShowRed);
|
||
SetDigitDisplay("MinSecond2Display", seconds % 10, shouldShowRed);
|
||
|
||
SetColonDisplay(shouldShowRed);
|
||
}
|
||
}
|
||
|
||
private void ParentWindow_TimerCompleted(object sender, EventArgs e)
|
||
{
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
this.Close();
|
||
});
|
||
}
|
||
|
||
private void SetDigitDisplay(string pathName, int digit, bool isRed = false)
|
||
{
|
||
var path = this.FindName(pathName) as Path;
|
||
if (path != null)
|
||
{
|
||
string resourceKey = $"Digit{digit}";
|
||
var geometry = this.FindResource(resourceKey) as Geometry;
|
||
if (geometry != null)
|
||
{
|
||
path.Data = geometry;
|
||
}
|
||
|
||
// 设置颜色
|
||
if (isRed)
|
||
{
|
||
path.Fill = Brushes.Red;
|
||
}
|
||
else
|
||
{
|
||
var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
|
||
if (defaultBrush != null)
|
||
{
|
||
path.Fill = defaultBrush;
|
||
}
|
||
else
|
||
{
|
||
bool isLightTheme = IsLightTheme();
|
||
path.Fill = isLightTheme ? Brushes.Black : Brushes.White;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置最小化窗口冒号显示颜色
|
||
/// </summary>
|
||
/// <param name="isRed">是否显示为红色</param>
|
||
private void SetColonDisplay(bool isRed = false)
|
||
{
|
||
var colon1 = this.FindName("MinColon1Display") as TextBlock;
|
||
var colon2 = this.FindName("MinColon2Display") as TextBlock;
|
||
|
||
if (colon1 != null)
|
||
{
|
||
if (isRed)
|
||
{
|
||
colon1.Foreground = Brushes.Red;
|
||
}
|
||
else
|
||
{
|
||
var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
|
||
if (defaultBrush != null)
|
||
{
|
||
colon1.Foreground = defaultBrush;
|
||
}
|
||
else
|
||
{
|
||
bool isLightTheme = IsLightTheme();
|
||
colon1.Foreground = isLightTheme ? Brushes.Black : Brushes.White;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (colon2 != null)
|
||
{
|
||
if (isRed)
|
||
{
|
||
colon2.Foreground = Brushes.Red;
|
||
}
|
||
else
|
||
{
|
||
var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
|
||
if (defaultBrush != null)
|
||
{
|
||
colon2.Foreground = defaultBrush;
|
||
}
|
||
else
|
||
{
|
||
bool isLightTheme = IsLightTheme();
|
||
colon2.Foreground = isLightTheme ? Brushes.Black : Brushes.White;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ApplyTheme()
|
||
{
|
||
try
|
||
{
|
||
|
||
bool isLightTheme = IsLightTheme();
|
||
if (!isLightTheme)
|
||
{
|
||
SetDarkThemeBorder();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"应用主题时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private bool IsLightTheme()
|
||
{
|
||
try
|
||
{
|
||
var mainWindow = Application.Current.MainWindow as MainWindow;
|
||
if (mainWindow != null)
|
||
{
|
||
var currentModeField = mainWindow.GetType().GetField("currentMode",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
if (currentModeField != null)
|
||
{
|
||
var currentMode = currentModeField.GetValue(mainWindow);
|
||
return currentMode?.ToString() == "Light";
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 如果获取主题失败,默认使用浅色主题
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 设置深色主题下的灰色边框
|
||
private void SetDarkThemeBorder()
|
||
{
|
||
try
|
||
{
|
||
// 找到Border元素并设置灰色边框
|
||
var border = this.FindName("MainBorder") as Border;
|
||
if (border != null)
|
||
{
|
||
border.BorderBrush = new SolidColorBrush(Color.FromRgb(64, 64, 64));
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
|
||
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||
{
|
||
// 记录点击时间
|
||
lastClickTime = DateTime.Now;
|
||
// 开始拖动
|
||
isDragging = true;
|
||
lastMousePosition = e.GetPosition(this);
|
||
this.CaptureMouse();
|
||
}
|
||
|
||
private void Window_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
if (isDragging)
|
||
{
|
||
var currentPosition = e.GetPosition(this);
|
||
var deltaX = currentPosition.X - lastMousePosition.X;
|
||
var deltaY = currentPosition.Y - lastMousePosition.Y;
|
||
|
||
this.Left += deltaX;
|
||
this.Top += deltaY;
|
||
}
|
||
}
|
||
|
||
private void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||
{
|
||
if (isDragging)
|
||
{
|
||
isDragging = false;
|
||
this.ReleaseMouseCapture();
|
||
|
||
var clickDuration = DateTime.Now - lastClickTime;
|
||
if (clickDuration.TotalMilliseconds < 200)
|
||
{
|
||
this.Close();
|
||
}
|
||
}
|
||
}
|
||
|
||
private DateTime lastClickTime = DateTime.Now;
|
||
|
||
private void Window_MouseEnter(object sender, MouseEventArgs e)
|
||
{
|
||
isMouseOver = true;
|
||
// 鼠标进入时显示关闭按钮
|
||
if (CloseButton != null)
|
||
{
|
||
CloseButton.Opacity = 1.0;
|
||
}
|
||
}
|
||
|
||
private void Window_MouseLeave(object sender, MouseEventArgs e)
|
||
{
|
||
isMouseOver = false;
|
||
// 鼠标离开时隐藏关闭按钮
|
||
if (CloseButton != null)
|
||
{
|
||
CloseButton.Opacity = 0.7;
|
||
}
|
||
}
|
||
|
||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (parentControl != null)
|
||
{
|
||
parentControl.StopTimer();
|
||
}
|
||
this.Close();
|
||
}
|
||
|
||
protected override void OnClosed(EventArgs e)
|
||
{
|
||
if (parentControl != null)
|
||
{
|
||
parentControl.TimerCompleted -= ParentWindow_TimerCompleted;
|
||
}
|
||
|
||
// 清理资源
|
||
if (updateTimer != null)
|
||
{
|
||
updateTimer.Stop();
|
||
updateTimer.Dispose();
|
||
}
|
||
base.OnClosed(e);
|
||
}
|
||
}
|
||
}
|