2025-08-31 11:43:52 +08:00
using Ink_Canvas.Helpers ;
using Ink_Canvas.Helpers.Plugins ;
using Ink_Canvas.Windows ;
using iNKORE.UI.WPF.Modern ;
using iNKORE.UI.WPF.Modern.Controls ;
using Microsoft.Win32 ;
2025-05-25 09:29:48 +08:00
using System ;
2025-07-28 14:40:44 +08:00
using System.Collections.Generic ;
using System.ComponentModel ;
using System.Diagnostics ;
2025-05-25 09:29:48 +08:00
using System.IO ;
2025-08-24 11:08:13 +08:00
using System.Linq ;
2025-07-28 14:40:44 +08:00
using System.Reflection ;
using System.Runtime.InteropServices ;
2025-10-18 18:26:13 +08:00
using System.Security.Principal ;
2025-07-28 14:40:44 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2025-05-25 09:29:48 +08:00
using System.Windows ;
using System.Windows.Controls ;
2025-09-06 13:16:10 +08:00
using System.Windows.Controls.Primitives ;
2025-07-28 14:40:44 +08:00
using System.Windows.Forms ;
2025-05-25 09:29:48 +08:00
using System.Windows.Ink ;
2025-07-28 14:40:44 +08:00
using System.Windows.Input ;
using System.Windows.Interop ;
2025-05-25 09:29:48 +08:00
using System.Windows.Media ;
2025-07-28 14:40:44 +08:00
using System.Windows.Threading ;
using Application = System . Windows . Application ;
2025-07-16 09:16:36 +08:00
using Brushes = System . Windows . Media . Brushes ;
2025-07-28 14:40:44 +08:00
using Button = System . Windows . Controls . Button ;
using Cursor = System . Windows . Input . Cursor ;
using Cursors = System . Windows . Input . Cursors ;
using DpiChangedEventArgs = System . Windows . DpiChangedEventArgs ;
2025-08-03 16:46:33 +08:00
using File = System . IO . File ;
2025-07-28 14:40:44 +08:00
using GroupBox = System . Windows . Controls . GroupBox ;
2025-12-20 13:56:46 +08:00
using HorizontalAlignment = System . Windows . HorizontalAlignment ;
2025-09-13 11:04:04 +08:00
using MessageBox = iNKORE . UI . WPF . Modern . Controls . MessageBox ;
2025-07-15 21:10:49 +08:00
using Point = System . Windows . Point ;
2025-12-20 13:56:46 +08:00
using VerticalAlignment = System . Windows . VerticalAlignment ;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
namespace Ink_Canvas
{
public partial class MainWindow : Window
{
2025-10-18 18:26:13 +08:00
[DllImport("UIAccessDLL_x86.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 PrepareUIAccessX86 ( ) ;
[DllImport("UIAccessDLL_x64.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 PrepareUIAccessX64 ( ) ;
2025-09-13 12:08:45 +08:00
// 每一页一个Canvas对象
2025-07-18 19:20:06 +08:00
private List < System . Windows . Controls . Canvas > whiteboardPages = new List < System . Windows . Controls . Canvas > ( ) ;
2025-07-28 14:40:44 +08:00
private int currentPageIndex ;
private System . Windows . Controls . Canvas currentCanvas ;
private AutoUpdateHelper . UpdateLineGroup AvailableLatestLineGroup ;
2025-08-31 11:43:52 +08:00
2025-08-23 21:39:00 +08:00
// 全局快捷键管理器
private GlobalHotkeyManager _globalHotkeyManager ;
2025-07-18 19:20:06 +08:00
2025-08-23 23:13:39 +08:00
// 墨迹渐隐管理器
private InkFadeManager _inkFadeManager ;
2025-09-13 15:34:50 +08:00
// 悬浮窗拦截管理器
private FloatingWindowInterceptorManager _floatingWindowInterceptorManager ;
2025-12-28 14:30:02 +08:00
// 窗口概览模型
private WindowOverviewModel _windowOverviewModel ;
2025-10-26 00:21:10 +08:00
2025-09-13 10:55:27 +08:00
// 设置面板相关状态
2025-10-18 18:29:52 +08:00
private bool isTemporarilyDisablingNoFocusMode = false ;
2025-12-20 13:56:46 +08:00
2025-12-20 13:03:31 +08:00
// 全屏处理状态标志
public bool isFullScreenApplied = false ;
2025-09-13 10:55:27 +08:00
2026-01-01 18:21:28 +08:00
private static Cursor _cachedPenCursor = null ;
private static readonly object _cursorLock = new object ( ) ;
2025-07-20 21:01:15 +08:00
2025-05-25 09:29:48 +08:00
#region Window Initialization
2025-08-03 16:46:33 +08:00
public MainWindow ( )
{
2025-05-25 09:29:48 +08:00
/*
处于画板模式内:Topmost == false / currentMode != 0
处于 PPT 放映内:BtnPPTSlideShowEnd.Visibility
*/
InitializeComponent ( ) ;
BlackboardLeftSide . Visibility = Visibility . Collapsed ;
BlackboardCenterSide . Visibility = Visibility . Collapsed ;
BlackboardRightSide . Visibility = Visibility . Collapsed ;
BorderTools . Visibility = Visibility . Collapsed ;
BorderSettings . Visibility = Visibility . Collapsed ;
LeftSidePanelForPPTNavigation . Visibility = Visibility . Collapsed ;
RightSidePanelForPPTNavigation . Visibility = Visibility . Collapsed ;
BorderSettings . Margin = new Thickness ( 0 , 0 , 0 , 0 ) ;
TwoFingerGestureBorder . Visibility = Visibility . Collapsed ;
BoardTwoFingerGestureBorder . Visibility = Visibility . Collapsed ;
BorderDrawShape . Visibility = Visibility . Collapsed ;
BoardBorderDrawShape . Visibility = Visibility . Collapsed ;
GridInkCanvasSelectionCover . Visibility = Visibility . Collapsed ;
//if (!App.StartArgs.Contains("-o"))
ViewBoxStackPanelMain . Visibility = Visibility . Collapsed ;
ViewBoxStackPanelShapes . Visibility = Visibility . Collapsed ;
2025-07-28 14:40:44 +08:00
var workingArea = Screen . PrimaryScreen . WorkingArea ;
2025-08-12 11:08:24 +08:00
// 考虑快捷调色盘的宽度,确保浮动栏有足够空间
double floatingBarWidth = 284 ; // 基础宽度
if ( Settings . Appearance . IsShowQuickColorPalette )
{
2025-08-12 12:19:55 +08:00
// 根据显示模式调整宽度
if ( Settings . Appearance . QuickColorPaletteDisplayMode = = 0 )
{
// 单行显示模式,自适应宽度,但需要足够空间显示6个颜色
floatingBarWidth = Math . Max ( floatingBarWidth , 120 ) ;
}
else
{
// 双行显示模式,宽度较大
floatingBarWidth = Math . Max ( floatingBarWidth , 820 ) ;
}
2025-08-12 11:08:24 +08:00
}
2025-06-06 16:09:28 +08:00
ViewboxFloatingBar . Margin = new Thickness (
2025-08-12 11:08:24 +08:00
( workingArea . Width - floatingBarWidth ) / 2 ,
2025-06-06 16:09:28 +08:00
workingArea . Bottom - 60 - workingArea . Top ,
- 2000 , - 200 ) ;
2025-08-31 09:59:49 +08:00
// 新增:只在屏幕模式下初始化浮动栏动画
if ( currentMode = = 0 )
{
ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
if ( File . Exists ( "debug.ini" ) ) Label . Visibility = Visibility . Visible ;
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
try
{
if ( File . Exists ( "Log.txt" ) )
{
2025-05-25 09:29:48 +08:00
var fileInfo = new FileInfo ( "Log.txt" ) ;
var fileSizeInKB = fileInfo . Length / 1024 ;
if ( fileSizeInKB > 512 )
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
File . Delete ( "Log.txt" ) ;
LogHelper . WriteLogToFile (
"The Log.txt file has been successfully deleted. Original file size: " + fileSizeInKB +
2025-07-28 14:40:44 +08:00
" KB" ) ;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile (
ex + " | Can not delete the Log.txt file. File size: " + fileSizeInKB + " KB" ,
LogHelper . LogType . Error ) ;
}
}
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
InitTimers ( ) ;
timeMachine . OnRedoStateChanged + = TimeMachine_OnRedoStateChanged ;
timeMachine . OnUndoStateChanged + = TimeMachine_OnUndoStateChanged ;
inkCanvas . Strokes . StrokesChanged + = StrokesOnStrokesChanged ;
2025-07-28 14:40:44 +08:00
SystemEvents . UserPreferenceChanged + = SystemEvents_UserPreferenceChanged ;
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
if ( File . Exists ( "SpecialVersion.ini" ) ) SpecialVersionResetToSuggestion_Click ( ) ;
}
2025-08-03 16:46:33 +08:00
catch ( Exception ex )
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( ex . ToString ( ) , LogHelper . LogType . Error ) ;
}
CheckColorTheme ( true ) ;
CheckPenTypeUIState ( ) ;
2025-06-12 11:21:45 +08:00
2025-07-26 19:03:07 +08:00
// 初始化墨迹平滑管理器
2025-07-28 14:40:44 +08:00
_inkSmoothingManager = new InkSmoothingManager ( Dispatcher ) ;
2025-07-26 19:03:07 +08:00
2025-08-23 23:13:39 +08:00
// 初始化墨迹渐隐管理器
_inkFadeManager = new InkFadeManager ( this ) ;
2025-06-12 11:21:45 +08:00
// 注册输入事件
inkCanvas . PreviewMouseDown + = inkCanvas_PreviewMouseDown ;
inkCanvas . StylusDown + = inkCanvas_StylusDown ;
2025-07-30 19:56:22 +08:00
inkCanvas . MouseRightButtonUp + = InkCanvas_MouseRightButtonUp ;
2025-07-18 19:20:06 +08:00
// 初始化第一页Canvas
var firstCanvas = new System . Windows . Controls . Canvas ( ) ;
whiteboardPages . Add ( firstCanvas ) ;
InkCanvasGridForInkReplay . Children . Add ( firstCanvas ) ;
currentPageIndex = 0 ;
ShowPage ( currentPageIndex ) ;
2025-07-21 10:14:21 +08:00
// 手动实现触摸滑动
2026-02-15 01:57:12 +08:00
const double TouchTapMovementThreshold = 15.0 ;
2025-07-21 10:14:21 +08:00
double leftTouchStartY = 0 ;
2026-02-15 01:57:12 +08:00
double leftTouchStartX = 0 ;
2025-07-21 10:14:21 +08:00
double leftScrollStartOffset = 0 ;
bool leftIsTouching = false ;
2026-02-15 01:57:12 +08:00
bool leftTouchDidScroll = false ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchDown + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
leftIsTouching = true ;
2026-02-15 01:57:12 +08:00
leftTouchDidScroll = false ;
var pt = e . GetTouchPoint ( BlackBoardLeftSidePageListScrollViewer ) . Position ;
leftTouchStartX = pt . X ;
leftTouchStartY = pt . Y ;
2025-07-21 10:14:21 +08:00
leftScrollStartOffset = BlackBoardLeftSidePageListScrollViewer . VerticalOffset ;
BlackBoardLeftSidePageListScrollViewer . CaptureTouch ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchMove + = ( s , e ) = >
{
if ( leftIsTouching )
{
2026-02-15 01:57:12 +08:00
var pt = e . GetTouchPoint ( BlackBoardLeftSidePageListScrollViewer ) . Position ;
double deltaY = leftTouchStartY - pt . Y ;
double deltaX = pt . X - leftTouchStartX ;
if ( ! leftTouchDidScroll & & ( Math . Abs ( deltaY ) > TouchTapMovementThreshold | | Math . Abs ( deltaX ) > TouchTapMovementThreshold ) )
leftTouchDidScroll = true ;
if ( leftTouchDidScroll )
BlackBoardLeftSidePageListScrollViewer . ScrollToVerticalOffset ( leftScrollStartOffset + deltaY ) ;
2025-07-21 10:14:21 +08:00
e . Handled = true ;
}
} ;
2025-08-03 16:46:33 +08:00
BlackBoardLeftSidePageListScrollViewer . TouchUp + = ( s , e ) = >
{
2026-02-15 01:57:12 +08:00
if ( leftIsTouching & & ! leftTouchDidScroll )
{
var pt = e . GetTouchPoint ( BlackBoardLeftSidePageListScrollViewer ) . Position ;
double dx = pt . X - leftTouchStartX , dy = pt . Y - leftTouchStartY ;
if ( dx * dx + dy * dy < = TouchTapMovementThreshold * TouchTapMovementThreshold )
2026-02-18 22:19:15 +08:00
TrySwitchWhiteboardPageByTouchPoint ( BlackBoardLeftSidePageListView , BlackBoardLeftSidePageListScrollViewer , pt ) ;
2026-02-15 01:57:12 +08:00
}
2025-07-21 10:14:21 +08:00
leftIsTouching = false ;
2026-02-15 01:57:12 +08:00
leftTouchDidScroll = false ;
2025-07-21 10:14:21 +08:00
BlackBoardLeftSidePageListScrollViewer . ReleaseTouchCapture ( e . TouchDevice ) ;
e . Handled = true ;
} ;
double rightTouchStartY = 0 ;
2026-02-15 01:57:12 +08:00
double rightTouchStartX = 0 ;
2025-07-21 10:14:21 +08:00
double rightScrollStartOffset = 0 ;
bool rightIsTouching = false ;
2026-02-15 01:57:12 +08:00
bool rightTouchDidScroll = false ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchDown + = ( s , e ) = >
{
2025-07-21 10:14:21 +08:00
rightIsTouching = true ;
2026-02-15 01:57:12 +08:00
rightTouchDidScroll = false ;
var pt = e . GetTouchPoint ( BlackBoardRightSidePageListScrollViewer ) . Position ;
rightTouchStartX = pt . X ;
rightTouchStartY = pt . Y ;
2025-07-21 10:14:21 +08:00
rightScrollStartOffset = BlackBoardRightSidePageListScrollViewer . VerticalOffset ;
BlackBoardRightSidePageListScrollViewer . CaptureTouch ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchMove + = ( s , e ) = >
{
if ( rightIsTouching )
{
2026-02-15 01:57:12 +08:00
var pt = e . GetTouchPoint ( BlackBoardRightSidePageListScrollViewer ) . Position ;
double deltaY = rightTouchStartY - pt . Y ;
double deltaX = pt . X - rightTouchStartX ;
if ( ! rightTouchDidScroll & & ( Math . Abs ( deltaY ) > TouchTapMovementThreshold | | Math . Abs ( deltaX ) > TouchTapMovementThreshold ) )
rightTouchDidScroll = true ;
if ( rightTouchDidScroll )
BlackBoardRightSidePageListScrollViewer . ScrollToVerticalOffset ( rightScrollStartOffset + deltaY ) ;
2025-07-21 10:14:21 +08:00
e . Handled = true ;
}
} ;
2025-08-03 16:46:33 +08:00
BlackBoardRightSidePageListScrollViewer . TouchUp + = ( s , e ) = >
{
2026-02-15 01:57:12 +08:00
if ( rightIsTouching & & ! rightTouchDidScroll )
{
var pt = e . GetTouchPoint ( BlackBoardRightSidePageListScrollViewer ) . Position ;
double dx = pt . X - rightTouchStartX , dy = pt . Y - rightTouchStartY ;
if ( dx * dx + dy * dy < = TouchTapMovementThreshold * TouchTapMovementThreshold )
2026-02-18 22:19:15 +08:00
TrySwitchWhiteboardPageByTouchPoint ( BlackBoardRightSidePageListView , BlackBoardRightSidePageListScrollViewer , pt ) ;
2026-02-15 01:57:12 +08:00
}
2025-07-21 10:14:21 +08:00
rightIsTouching = false ;
2026-02-15 01:57:12 +08:00
rightTouchDidScroll = false ;
2025-07-21 10:14:21 +08:00
BlackBoardRightSidePageListScrollViewer . ReleaseTouchCapture ( e . TouchDevice ) ;
e . Handled = true ;
} ;
2025-07-25 18:21:16 +08:00
// 初始化无焦点模式开关
ToggleSwitchNoFocusMode . IsOn = Settings . Advanced . IsNoFocusMode ;
2025-11-08 23:09:04 +08:00
ApplyNoFocusMode ( ) ;
2025-08-13 11:54:36 +08:00
// 初始化窗口置顶开关
ToggleSwitchAlwaysOnTop . IsOn = Settings . Advanced . IsAlwaysOnTop ;
2025-11-08 23:09:04 +08:00
ApplyAlwaysOnTop ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
// 添加窗口激活事件处理,确保置顶状态在窗口重新激活时得到保持
2025-08-30 19:40:14 +08:00
Activated + = Window_Activated ;
Deactivated + = Window_Deactivated ;
2025-08-31 11:43:52 +08:00
2025-09-06 13:16:10 +08:00
// 为滑块控件添加触摸事件支持
AddTouchSupportToSliders ( ) ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
// 初始化计时器控件事件
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
if ( TimerControl ! = null )
{
TimerControl . ShowMinimizedRequested + = TimerControl_ShowMinimizedRequested ;
TimerControl . HideMinimizedRequested + = TimerControl_HideMinimizedRequested ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( MinimizedTimerControl ! = null & & TimerControl ! = null )
{
MinimizedTimerControl . SetParentControl ( TimerControl ) ;
}
2026-02-20 14:20:25 +08:00
CheckAndShowOobe ( ) ;
2025-11-29 16:27:35 +08:00
} ) , DispatcherPriority . Loaded ) ;
}
2025-12-20 13:56:46 +08:00
2026-02-20 14:20:25 +08:00
private void CheckAndShowOobe ( )
{
try
{
if ( Settings ? . Startup ? . HasShownOobe = = false )
{
var oobeTimer = new DispatcherTimer ( DispatcherPriority . Loaded , Dispatcher )
{
Interval = TimeSpan . FromMilliseconds ( 500 )
} ;
oobeTimer . Tick + = ( s , e ) = >
{
oobeTimer . Stop ( ) ;
oobeTimer = null ;
try
{
if ( ViewboxFloatingBar ! = null )
{
ViewboxFloatingBar . Visibility = Visibility . Collapsed ;
}
var oobeWindow = new OobeWindow ( Settings ) ;
oobeWindow . Owner = this ;
oobeWindow . ShowDialog ( ) ;
OnOobeCompleted ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"显示 OOBE 时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
if ( ViewboxFloatingBar ! = null )
{
ViewboxFloatingBar . Visibility = Visibility . Visible ;
}
}
} ;
oobeTimer . Start ( ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检查 OOBE 时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
private void OnOobeCompleted ( )
{
try
{
if ( Settings ? . Startup ! = null )
{
Settings . Startup . HasShownOobe = true ;
SaveSettingsToFile ( ) ;
}
if ( ViewboxFloatingBar ! = null & & currentMode = = 0 )
{
ViewboxFloatingBar . Visibility = Visibility . Visible ;
ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
LogHelper . WriteLogToFile ( "OOBE 已完成" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"完成 OOBE 时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-11-29 16:27:35 +08:00
private void TimerControl_ShowMinimizedRequested ( object sender , EventArgs e )
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
var minimizedContainer = FindName ( "MinimizedTimerContainer" ) as FrameworkElement ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( timerContainer ! = null & & minimizedContainer ! = null )
{
double x = 0 , y = 0 ;
2025-12-20 13:56:46 +08:00
if ( timerContainer . HorizontalAlignment = = HorizontalAlignment . Center & &
2025-11-29 16:27:35 +08:00
timerContainer . VerticalAlignment = = VerticalAlignment . Center )
{
var timerPoint = timerContainer . TransformToAncestor ( this ) . Transform ( new Point ( 0 , 0 ) ) ;
x = timerPoint . X ;
y = timerPoint . Y ;
}
else
{
var timerMargin = timerContainer . Margin ;
x = double . IsNaN ( timerMargin . Left ) ? 0 : timerMargin . Left ;
y = double . IsNaN ( timerMargin . Top ) ? 0 : timerMargin . Top ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
minimizedContainer . Margin = new Thickness ( x , y , 0 , 0 ) ;
minimizedContainer . HorizontalAlignment = HorizontalAlignment . Left ;
minimizedContainer . VerticalAlignment = VerticalAlignment . Top ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
timerContainer . Margin = new Thickness ( x , y , 0 , 0 ) ;
timerContainer . HorizontalAlignment = HorizontalAlignment . Left ;
timerContainer . VerticalAlignment = VerticalAlignment . Top ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
timerContainer . Visibility = Visibility . Collapsed ;
minimizedContainer . Visibility = Visibility . Visible ;
}
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
private void TimerControl_HideMinimizedRequested ( object sender , EventArgs e )
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
var minimizedContainer = FindName ( "MinimizedTimerContainer" ) as FrameworkElement ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( timerContainer ! = null & & minimizedContainer ! = null )
{
minimizedContainer . Visibility = Visibility . Collapsed ;
timerContainer . Visibility = Visibility . Visible ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
if ( TimerControl ! = null )
{
TimerControl . UpdateActivityTime ( ) ;
}
}
2025-05-25 09:29:48 +08:00
}
2025-12-27 22:57:22 +08:00
/// <summary>
/// 根据DPI缩放因子调整TimerContainer的尺寸
/// </summary>
2026-01-11 00:14:44 +08:00
public void AdjustTimerContainerSize ( )
2025-12-27 22:57:22 +08:00
{
try
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
if ( timerContainer = = null ) return ;
var source = System . Windows . PresentationSource . FromVisual ( this ) ;
if ( source ! = null )
{
var dpiScaleX = source . CompositionTarget . TransformToDevice . M11 ;
var dpiScaleY = source . CompositionTarget . TransformToDevice . M22 ;
// 如果DPI缩放因子大于1.25,则适当缩小容器尺寸
// 这样可以确保在高DPI屏幕上,计时器窗口的物理像素大小不会过大
if ( dpiScaleX > 1.25 | | dpiScaleY > 1.25 )
{
// 使用较小的缩放因子来限制最大尺寸
double scaleFactor = Math . Min ( dpiScaleX , dpiScaleY ) ;
// 计算目标物理像素大小(约1350x750物理像素)
// 然后转换为逻辑像素
double targetPhysicalWidth = 1350 ;
double targetPhysicalHeight = 750 ;
// 转换为逻辑像素
double maxWidth = targetPhysicalWidth / scaleFactor ;
double maxHeight = targetPhysicalHeight / scaleFactor ;
// 确保不会小于原始尺寸的70%
maxWidth = Math . Max ( maxWidth , 900 * 0.7 ) ;
maxHeight = Math . Max ( maxHeight , 500 * 0.7 ) ;
// 应用调整后的尺寸
timerContainer . Width = maxWidth ;
timerContainer . Height = maxHeight ;
}
else
{
// 标准DPI,使用原始尺寸
timerContainer . Width = 900 ;
timerContainer . Height = 500 ;
}
}
else
{
// 无法获取DPI信息,使用原始尺寸
timerContainer . Width = 900 ;
timerContainer . Height = 500 ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"调整TimerContainer尺寸失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-07-25 18:12:46 +08:00
2025-07-20 21:03:42 +08:00
2025-05-25 09:29:48 +08:00
#endregion
#region Ink Canvas Functions
2025-07-28 14:40:44 +08:00
private Color Ink_DefaultColor = Colors . Red ;
2025-05-25 09:29:48 +08:00
private DrawingAttributes drawingAttributes ;
2025-07-28 14:40:44 +08:00
private InkSmoothingManager _inkSmoothingManager ;
2025-05-25 09:29:48 +08:00
2026-02-14 00:23:05 +08:00
private DispatcherTimer _brushAutoRestoreTimer ;
2026-02-14 13:12:39 +08:00
private bool _isBoardBrushMode ;
private double _savedInkWidthBeforeBoardBrush = 5 ;
2025-08-03 16:46:33 +08:00
private void loadPenCanvas ( )
{
try
{
2025-05-25 09:29:48 +08:00
//drawingAttributes = new DrawingAttributes();
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
drawingAttributes . Color = Ink_DefaultColor ;
drawingAttributes . Height = 2.5 ;
drawingAttributes . Width = 2.5 ;
drawingAttributes . IsHighlighter = false ;
2025-07-20 15:21:59 +08:00
// 默认使用高级贝塞尔曲线平滑,如果未启用则使用原来的FitToCurve
if ( Settings . Canvas . UseAdvancedBezierSmoothing )
{
drawingAttributes . FitToCurve = false ;
}
else
{
drawingAttributes . FitToCurve = Settings . Canvas . FitToCurve ;
}
2025-05-25 09:29:48 +08:00
inkCanvas . Gesture + = InkCanvas_Gesture ;
}
catch { }
}
2026-02-14 00:23:05 +08:00
2026-02-14 09:35:16 +08:00
private static Color GetCanonicalPaletteColorFromHex ( string hex , byte alpha )
{
if ( string . IsNullOrWhiteSpace ( hex ) ) return Color . FromArgb ( alpha , 255 , 0 , 0 ) ;
string n = hex . Trim ( ) . ToLowerInvariant ( ) ;
if ( n . StartsWith ( "#" ) ) n = n . Substring ( 1 ) ;
if ( n . Length = = 8 ) n = n . Substring ( 2 , 6 ) ; // 去掉 AA
else if ( n . Length ! = 6 ) n = "" ;
if ( n . Length = = 6 )
{
if ( n = = "ffffff" ) return Color . FromArgb ( alpha , 255 , 255 , 255 ) ;
if ( n = = "fb9650" ) return Color . FromArgb ( alpha , 251 , 150 , 80 ) ; // 251,150,80 橙
if ( n = = "ffff00" ) return Color . FromArgb ( alpha , 255 , 255 , 0 ) ;
if ( n = = "000000" ) return Color . FromArgb ( alpha , 0 , 0 , 0 ) ;
if ( n = = "2563eb" ) return Color . FromArgb ( alpha , 37 , 99 , 235 ) ; // 37,99,235 蓝
if ( n = = "ff0000" ) return Color . FromArgb ( alpha , 255 , 0 , 0 ) ;
if ( n = = "16a34a" ) return Color . FromArgb ( alpha , 22 , 163 , 74 ) ; // 22,163,74 绿
if ( n = = "9333ea" ) return Color . FromArgb ( alpha , 147 , 51 , 234 ) ; // 147,51,234 紫
}
try
{
var converted = ColorConverter . ConvertFromString ( hex ) ;
if ( converted is Color parsed )
{
byte r = parsed . R , g = parsed . G , b = parsed . B ;
if ( r = = 255 & & g = = 255 & & b = = 255 ) return Color . FromArgb ( alpha , 255 , 255 , 255 ) ;
if ( r = = 251 & & g = = 150 & & b = = 80 ) return Color . FromArgb ( alpha , 251 , 150 , 80 ) ;
if ( r = = 255 & & g = = 255 & & b = = 0 ) return Color . FromArgb ( alpha , 255 , 255 , 0 ) ;
if ( r = = 0 & & g = = 0 & & b = = 0 ) return Color . FromArgb ( alpha , 0 , 0 , 0 ) ;
if ( r = = 37 & & g = = 99 & & b = = 235 ) return Color . FromArgb ( alpha , 37 , 99 , 235 ) ;
if ( r = = 255 & & g = = 0 & & b = = 0 ) return Color . FromArgb ( alpha , 255 , 0 , 0 ) ;
if ( r = = 22 & & g = = 163 & & b = = 74 ) return Color . FromArgb ( alpha , 22 , 163 , 74 ) ;
if ( r = = 147 & & g = = 51 & & b = = 234 ) return Color . FromArgb ( alpha , 147 , 51 , 234 ) ;
return Color . FromArgb ( alpha , r , g , b ) ;
}
}
catch { }
return Color . FromArgb ( alpha , 255 , 0 , 0 ) ;
}
2026-02-14 09:13:24 +08:00
/// <param name="color">目标颜色</param>
2026-02-14 00:23:05 +08:00
/// <param name="width">目标粗细</param>
2026-02-14 09:13:24 +08:00
/// <param name="height">目标高度</param>
2026-02-14 00:23:05 +08:00
private void SetBrushAttributesDirectly ( Color color , double width , double height )
{
try
{
2026-02-14 09:13:24 +08:00
if ( ! Dispatcher . CheckAccess ( ) )
{
Dispatcher . Invoke ( ( ) = > SetBrushAttributesDirectly ( color , width , height ) ) ;
return ;
}
2026-02-14 00:23:05 +08:00
if ( drawingAttributes = = null )
{
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
}
2026-02-14 12:35:08 +08:00
Color rgbColor = Color . FromRgb ( color . R , color . G , color . B ) ;
if ( currentMode = = 0 )
{
if ( rgbColor = = Colors . White ) lastDesktopInkColor = 5 ;
else if ( rgbColor = = Color . FromRgb ( 251 , 150 , 80 ) ) lastDesktopInkColor = 8 ;
else if ( rgbColor = = Colors . Yellow ) lastDesktopInkColor = 4 ;
else if ( rgbColor = = Colors . Black ) lastDesktopInkColor = 0 ;
else if ( rgbColor = = Color . FromRgb ( 37 , 99 , 235 ) ) lastDesktopInkColor = 3 ;
else if ( rgbColor = = Colors . Red ) lastDesktopInkColor = 1 ;
else if ( rgbColor = = Colors . Green | | rgbColor = = Color . FromRgb ( 22 , 163 , 74 ) ) lastDesktopInkColor = 2 ;
else if ( rgbColor = = Color . FromRgb ( 147 , 51 , 234 ) ) lastDesktopInkColor = 6 ;
}
else
{
if ( rgbColor = = Colors . White ) lastBoardInkColor = 5 ;
else if ( rgbColor = = Color . FromRgb ( 251 , 150 , 80 ) ) lastBoardInkColor = 8 ;
else if ( rgbColor = = Colors . Yellow ) lastBoardInkColor = 4 ;
else if ( rgbColor = = Colors . Black ) lastBoardInkColor = 0 ;
else if ( rgbColor = = Color . FromRgb ( 37 , 99 , 235 ) ) lastBoardInkColor = 3 ;
else if ( rgbColor = = Colors . Red ) lastBoardInkColor = 1 ;
else if ( rgbColor = = Colors . Green | | rgbColor = = Color . FromRgb ( 22 , 163 , 74 ) ) lastBoardInkColor = 2 ;
else if ( rgbColor = = Color . FromRgb ( 147 , 51 , 234 ) ) lastBoardInkColor = 6 ;
}
2026-02-18 22:13:55 +08:00
var colorWithAlpha = Color . FromArgb ( color . A , color . R , color . G , color . B ) ;
2026-02-14 12:35:08 +08:00
drawingAttributes . Color = colorWithAlpha ;
inkCanvas . DefaultDrawingAttributes . Color = colorWithAlpha ;
CheckColorTheme ( ) ;
2026-02-14 00:23:05 +08:00
2026-02-14 12:35:08 +08:00
Ink_DefaultColor = inkCanvas . DefaultDrawingAttributes . Color ;
2026-02-14 00:23:05 +08:00
2026-02-14 12:35:08 +08:00
// 粗细与透明度
if ( penType ! = 1 )
2026-02-14 09:13:24 +08:00
{
drawingAttributes . Width = width ;
drawingAttributes . Height = height ;
inkCanvas . DefaultDrawingAttributes . Width = width ;
inkCanvas . DefaultDrawingAttributes . Height = height ;
}
if ( Settings ? . Canvas ! = null )
{
Settings . Canvas . InkWidth = width ;
2026-02-14 12:35:08 +08:00
Settings . Canvas . InkAlpha = ( int ) color . A ;
2026-02-14 09:13:24 +08:00
}
2026-02-14 12:35:08 +08:00
if ( InkWidthSlider ! = null ) InkWidthSlider . Value = width * 2 ;
if ( InkAlphaSlider ! = null ) InkAlphaSlider . Value = color . A ;
if ( BoardInkWidthSlider ! = null ) BoardInkWidthSlider . Value = width * 2 ;
if ( BoardInkAlphaSlider ! = null ) BoardInkAlphaSlider . Value = color . A ;
2026-02-14 09:35:16 +08:00
if ( penType ! = 1 )
{
drawingAttributes . Width = width ;
drawingAttributes . Height = height ;
inkCanvas . DefaultDrawingAttributes . Width = width ;
inkCanvas . DefaultDrawingAttributes . Height = height ;
}
2026-02-14 00:23:05 +08:00
}
catch ( Exception ex )
{
2026-02-14 12:49:39 +08:00
LogHelper . WriteLogToFile ( $"SetBrushAttributesDirectly: {ex.Message}" , LogHelper . LogType . Error ) ;
2026-02-14 00:23:05 +08:00
}
}
2026-02-15 01:43:46 +08:00
private const double BoardBrushInkWidth = 16 ;
private const double BoardBrushInkHeight = 50 ;
2026-02-14 13:12:39 +08:00
2026-02-14 13:47:44 +08:00
private void BoardBrushModeButton_Click ( object sender , RoutedEventArgs e )
{
_isBoardBrushMode = ! _isBoardBrushMode ;
try
{
if ( drawingAttributes = = null )
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
if ( penType = = 1 ) return ;
if ( _isBoardBrushMode )
{
_savedInkWidthBeforeBoardBrush = InkWidthSlider ! = null ? InkWidthSlider . Value / 2.0 : drawingAttributes . Width ;
if ( _savedInkWidthBeforeBoardBrush < 0.5 ) _savedInkWidthBeforeBoardBrush = 2.5 ;
drawingAttributes . Width = BoardBrushInkWidth ;
2026-02-15 01:43:46 +08:00
drawingAttributes . Height = BoardBrushInkHeight ;
2026-02-14 13:47:44 +08:00
inkCanvas . DefaultDrawingAttributes . Width = BoardBrushInkWidth ;
2026-02-15 01:43:46 +08:00
inkCanvas . DefaultDrawingAttributes . Height = BoardBrushInkHeight ;
drawingAttributes . StylusTip = StylusTip . Rectangle ;
inkCanvas . DefaultDrawingAttributes . StylusTip = StylusTip . Rectangle ;
2026-02-14 13:47:44 +08:00
drawingAttributes . IgnorePressure = true ;
inkCanvas . DefaultDrawingAttributes . IgnorePressure = true ;
if ( BoardBrushModeButton ! = null )
BoardBrushModeButton . Background = new SolidColorBrush ( Color . FromRgb ( 37 , 99 , 235 ) ) ;
}
else
{
double w = InkWidthSlider ! = null ? InkWidthSlider . Value / 2.0 : _savedInkWidthBeforeBoardBrush ;
if ( w < 0.5 ) w = 2.5 ;
drawingAttributes . Width = w ;
drawingAttributes . Height = w ;
inkCanvas . DefaultDrawingAttributes . Width = w ;
inkCanvas . DefaultDrawingAttributes . Height = w ;
2026-02-15 01:43:46 +08:00
drawingAttributes . StylusTip = StylusTip . Ellipse ;
inkCanvas . DefaultDrawingAttributes . StylusTip = StylusTip . Ellipse ;
2026-02-14 13:47:44 +08:00
drawingAttributes . IgnorePressure = Settings . Canvas . DisablePressure ;
inkCanvas . DefaultDrawingAttributes . IgnorePressure = Settings . Canvas . DisablePressure ;
if ( BoardInkWidthSlider ! = null ) BoardInkWidthSlider . Value = w * 2 ;
if ( Settings ? . Canvas ! = null ) Settings . Canvas . InkWidth = w ;
if ( BoardBrushModeButton ! = null )
BoardBrushModeButton . ClearValue ( BackgroundProperty ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"BoardBrushModeButton_Click: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2026-02-14 13:12:39 +08:00
private void BoardBrushModeButton_MouseUp ( object sender , MouseButtonEventArgs e )
{
if ( sender ! = BoardBrushModeButton ) return ;
if ( lastBorderMouseDownObject ! = BoardBrushModeButton ) return ;
_isBoardBrushMode = ! _isBoardBrushMode ;
try
{
if ( drawingAttributes = = null )
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
if ( penType = = 1 ) return ;
if ( _isBoardBrushMode )
{
_savedInkWidthBeforeBoardBrush = InkWidthSlider ! = null ? InkWidthSlider . Value / 2.0 : drawingAttributes . Width ;
if ( _savedInkWidthBeforeBoardBrush < 0.5 ) _savedInkWidthBeforeBoardBrush = 2.5 ;
drawingAttributes . Width = BoardBrushInkWidth ;
2026-02-15 01:43:46 +08:00
drawingAttributes . Height = BoardBrushInkHeight ;
2026-02-14 13:12:39 +08:00
inkCanvas . DefaultDrawingAttributes . Width = BoardBrushInkWidth ;
2026-02-15 01:43:46 +08:00
inkCanvas . DefaultDrawingAttributes . Height = BoardBrushInkHeight ;
drawingAttributes . StylusTip = StylusTip . Rectangle ;
inkCanvas . DefaultDrawingAttributes . StylusTip = StylusTip . Rectangle ;
2026-02-14 13:12:39 +08:00
drawingAttributes . IgnorePressure = true ;
inkCanvas . DefaultDrawingAttributes . IgnorePressure = true ;
if ( BoardBrushModeButton ! = null )
BoardBrushModeButton . Background = new SolidColorBrush ( Color . FromRgb ( 37 , 99 , 235 ) ) ;
}
else
{
double w = InkWidthSlider ! = null ? InkWidthSlider . Value / 2.0 : _savedInkWidthBeforeBoardBrush ;
if ( w < 0.5 ) w = 2.5 ;
drawingAttributes . Width = w ;
drawingAttributes . Height = w ;
inkCanvas . DefaultDrawingAttributes . Width = w ;
inkCanvas . DefaultDrawingAttributes . Height = w ;
2026-02-15 01:43:46 +08:00
drawingAttributes . StylusTip = StylusTip . Ellipse ;
inkCanvas . DefaultDrawingAttributes . StylusTip = StylusTip . Ellipse ;
2026-02-14 13:12:39 +08:00
drawingAttributes . IgnorePressure = Settings . Canvas . DisablePressure ;
inkCanvas . DefaultDrawingAttributes . IgnorePressure = Settings . Canvas . DisablePressure ;
if ( BoardInkWidthSlider ! = null ) BoardInkWidthSlider . Value = w * 2 ;
if ( Settings ? . Canvas ! = null ) Settings . Canvas . InkWidth = w ;
if ( BoardBrushModeButton ! = null )
BoardBrushModeButton . ClearValue ( BackgroundProperty ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"BoardBrushModeButton_MouseUp: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2026-02-14 00:23:05 +08:00
private void InitBrushAutoRestoreTimer ( )
{
if ( _brushAutoRestoreTimer = = null )
{
_brushAutoRestoreTimer = new DispatcherTimer ( ) ;
_brushAutoRestoreTimer . Tick + = BrushAutoRestoreTimer_Tick ;
}
UpdateBrushAutoRestoreTimerInterval ( ) ;
}
private void UpdateBrushAutoRestoreTimerInterval ( )
{
if ( _brushAutoRestoreTimer = = null ) return ;
TimeSpan ? nextInterval = null ;
try
{
var timesConfig = Settings ? . Canvas ? . BrushAutoRestoreTimes ;
if ( ! string . IsNullOrWhiteSpace ( timesConfig ) )
{
var parts = timesConfig
. Split ( new [ ] { ';' , '; ' , ',' , ', ' } , StringSplitOptions . RemoveEmptyEntries )
. Select ( p = > p . Trim ( ) )
. ToList ( ) ;
var validTimes = new List < TimeSpan > ( ) ;
foreach ( var part in parts )
{
if ( TimeSpan . TryParse ( part , out var ts ) & &
ts > = TimeSpan . Zero & &
ts < TimeSpan . FromDays ( 1 ) )
{
validTimes . Add ( ts ) ;
}
}
if ( validTimes . Count > 0 )
{
var now = DateTime . Now ;
var today = now . Date ;
var nowTod = now . TimeOfDay ;
TimeSpan ? todayNext = null ;
foreach ( var t in validTimes )
{
if ( t > = nowTod )
{
if ( todayNext = = null | | t < todayNext . Value )
{
todayNext = t ;
}
}
}
DateTime target ;
if ( todayNext . HasValue )
{
target = today + todayNext . Value ;
}
else
{
var firstTime = validTimes . OrderBy ( t = > t ) . First ( ) ;
target = today . AddDays ( 1 ) + firstTime ;
}
var interval = target - now ;
if ( interval < TimeSpan . FromSeconds ( 1 ) )
{
interval = TimeSpan . FromSeconds ( 1 ) ;
}
nextInterval = interval ;
}
}
}
2026-02-14 12:49:39 +08:00
catch { }
2026-02-14 00:23:05 +08:00
if ( ! nextInterval . HasValue )
{
int seconds = Settings ? . Canvas ? . BrushAutoRestoreDelaySeconds ? ? 0 ;
if ( seconds < 1 ) seconds = 1 ;
nextInterval = TimeSpan . FromSeconds ( seconds ) ;
}
_brushAutoRestoreTimer . Interval = nextInterval . Value ;
}
internal void ScheduleBrushAutoRestore ( )
{
try
{
if ( Settings = = null | | Settings . Canvas = = null | | ! Settings . Canvas . EnableBrushAutoRestore )
{
return ;
}
if ( _brushAutoRestoreTimer = = null )
{
InitBrushAutoRestoreTimer ( ) ;
}
else
{
UpdateBrushAutoRestoreTimerInterval ( ) ;
}
_brushAutoRestoreTimer . Stop ( ) ;
_brushAutoRestoreTimer . Start ( ) ;
}
catch ( Exception ex )
{
2026-02-14 12:49:39 +08:00
LogHelper . WriteLogToFile ( $"ScheduleBrushAutoRestore: {ex.Message}" , LogHelper . LogType . Error ) ;
2026-02-14 00:23:05 +08:00
}
}
private void BrushAutoRestoreTimer_Tick ( object sender , EventArgs e )
{
try
{
_brushAutoRestoreTimer . Stop ( ) ;
if ( Settings = = null | | Settings . Canvas = = null | | ! Settings . Canvas . EnableBrushAutoRestore )
{
return ;
}
if ( drawingAttributes = = null )
{
drawingAttributes = inkCanvas . DefaultDrawingAttributes ;
}
int alphaConfig = Settings . Canvas . BrushAutoRestoreAlpha ;
if ( alphaConfig < 0 ) alphaConfig = 0 ;
if ( alphaConfig > 255 ) alphaConfig = 255 ;
2026-02-14 09:35:16 +08:00
byte alpha = ( byte ) alphaConfig ;
Color targetColor = GetCanonicalPaletteColorFromHex ( Settings . Canvas . BrushAutoRestoreColor ? ? "" , alpha ) ;
2026-02-14 00:23:05 +08:00
2026-02-14 09:35:16 +08:00
double sliderValue = Settings . Canvas . BrushAutoRestoreWidth ;
double width ;
if ( sliderValue < = 0 )
2026-02-14 00:23:05 +08:00
{
width = Settings . Canvas . InkWidth > 0 ? Settings . Canvas . InkWidth : 2.5 ;
}
2026-02-14 09:35:16 +08:00
else
{
width = sliderValue / 2.0 ;
}
2026-02-14 00:23:05 +08:00
SetBrushAttributesDirectly ( targetColor , width , width ) ;
UpdateBrushAutoRestoreTimerInterval ( ) ;
_brushAutoRestoreTimer . Start ( ) ;
}
catch ( Exception ex )
{
2026-02-14 12:49:39 +08:00
LogHelper . WriteLogToFile ( $"BrushAutoRestoreTimer_Tick: {ex.Message}" , LogHelper . LogType . Error ) ;
2026-02-14 00:23:05 +08:00
}
}
2025-05-25 09:29:48 +08:00
//ApplicationGesture lastApplicationGesture = ApplicationGesture.AllGestures;
private DateTime lastGestureTime = DateTime . Now ;
2025-08-03 16:46:33 +08:00
private void InkCanvas_Gesture ( object sender , InkCanvasGestureEventArgs e )
{
2025-05-25 09:29:48 +08:00
var gestures = e . GetGestureRecognitionResults ( ) ;
2025-08-03 16:46:33 +08:00
try
{
2025-05-25 09:29:48 +08:00
foreach ( var gest in gestures )
//Trace.WriteLine(string.Format("Gesture: {0}, Confidence: {1}", gest.ApplicationGesture, gest.RecognitionConfidence));
2025-09-06 10:17:12 +08:00
// 只有在PPT放映模式下才响应翻页手势
2025-09-07 13:30:46 +08:00
if ( StackPanelPPTControls . Visibility = = Visibility . Visible & &
2025-09-06 10:17:12 +08:00
BtnPPTSlideShowEnd . Visibility = = Visibility . Visible & &
PPTManager ? . IsInSlideShow = = true )
2025-08-03 16:46:33 +08:00
{
2025-05-25 09:29:48 +08:00
if ( gest . ApplicationGesture = = ApplicationGesture . Left )
2025-08-24 11:08:13 +08:00
{
2025-11-15 18:04:22 +08:00
BtnPPTSlidesDown_Click ( null , null ) ; // 下一页
2025-08-24 11:08:13 +08:00
}
2025-05-25 09:29:48 +08:00
if ( gest . ApplicationGesture = = ApplicationGesture . Right )
2025-08-24 11:08:13 +08:00
{
2025-11-15 18:04:22 +08:00
BtnPPTSlidesUp_Click ( null , null ) ; // 上一页
2025-08-24 11:08:13 +08:00
}
2025-05-25 09:29:48 +08:00
}
}
catch { }
}
2025-08-03 16:46:33 +08:00
private void inkCanvas_EditingModeChanged ( object sender , RoutedEventArgs e )
{
2025-09-30 19:15:03 +08:00
var inkCanvas1 = sender as InkCanvas ;
if ( inkCanvas1 = = null ) return ;
2025-07-19 10:56:30 +08:00
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
SetCursorBasedOnEditingMode ( inkCanvas1 ) ;
2025-08-03 16:46:33 +08:00
if ( Settings . Canvas . IsShowCursor )
{
2025-07-19 10:56:30 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Ink | |
inkCanvas1 . EditingMode = = InkCanvasEditingMode . Select | |
drawingShapeMode ! = 0 )
2025-07-18 11:49:00 +08:00
inkCanvas1 . ForceCursor = true ;
else
inkCanvas1 . ForceCursor = false ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-23 22:16:43 +08:00
// 套索选择模式下始终强制显示光标,即使用户设置不显示光标
2025-08-03 16:46:33 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Select )
{
2025-07-19 10:56:30 +08:00
inkCanvas1 . ForceCursor = true ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-19 10:56:30 +08:00
inkCanvas1 . ForceCursor = false ;
}
2025-07-18 11:49:00 +08:00
}
2025-07-23 22:16:43 +08:00
2025-05-25 09:29:48 +08:00
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . Ink ) forcePointEraser = ! forcePointEraser ;
2025-08-03 16:46:33 +08:00
2025-09-27 17:01:33 +08:00
// 处理橡皮擦覆盖层的启用/禁用
2025-09-30 19:15:03 +08:00
var eraserOverlay = FindName ( "EraserOverlayCanvas" ) as Canvas ;
if ( eraserOverlay ! = null )
2025-08-03 16:46:33 +08:00
{
if ( inkCanvas1 . EditingMode = = InkCanvasEditingMode . EraseByPoint )
{
2025-07-23 22:16:43 +08:00
// 橡皮擦模式下启用覆盖层
2025-09-27 17:01:33 +08:00
EnableEraserOverlay ( ) ;
Trace . WriteLine ( "Eraser: Overlay enabled in eraser mode" ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-07-23 22:16:43 +08:00
// 其他模式下禁用覆盖层
2025-09-27 17:01:33 +08:00
DisableEraserOverlay ( ) ;
Trace . WriteLine ( "Eraser: Overlay disabled in non-eraser mode" ) ;
2025-07-23 22:16:43 +08:00
}
}
2025-05-25 09:29:48 +08:00
}
#endregion Ink Canvas
#region Definations and Loading
public static Settings Settings = new Settings ( ) ;
2025-09-13 21:10:36 +08:00
public static string settingsFileName = Path . Combine ( "Configs" , "Settings.json" ) ;
2025-07-28 14:40:44 +08:00
private bool isLoaded ;
private bool forcePointEraser ;
2025-05-25 09:29:48 +08:00
2025-08-03 16:46:33 +08:00
private void Window_Loaded ( object sender , RoutedEventArgs e )
{
2025-05-25 09:29:48 +08:00
loadPenCanvas ( ) ;
//加载设置
LoadSettings ( true ) ;
2025-10-01 00:01:35 +08:00
AutoBackupManager . Initialize ( Settings ) ;
2026-02-07 07:39:05 +08:00
CheckUpdateChannelAndTelemetryConsistency ( ) ;
2025-10-03 17:08:46 +08:00
2025-11-29 17:26:47 +08:00
// 初始化Dlass上传队列(恢复上次的上传队列)
DlassNoteUploader . InitializeQueue ( ) ;
2026-02-06 23:12:54 +08:00
_ = TelemetryUploader . UploadTelemetryIfNeededAsync ( ) ;
2025-07-24 00:20:00 +08:00
// 检查保存路径是否可用,不可用则修正
try
{
string savePath = Settings . Automation . AutoSavedStrokesLocation ;
bool needFix = false ;
2025-07-28 14:40:44 +08:00
if ( string . IsNullOrWhiteSpace ( savePath ) | | ! Directory . Exists ( savePath ) )
2025-07-24 00:20:00 +08:00
{
needFix = true ;
}
else
{
// 检查是否可写
try
{
2025-07-28 14:40:44 +08:00
string testFile = Path . Combine ( savePath , "test.tmp" ) ;
File . WriteAllText ( testFile , "test" ) ;
File . Delete ( testFile ) ;
2025-07-24 00:20:00 +08:00
}
catch
{
needFix = true ;
}
}
if ( needFix )
{
2025-09-13 21:10:36 +08:00
string newPath = Path . Combine ( AppDomain . CurrentDomain . BaseDirectory , "Saves" ) ;
2025-07-24 00:20:00 +08:00
Settings . Automation . AutoSavedStrokesLocation = newPath ;
2025-07-28 14:40:44 +08:00
if ( ! Directory . Exists ( newPath ) )
Directory . CreateDirectory ( newPath ) ;
2025-07-24 00:20:00 +08:00
SaveSettingsToFile ( ) ;
LogHelper . WriteLogToFile ( $"自动修正保存路径为: {newPath}" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检测或修正保存路径时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 加载自定义背景颜色
LoadCustomBackgroundColor ( ) ;
2025-07-29 01:15:32 +08:00
2025-12-13 17:03:58 +08:00
// 设置窗口模式
SetWindowMode ( ) ;
2025-07-15 21:10:49 +08:00
// 注册设置面板滚动事件
if ( SettingsPanelScrollViewer ! = null )
{
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
2025-07-29 01:15:32 +08:00
// 初始化PPT管理器
InitializePPTManagers ( ) ;
// 如果启用PPT支持,开始监控
if ( Settings . PowerPointSettings . PowerPointSupport )
{
StartPPTMonitoring ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-12-28 14:30:02 +08:00
// 初始化窗口概览模型
try
{
_windowOverviewModel = new WindowOverviewModel ( ) ;
LogHelper . WriteLogToFile ( "窗口概览模型已初始化" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化窗口概览模型失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-09-06 12:09:06 +08:00
// 如果启用PowerPoint联动增强功能,启动进程守护
if ( Settings . PowerPointSettings . EnablePowerPointEnhancement )
{
StartPowerPointProcessMonitoring ( ) ;
}
2025-05-25 09:29:48 +08:00
// HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow();
if ( Environment . Is64BitProcess ) GroupBoxInkRecognition . Visibility = Visibility . Collapsed ;
2025-09-21 00:25:09 +08:00
// 根据设置应用主题
switch ( Settings . Appearance . Theme )
{
case 0 : // 浅色主题
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Light ;
SetTheme ( "Light" ) ;
break ;
case 1 : // 深色主题
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Dark ;
SetTheme ( "Dark" ) ;
break ;
case 2 : // 跟随系统
if ( IsSystemThemeLight ( ) )
{
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Light ;
SetTheme ( "Light" ) ;
}
else
{
ThemeManager . Current . ApplicationTheme = ApplicationTheme . Dark ;
SetTheme ( "Dark" ) ;
}
break ;
}
2025-05-25 09:29:48 +08:00
//TextBlockVersion.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();
LogHelper . WriteLogToFile ( "Ink Canvas Loaded" , LogHelper . LogType . Event ) ;
isLoaded = true ;
BlackBoardLeftSidePageListView . ItemsSource = blackBoardSidePageListViewObservableCollection ;
BlackBoardRightSidePageListView . ItemsSource = blackBoardSidePageListViewObservableCollection ;
BtnLeftWhiteBoardSwitchPreviousGeometry . Brush =
2025-07-28 14:40:44 +08:00
new SolidColorBrush ( Color . FromArgb ( 127 , 24 , 24 , 27 ) ) ;
2025-05-25 09:29:48 +08:00
BtnLeftWhiteBoardSwitchPreviousLabel . Opacity = 0.5 ;
BtnRightWhiteBoardSwitchPreviousGeometry . Brush =
2025-07-28 14:40:44 +08:00
new SolidColorBrush ( Color . FromArgb ( 127 , 24 , 24 , 27 ) ) ;
2025-05-25 09:29:48 +08:00
BtnRightWhiteBoardSwitchPreviousLabel . Opacity = 0.5 ;
2025-07-16 09:16:36 +08:00
// 应用颜色主题,这将考虑自定义背景色
CheckColorTheme ( true ) ;
2025-08-03 16:46:33 +08:00
2025-05-25 09:29:48 +08:00
BtnWhiteBoardSwitchPrevious . IsEnabled = CurrentWhiteboardIndex ! = 1 ;
BorderInkReplayToolBox . Visibility = Visibility . Collapsed ;
// 提前加载IA库,优化第一笔等待时间
2025-08-03 16:46:33 +08:00
if ( Settings . InkToShape . IsInkToShapeEnabled & & ! Environment . Is64BitProcess )
{
2025-05-25 09:29:48 +08:00
var strokeEmpty = new StrokeCollection ( ) ;
InkRecognizeHelper . RecognizeShape ( strokeEmpty ) ;
}
SystemEvents . DisplaySettingsChanged + = SystemEventsOnDisplaySettingsChanged ;
2025-10-02 15:30:51 +08:00
// 自动收纳到侧边栏(若通过 --board 进入白板模式或 --show 参数则跳过收纳)
if ( Settings . Startup . IsFoldAtStartup & & ! App . StartWithBoardMode & & ! App . StartWithShowMode )
2025-05-31 21:13:02 +08:00
{
2025-06-29 15:07:13 +08:00
FoldFloatingBar_MouseUp ( new object ( ) , null ) ;
2025-05-31 21:13:02 +08:00
}
2025-06-12 10:13:47 +08:00
// 恢复崩溃后操作设置
if ( App . CrashAction = = App . CrashActionType . SilentRestart )
RadioCrashSilentRestart . IsChecked = true ;
else
RadioCrashNoAction . IsChecked = true ;
2025-07-20 15:42:11 +08:00
2025-10-26 00:21:10 +08:00
// 显示快抽悬浮按钮
ShowQuickDrawFloatingButton ( ) ;
2025-07-16 09:16:36 +08:00
// 如果当前不是黑板模式,则切换到黑板模式
if ( currentMode = = 0 )
{
// 延迟执行,确保UI已完全加载
2025-08-03 16:46:33 +08:00
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
2025-07-16 09:16:36 +08:00
// 重新加载自定义背景颜色
LoadCustomBackgroundColor ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 模拟点击切换按钮进入黑板模式
if ( GridTransparencyFakeBackground . Background ! = Brushes . Transparent )
{
BtnSwitch_Click ( BtnSwitch , null ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-16 09:16:36 +08:00
// 确保背景颜色正确设置为黑板颜色
CheckColorTheme ( true ) ;
2025-07-28 14:40:44 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-07-16 09:16:36 +08:00
}
2025-07-16 13:35:17 +08:00
// 初始化插件系统
InitializePluginSystem ( ) ;
2025-07-25 18:21:16 +08:00
// 确保开关和设置同步
ToggleSwitchNoFocusMode . IsOn = Settings . Advanced . IsNoFocusMode ;
2025-11-08 23:09:04 +08:00
ApplyNoFocusMode ( ) ;
2025-08-13 11:54:36 +08:00
ToggleSwitchAlwaysOnTop . IsOn = Settings . Advanced . IsAlwaysOnTop ;
2025-11-08 23:09:04 +08:00
ApplyAlwaysOnTop ( ) ;
2025-10-03 17:08:46 +08:00
2025-10-18 18:26:13 +08:00
// 初始化UIA置顶开关
ToggleSwitchUIAccessTopMost . IsOn = Settings . Advanced . EnableUIAccessTopMost ;
UpdateUIAccessTopMostVisibility ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
App . IsUIAccessTopMostEnabled = Settings . Advanced . EnableUIAccessTopMost ;
2025-07-30 19:56:22 +08:00
// 初始化剪贴板监控
InitializeClipboardMonitoring ( ) ;
2025-08-31 11:43:52 +08:00
2025-09-13 15:34:50 +08:00
// 初始化悬浮窗拦截管理器
InitializeFloatingWindowInterceptor ( ) ;
2025-08-23 21:39:00 +08:00
// 初始化全局快捷键管理器
InitializeGlobalHotkeyManager ( ) ;
2025-08-23 23:13:39 +08:00
// 初始化墨迹渐隐管理器
InitializeInkFadeManager ( ) ;
2025-08-31 11:49:00 +08:00
// 处理命令行参数中的文件路径
HandleCommandLineFileOpen ( ) ;
2025-08-31 12:03:58 +08:00
// 初始化文件关联状态显示
InitializeFileAssociationStatus ( ) ;
2025-09-06 21:26:46 +08:00
// 检查模式设置并应用
CheckMainWindowVisibility ( ) ;
2025-09-13 14:59:47 +08:00
// 检查是否通过--board参数启动,如果是则自动切换到白板模式
if ( App . StartWithBoardMode )
{
LogHelper . WriteLogToFile ( "检测到--board参数,自动切换到白板模式" , LogHelper . LogType . Event ) ;
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
SwitchToBoardMode ( ) ;
} ) , DispatcherPriority . Loaded ) ;
}
2025-10-02 15:30:51 +08:00
// 检查是否通过--show参数启动,如果是则确保退出收纳模式并恢复浮动栏
if ( App . StartWithShowMode )
{
LogHelper . WriteLogToFile ( "检测到--show参数,退出收纳模式并恢复浮动栏" , LogHelper . LogType . Event ) ;
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( async ( ) = >
{
// 如果当前处于收纳模式,则展开浮动栏
if ( isFloatingBarFolded )
{
await UnFoldFloatingBar ( new object ( ) ) ;
}
} ) , DispatcherPriority . Loaded ) ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
// 初始化计时器控件关联
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
if ( TimerControl ! = null & & MinimizedTimerControl ! = null )
{
MinimizedTimerControl . SetParentControl ( TimerControl ) ;
2025-12-27 22:41:24 +08:00
// 设置PPT时间胶囊的父控件
if ( PPTTimeCapsule ! = null )
{
PPTTimeCapsule . SetParentControl ( TimerControl ) ;
}
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
TimerControl . ShowMinimizedRequested + = ( s , args ) = >
{
if ( TimerContainer ! = null & & MinimizedTimerContainer ! = null & & MinimizedTimerControl ! = null )
{
TimerContainer . Visibility = Visibility . Collapsed ;
2025-12-27 22:41:24 +08:00
if ( Settings . PowerPointSettings . EnablePPTTimeCapsule & &
BtnPPTSlideShowEnd . Visibility = = Visibility . Visible & &
PPTTimeCapsule ! = null )
{
MinimizedTimerContainer . Visibility = Visibility . Collapsed ;
}
else
{
MinimizedTimerContainer . Visibility = Visibility . Visible ;
MinimizedTimerControl . Visibility = Visibility . Visible ;
}
2025-11-29 16:27:35 +08:00
}
} ;
2025-12-20 13:56:46 +08:00
2025-11-29 16:27:35 +08:00
TimerControl . HideMinimizedRequested + = ( s , args ) = >
{
if ( MinimizedTimerContainer ! = null & & MinimizedTimerControl ! = null )
{
MinimizedTimerContainer . Visibility = Visibility . Collapsed ;
MinimizedTimerControl . Visibility = Visibility . Collapsed ;
}
2025-12-27 22:41:24 +08:00
// 如果启用了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 ( ) ;
}
2025-11-29 16:27:35 +08:00
} ;
}
} ) , DispatcherPriority . Loaded ) ;
2026-02-14 00:23:05 +08:00
AddTouchSupportToSliders ( ) ;
2025-05-25 09:29:48 +08:00
}
2026-02-14 15:59:10 +08:00
private void ShowOobeIfNeeded ( )
{
try
{
if ( Settings ? . Startup = = null ) return ;
if ( Settings . Startup . HasShownOobe ) return ;
var oobeWindow = new OobeWindow ( Settings )
{
Owner = this ,
WindowStartupLocation = WindowStartupLocation . CenterOwner
} ;
try
{
App . IsOobeShowing = true ;
oobeWindow . ShowDialog ( ) ;
}
finally
{
App . IsOobeShowing = false ;
}
Settings . Startup . HasShownOobe = true ;
SaveSettingsToFile ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"显示首次启动体验(OOBE)时出错: {ex}" , LogHelper . LogType . Error ) ;
}
finally
{
App . IsOobeShowing = false ;
}
}
2025-08-03 16:46:33 +08:00
private void SystemEventsOnDisplaySettingsChanged ( object sender , EventArgs e )
{
2025-05-25 09:29:48 +08:00
if ( ! Settings . Advanced . IsEnableResolutionChangeDetection ) return ;
2025-07-28 14:40:44 +08:00
ShowNotification ( $"检测到显示器信息变化,变为{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}) " ) ;
2025-08-03 16:46:33 +08:00
new Thread ( ( ) = >
{
2025-05-25 09:29:48 +08:00
var isFloatingBarOutsideScreen = false ;
var isInPPTPresentationMode = false ;
2025-08-03 16:46:33 +08:00
Dispatcher . Invoke ( ( ) = >
{
2025-05-25 09:29:48 +08:00
isFloatingBarOutsideScreen = IsOutsideOfScreenHelper . IsOutsideOfScreen ( ViewboxFloatingBar ) ;
isInPPTPresentationMode = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
} ) ;
2025-08-03 16:46:33 +08:00
if ( isFloatingBarOutsideScreen ) dpiChangedDelayAction . DebounceAction ( 3000 , null , ( ) = >
{
2025-05-25 09:29:48 +08:00
if ( ! isFloatingBarFolded )
{
if ( isInPPTPresentationMode ) ViewboxFloatingBarMarginAnimation ( 60 ) ;
else ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
} ) ;
} ) . Start ( ) ;
}
public DelayAction dpiChangedDelayAction = new DelayAction ( ) ;
private void MainWindow_OnDpiChanged ( object sender , DpiChangedEventArgs e )
{
if ( e . OldDpi . DpiScaleX ! = e . NewDpi . DpiScaleX & & e . OldDpi . DpiScaleY ! = e . NewDpi . DpiScaleY & & Settings . Advanced . IsEnableDPIChangeDetection )
{
ShowNotification ( $"系统DPI发生变化,从 {e.OldDpi.DpiScaleX}x{e.OldDpi.DpiScaleY} 变化为 {e.NewDpi.DpiScaleX}x{e.NewDpi.DpiScaleY}" ) ;
2025-12-27 22:57:22 +08:00
// 如果TimerContainer可见,调整其尺寸
Dispatcher . Invoke ( ( ) = >
{
var timerContainer = FindName ( "TimerContainer" ) as FrameworkElement ;
if ( timerContainer ! = null & & timerContainer . Visibility = = Visibility . Visible )
{
AdjustTimerContainerSize ( ) ;
}
} ) ;
2025-08-03 16:46:33 +08:00
new Thread ( ( ) = >
{
2025-05-25 09:29:48 +08:00
var isFloatingBarOutsideScreen = false ;
var isInPPTPresentationMode = false ;
2025-08-03 16:46:33 +08:00
Dispatcher . Invoke ( ( ) = >
{
2025-05-25 09:29:48 +08:00
isFloatingBarOutsideScreen = IsOutsideOfScreenHelper . IsOutsideOfScreen ( ViewboxFloatingBar ) ;
isInPPTPresentationMode = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
} ) ;
2025-08-03 16:46:33 +08:00
if ( isFloatingBarOutsideScreen ) dpiChangedDelayAction . DebounceAction ( 3000 , null , ( ) = >
{
2025-05-25 09:29:48 +08:00
if ( ! isFloatingBarFolded )
{
if ( isInPPTPresentationMode ) ViewboxFloatingBarMarginAnimation ( 60 ) ;
else ViewboxFloatingBarMarginAnimation ( 100 , true ) ;
}
} ) ;
} ) . Start ( ) ;
}
}
2025-12-13 17:03:58 +08:00
private void SetWindowMode ( )
{
if ( Settings . Advanced . WindowMode )
{
WindowState = WindowState . Normal ;
Left = 0.0 ;
Top = 0.0 ;
Height = SystemParameters . PrimaryScreenHeight ;
Width = SystemParameters . PrimaryScreenWidth ;
}
else // 全屏
{
WindowState = WindowState . Maximized ;
}
}
2026-02-19 18:28:01 +08:00
private bool _allowCloseAfterExitVerification ;
private bool _isExitVerificationInProgress ;
private void Window_Closing ( object sender , CancelEventArgs e )
2025-08-03 16:46:33 +08:00
{
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( "Ink Canvas closing" , LogHelper . LogType . Event ) ;
2026-02-19 18:28:01 +08:00
if ( _allowCloseAfterExitVerification )
{
_allowCloseAfterExitVerification = false ;
return ;
}
2025-10-26 00:21:10 +08:00
try
{
2025-11-08 22:07:38 +08:00
// 快抽按钮现在集成在主窗口中,不需要单独关闭
2025-10-26 00:21:10 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"关闭快抽悬浮按钮时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-12-20 13:56:46 +08:00
2026-02-16 20:22:41 +08:00
try
{
if ( ! App . IsUpdateInstalling & & SecurityManager . IsPasswordRequiredForExit ( Settings ) )
{
2026-02-19 18:28:01 +08:00
e . Cancel = true ;
if ( _isExitVerificationInProgress ) return ;
_isExitVerificationInProgress = true ;
Dispatcher . BeginInvoke ( new Action ( async ( ) = >
2026-02-16 20:22:41 +08:00
{
2026-02-19 18:28:01 +08:00
try
{
bool ok = await SecurityManager . PromptAndVerifyAsync ( Settings , this , "退出验证" , "请输入安全密码以退出软件。" ) ;
if ( ! ok )
{
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled by security password" , LogHelper . LogType . Event ) ;
return ;
}
_allowCloseAfterExitVerification = true ;
Close ( ) ;
}
catch
{
}
finally
{
_isExitVerificationInProgress = false ;
}
} ) , DispatcherPriority . Normal ) ;
return ;
2026-02-16 20:22:41 +08:00
}
}
catch
{
}
2025-08-03 16:46:33 +08:00
if ( ! CloseIsFromButton & & Settings . Advanced . IsSecondConfirmWhenShutdownApp )
{
2025-07-31 10:03:29 +08:00
// 第一个确认对话框
var result1 = MessageBox . Show ( "是否继续关闭 InkCanvasForClass,这将丢失当前未保存的墨迹。" , "InkCanvasForClass" ,
MessageBoxButton . OKCancel , MessageBoxImage . Warning ) ;
2025-08-03 16:46:33 +08:00
if ( result1 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at first confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 第二个确认对话框
2025-08-03 16:46:33 +08:00
var result2 = MessageBox . Show ( "真的狠心关闭 InkCanvasForClass吗?" , "InkCanvasForClass" ,
2025-07-31 10:03:29 +08:00
MessageBoxButton . OKCancel , MessageBoxImage . Error ) ;
2025-08-03 16:46:33 +08:00
if ( result2 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at second confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 第三个最终确认对话框
2025-08-03 16:46:33 +08:00
var result3 = MessageBox . Show ( "最后确认:确定要关闭 InkCanvasForClass 吗?" , "InkCanvasForClass" ,
2025-07-31 10:03:29 +08:00
MessageBoxButton . OKCancel , MessageBoxImage . Question ) ;
2025-08-03 16:46:33 +08:00
if ( result3 = = MessageBoxResult . Cancel )
{
2025-07-31 10:03:29 +08:00
e . Cancel = true ;
LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled at final confirmation" , LogHelper . LogType . Event ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-31 10:03:29 +08:00
// 所有确认都通过,允许关闭
e . Cancel = false ;
LogHelper . WriteLogToFile ( "Ink Canvas closing confirmed by user" , LogHelper . LogType . Event ) ;
2025-05-25 09:29:48 +08:00
}
if ( e . Cancel ) LogHelper . WriteLogToFile ( "Ink Canvas closing cancelled" , LogHelper . LogType . Event ) ;
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow ( IntPtr hWnd , int X , int Y , int nWidth , int nHeight , bool bRepaint ) ;
2025-08-03 16:46:33 +08:00
private void MainWindow_OnSizeChanged ( object sender , SizeChangedEventArgs e )
{
if ( Settings . Advanced . IsEnableForceFullScreen )
{
2025-05-25 09:29:48 +08:00
if ( isLoaded ) ShowNotification (
2025-07-28 14:40:44 +08:00
$"检测到窗口大小变化,已自动恢复到全屏:{Screen.PrimaryScreen.Bounds.Width}x{Screen.PrimaryScreen.Bounds.Height}(缩放比例为{Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth}x{Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight}) " ) ;
2025-05-25 09:29:48 +08:00
WindowState = WindowState . Maximized ;
MoveWindow ( new WindowInteropHelper ( this ) . Handle , 0 , 0 ,
2025-07-28 14:40:44 +08:00
Screen . PrimaryScreen . Bounds . Width ,
Screen . PrimaryScreen . Bounds . Height , true ) ;
2025-05-25 09:29:48 +08:00
}
}
2025-06-06 16:09:28 +08:00
2025-08-03 16:46:33 +08:00
private void Window_Closed ( object sender , EventArgs e )
{
2025-05-25 09:29:48 +08:00
SystemEvents . DisplaySettingsChanged - = SystemEventsOnDisplaySettingsChanged ;
2026-02-19 18:18:47 +08:00
try
{
// 清理视频展台资源
if ( _cameraService ! = null )
{
_cameraService . FrameReceived - = CameraService_FrameReceived ;
_cameraService . ErrorOccurred - = CameraService_ErrorOccurred ;
_cameraService . Dispose ( ) ;
_cameraService = null ;
}
lock ( _videoPresenterFrameLock )
{
_lastFrame ? . Dispose ( ) ;
_lastFrame = null ;
}
}
catch { }
2025-07-29 01:15:32 +08:00
// 释放PPT管理器资源
DisposePPTManagers ( ) ;
2025-07-30 19:56:22 +08:00
// 清理剪贴板监控
CleanupClipboardMonitoring ( ) ;
ClipboardNotification . Stop ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-23 21:39:00 +08:00
// 清理全局快捷键管理器
if ( _globalHotkeyManager ! = null )
{
_globalHotkeyManager . Dispose ( ) ;
_globalHotkeyManager = null ;
}
2025-07-30 19:56:22 +08:00
2025-08-23 23:13:39 +08:00
// 清理墨迹渐隐管理器
if ( _inkFadeManager ! = null )
{
_inkFadeManager . ClearAllFadingStrokes ( ) ;
_inkFadeManager = null ;
}
2025-10-02 11:50:00 +08:00
// 清理悬浮窗拦截管理器
if ( _floatingWindowInterceptorManager ! = null )
{
_floatingWindowInterceptorManager . Dispose ( ) ;
_floatingWindowInterceptorManager = null ;
}
2025-12-28 14:30:02 +08:00
// 清理窗口概览模型
if ( _windowOverviewModel ! = null )
{
_windowOverviewModel . Dispose ( ) ;
_windowOverviewModel = null ;
}
2025-08-13 12:52:56 +08:00
// 停止置顶维护定时器
StopTopmostMaintenance ( ) ;
2025-11-15 20:24:28 +08:00
UninstallKeyboardHook ( ) ;
2025-09-06 12:38:57 +08:00
// 从Z-Order管理器中移除主窗口
WindowZOrderManager . UnregisterWindow ( this ) ;
2025-05-25 09:29:48 +08:00
LogHelper . WriteLogToFile ( "Ink Canvas closed" , LogHelper . LogType . Event ) ;
2025-07-29 01:15:32 +08:00
2025-06-18 18:16:48 +08:00
// 检查是否有待安装的更新
CheckPendingUpdates ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
private void CheckPendingUpdates ( )
{
try
{
// 如果有可用的更新版本且启用了自动更新
if ( AvailableLatestVersion ! = null & & Settings . Startup . IsAutoUpdate )
{
// 检查更新文件是否已下载
string updatesFolderPath = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "AutoUpdate" ) ;
string statusFilePath = Path . Combine ( updatesFolderPath , $"DownloadV{AvailableLatestVersion}Status.txt" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
if ( File . Exists ( statusFilePath ) & & File . ReadAllText ( statusFilePath ) . Trim ( ) . ToLower ( ) = = "true" )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | Installing pending update v{AvailableLatestVersion} on application close" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 设置为用户主动退出,避免被看门狗判定为崩溃
App . IsAppExitByUser = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 创建批处理脚本并启动,软件关闭后会执行更新操作
AutoUpdateHelper . InstallNewVersionApp ( AvailableLatestVersion , true ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | Error checking pending updates: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-05-25 09:29:48 +08:00
}
2025-10-06 14:11:32 +08:00
// 使用多线路组下载更新
2025-07-19 16:03:45 +08:00
private async Task < bool > DownloadUpdateWithFallback ( string version , AutoUpdateHelper . UpdateLineGroup primaryGroup , UpdateChannel channel )
{
try
{
// 如果主要线路组可用,直接使用
if ( primaryGroup ! = null )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 使用主要线路组下载: {primaryGroup.GroupName}" ) ;
return await AutoUpdateHelper . DownloadSetupFile ( version , primaryGroup ) ;
}
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 如果主要线路组不可用,获取所有可用线路组
LogHelper . WriteLogToFile ( "AutoUpdate | 主要线路组不可用,获取所有可用线路组" ) ;
var availableGroups = await AutoUpdateHelper . GetAvailableLineGroupsOrdered ( channel ) ;
if ( availableGroups . Count = = 0 )
{
LogHelper . WriteLogToFile ( "AutoUpdate | 没有可用的线路组" , LogHelper . LogType . Error ) ;
return false ;
}
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | 使用 {availableGroups.Count} 个可用线路组进行下载" ) ;
return await AutoUpdateHelper . DownloadSetupFileWithFallback ( version , availableGroups ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 下载更新时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
return false ;
}
}
2025-08-03 16:46:33 +08:00
private async void AutoUpdate ( )
{
2025-12-20 17:45:35 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . AutoUpdatePauseUntilDate ) )
{
if ( DateTime . TryParse ( Settings . Startup . AutoUpdatePauseUntilDate , out DateTime pauseUntilDate ) )
{
if ( DateTime . Now < pauseUntilDate )
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 自动更新已暂停,直到 {pauseUntilDate:yyyy-MM-dd}" ) ;
2025-12-27 12:05:34 +08:00
return ;
2025-12-20 17:45:35 +08:00
}
else
{
LogHelper . WriteLogToFile ( $"AutoUpdate | 暂停期已过,恢复自动更新检查" ) ;
Settings . Startup . AutoUpdatePauseUntilDate = "" ;
SaveSettingsToFile ( ) ;
}
}
}
2025-07-19 16:03:45 +08:00
// 清除之前的更新状态,确保使用新通道重新检查
AvailableLatestVersion = null ;
AvailableLatestLineGroup = null ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 使用当前选择的更新通道检查更新
2025-07-22 18:02:29 +08:00
var ( remoteVersion , lineGroup , apiReleaseNotes ) = await AutoUpdateHelper . CheckForUpdates ( Settings . Startup . UpdateChannel ) ;
2025-07-19 15:36:05 +08:00
AvailableLatestVersion = remoteVersion ;
AvailableLatestLineGroup = lineGroup ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false ;
2025-05-25 09:29:48 +08:00
2025-10-06 22:41:53 +08:00
bool hasValidLineGroup = lineGroup ! = null ;
2025-08-03 16:46:33 +08:00
if ( AvailableLatestVersion ! = null )
{
2025-10-04 14:47:53 +08:00
// 检测到新版本,停止重试定时器
timerCheckAutoUpdateRetry . Stop ( ) ;
updateCheckRetryCount = 0 ;
2025-10-06 18:29:12 +08:00
2025-06-29 11:56:38 +08:00
// 检测到新版本
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | New version available: {AvailableLatestVersion}" ) ;
2025-08-03 16:46:33 +08:00
2025-12-20 19:16:39 +08:00
// 通过 Windows 系统通知提示有新版本
WindowsNotificationHelper . ShowNewVersionToast ( AvailableLatestVersion ) ;
2025-06-29 11:56:38 +08:00
// 检查是否是用户选择跳过的版本
2025-08-03 16:46:33 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . SkippedVersion ) & &
Settings . Startup . SkippedVersion = = AvailableLatestVersion )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | Version {AvailableLatestVersion} was marked to be skipped by the user" ) ;
return ; // 跳过此版本,不执行更新操作
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果检测到的版本与跳过的版本不同,则清除跳过版本记录
// 这确保用户只能跳过当前最新版本,而不是永久跳过所有更新
2025-08-03 16:46:33 +08:00
if ( ! string . IsNullOrEmpty ( Settings . Startup . SkippedVersion ) & &
Settings . Startup . SkippedVersion ! = AvailableLatestVersion )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( $"AutoUpdate | Detected new version {AvailableLatestVersion} different from skipped version {Settings.Startup.SkippedVersion}, clearing skip record" ) ;
Settings . Startup . SkippedVersion = "" ;
SaveSettingsToFile ( ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 获取当前版本
2025-06-18 18:16:48 +08:00
string currentVersion = Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version . ToString ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果启用了静默更新,则自动下载更新而不显示提示
2025-08-03 16:46:33 +08:00
if ( Settings . Startup . IsAutoUpdateWithSilence )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Silent update enabled, downloading update automatically without notification" ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 静默下载更新,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update downloaded successfully, will install when conditions are met" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 启动检查定时器,定期检查是否可以安装
timerCheckAutoUpdateWithSilence . Start ( ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-29 11:56:38 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Silent update download failed" , LogHelper . LogType . Error ) ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
return ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果没有启用静默更新,则显示常规更新窗口
2025-06-18 18:16:48 +08:00
string releaseDate = DateTime . Now . ToString ( "yyyy年MM月dd日" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 从服务器获取更新日志
string releaseNotes = await AutoUpdateHelper . GetUpdateLog ( Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果获取失败,使用默认文本
if ( string . IsNullOrEmpty ( releaseNotes ) )
{
releaseNotes = $@"# InkCanvasForClass v{AvailableLatestVersion}更新
2025-06-29 14:15:20 +08:00
2025-06-29 11:56:38 +08:00
无法获取更新日志,但新版本已准备就绪。" ;
}
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 创建并显示更新窗口
HasNewUpdateWindow updateWindow = new HasNewUpdateWindow ( currentVersion , AvailableLatestVersion , releaseDate , releaseNotes ) ;
2025-08-30 16:25:10 +08:00
updateWindow . Owner = this ;
2025-06-18 18:16:48 +08:00
bool? dialogResult = updateWindow . ShowDialog ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 如果窗口被关闭但没有点击按钮,则不执行任何操作
2025-08-03 16:46:33 +08:00
if ( dialogResult ! = true )
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update dialog closed without selection" ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 不再从更新窗口获取自动更新设置
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 根据用户选择处理更新
2025-08-03 16:46:33 +08:00
switch ( updateWindow . Result )
{
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . UpdateNow :
// 立即更新:显示下载进度,下载完成后立即安装
LogHelper . WriteLogToFile ( "AutoUpdate | User chose to update now" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 显示下载进度提示
MessageBox . Show ( "开始下载更新,请稍候..." , "正在更新" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 下载更新文件,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-18 18:16:48 +08:00
// 下载成功,提示用户准备安装
MessageBoxResult result = MessageBox . Show ( "更新已下载完成,点击确定后将关闭软件并安装新版本!" , "安装更新" , MessageBoxButton . OKCancel , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 只有当用户点击确定按钮后才关闭软件
2025-08-03 16:46:33 +08:00
if ( result = = MessageBoxResult . OK )
{
2025-06-18 18:16:48 +08:00
// 设置为用户主动退出,避免被看门狗判定为崩溃
App . IsAppExitByUser = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 准备批处理脚本
2025-08-30 14:56:59 +08:00
AutoUpdateHelper . InstallNewVersionApp ( AvailableLatestVersion , true ) ; // 修改为静默模式,避免重复启动进程
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 关闭软件,让安装程序接管
Application . Current . Shutdown ( ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | User cancelled update installation" ) ;
}
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
// 下载失败
MessageBox . Show ( "更新下载失败,请检查网络连接后重试。" , "下载失败" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
break ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . UpdateLater :
// 稍后更新:静默下载,在软件关闭时自动安装
LogHelper . WriteLogToFile ( "AutoUpdate | User chose to update later" ) ;
2025-08-03 16:46:33 +08:00
2025-07-19 16:03:45 +08:00
// 不管设置如何,都进行下载,使用多线路组下载功能
isDownloadSuccessful = await DownloadUpdateWithFallback ( AvailableLatestVersion , AvailableLatestLineGroup , Settings . Startup . UpdateChannel ) ;
2025-08-03 16:46:33 +08:00
if ( isDownloadSuccessful )
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update downloaded successfully, will install when application closes" ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 设置标志,在应用程序关闭时安装
Settings . Startup . IsAutoUpdate = true ;
Settings . Startup . IsAutoUpdateWithSilence = true ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 启动检查定时器
timerCheckAutoUpdateWithSilence . Start ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
// 通知用户
MessageBox . Show ( "更新已下载完成,将在软件关闭时自动安装。" , "更新已准备就绪" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 18:16:48 +08:00
LogHelper . WriteLogToFile ( "AutoUpdate | Update download failed" , LogHelper . LogType . Error ) ;
MessageBox . Show ( "更新下载失败,请检查网络连接后重试。" , "下载失败" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
break ;
2025-08-03 16:46:33 +08:00
2025-06-18 18:16:48 +08:00
case HasNewUpdateWindow . UpdateResult . SkipVersion :
// 跳过该版本:记录到设置中
LogHelper . WriteLogToFile ( $"AutoUpdate | User chose to skip version {AvailableLatestVersion}" ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 记录要跳过的版本号
Settings . Startup . SkippedVersion = AvailableLatestVersion ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 保存设置到文件
SaveSettingsToFile ( ) ;
2025-08-03 16:46:33 +08:00
2025-06-29 11:56:38 +08:00
// 通知用户
2025-08-03 16:46:33 +08:00
MessageBox . Show ( $"已设置跳过版本 {AvailableLatestVersion},在下次发布新版本之前不会再提示更新。" ,
"已跳过此版本" ,
MessageBoxButton . OK ,
2025-06-29 11:56:38 +08:00
MessageBoxImage . Information ) ;
2025-06-18 18:16:48 +08:00
break ;
2025-05-25 09:29:48 +08:00
}
2025-08-03 16:46:33 +08:00
}
2025-10-06 22:41:53 +08:00
else if ( hasValidLineGroup )
{
LogHelper . WriteLogToFile ( "AutoUpdate | Current version is already the latest, no retry needed" ) ;
// 停止重试定时器
timerCheckAutoUpdateRetry . Stop ( ) ;
updateCheckRetryCount = 0 ;
}
2025-08-03 16:46:33 +08:00
else
{
2025-10-04 14:47:53 +08:00
// 检查更新失败,启动重试定时器
LogHelper . WriteLogToFile ( "AutoUpdate | Update check failed, starting retry timer" ) ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 重置重试计数
updateCheckRetryCount = 0 ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 启动重试定时器,10分钟后重新检查
timerCheckAutoUpdateRetry . Start ( ) ;
2025-10-06 18:29:12 +08:00
2025-10-04 14:47:53 +08:00
// 清理更新文件夹
2025-05-25 09:29:48 +08:00
AutoUpdateHelper . DeleteUpdatesFolder ( ) ;
}
}
2025-10-06 22:41:53 +08:00
// 新增:崩溃后操作设置按钮事件
2025-06-12 10:13:47 +08:00
private void RadioCrashAction_Checked ( object sender , RoutedEventArgs e )
{
if ( RadioCrashSilentRestart ! = null & & RadioCrashSilentRestart . IsChecked = = true )
{
App . CrashAction = App . CrashActionType . SilentRestart ;
2025-07-06 15:39:37 +08:00
Settings . Startup . CrashAction = 0 ;
2025-06-12 10:13:47 +08:00
}
else if ( RadioCrashNoAction ! = null & & RadioCrashNoAction . IsChecked = = true )
{
App . CrashAction = App . CrashActionType . NoAction ;
2025-07-06 15:39:37 +08:00
Settings . Startup . CrashAction = 1 ;
2025-06-12 10:13:47 +08:00
}
2025-06-12 11:21:45 +08:00
SaveSettingsToFile ( ) ;
2025-07-06 15:39:37 +08:00
// 强制同步全局变量,防止后台逻辑未及时感知
App . SyncCrashActionFromSettings ( ) ;
2025-06-12 11:21:45 +08:00
}
2025-06-18 09:08:38 +08:00
// 添加一个辅助方法,根据当前编辑模式设置光标
2025-07-18 16:12:04 +08:00
public void SetCursorBasedOnEditingMode ( InkCanvas canvas )
2025-06-18 09:08:38 +08:00
{
2025-07-19 10:56:30 +08:00
// 套索选择模式下光标始终显示,无论用户设置如何
2025-08-03 16:46:33 +08:00
if ( canvas . EditingMode = = InkCanvasEditingMode . Select )
{
2025-07-19 10:18:16 +08:00
canvas . UseCustomCursor = true ;
canvas . ForceCursor = true ;
2025-07-19 10:56:30 +08:00
canvas . Cursor = Cursors . Cross ;
2025-07-19 10:18:16 +08:00
System . Windows . Forms . Cursor . Show ( ) ;
return ;
}
2025-07-19 10:56:30 +08:00
// 其他模式按照用户设置处理
2025-08-03 16:46:33 +08:00
if ( Settings . Canvas . IsShowCursor )
{
2025-06-18 09:08:38 +08:00
canvas . UseCustomCursor = true ;
canvas . ForceCursor = true ;
2025-07-19 10:56:30 +08:00
2025-06-18 09:08:38 +08:00
// 根据编辑模式设置不同的光标
2025-08-03 16:46:33 +08:00
if ( canvas . EditingMode = = InkCanvasEditingMode . EraseByPoint )
{
2025-06-18 09:08:38 +08:00
canvas . Cursor = Cursors . Cross ;
2025-08-03 16:46:33 +08:00
}
else if ( canvas . EditingMode = = InkCanvasEditingMode . Ink )
{
2026-01-01 18:21:28 +08:00
if ( _cachedPenCursor = = null )
{
lock ( _cursorLock )
{
if ( _cachedPenCursor = = null )
{
try
{
var sri = Application . GetResourceStream ( new Uri ( "Resources/Cursors/Pen.cur" , UriKind . Relative ) ) ;
if ( sri ! = null )
{
_cachedPenCursor = new Cursor ( sri . Stream ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"加载 Pen 光标资源失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
}
}
if ( _cachedPenCursor ! = null )
{
canvas . Cursor = _cachedPenCursor ;
}
2025-06-18 09:08:38 +08:00
}
2025-07-19 10:56:30 +08:00
2025-06-19 11:25:15 +08:00
// 确保光标可见,无论是鼠标、触控还是手写笔
2025-06-18 09:08:38 +08:00
System . Windows . Forms . Cursor . Show ( ) ;
2025-07-19 10:56:30 +08:00
2025-06-19 11:25:15 +08:00
// 确保手写笔模式下也能显示光标
2025-08-03 16:46:33 +08:00
if ( Tablet . TabletDevices . Count > 0 )
{
foreach ( TabletDevice device in Tablet . TabletDevices )
{
if ( device . Type = = TabletDeviceType . Stylus )
{
2025-06-19 11:25:15 +08:00
// 手写笔设备存在,强制显示光标
System . Windows . Forms . Cursor . Show ( ) ;
break ;
}
}
}
2025-08-03 16:46:33 +08:00
}
else
{
2025-06-18 09:08:38 +08:00
canvas . UseCustomCursor = false ;
canvas . ForceCursor = false ;
System . Windows . Forms . Cursor . Show ( ) ;
}
}
2025-06-12 11:21:45 +08:00
// 鼠标输入
private void inkCanvas_PreviewMouseDown ( object sender , MouseButtonEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
2025-07-19 10:56:30 +08:00
SetCursorBasedOnEditingMode ( sender as InkCanvas ) ;
2025-07-30 14:12:10 +08:00
2025-08-31 08:04:46 +08:00
// 检查是否点击了空白区域或其他非图片元素
var hitTest = e . OriginalSource ;
if ( ! ( hitTest is Image ) & & ! ( hitTest is MediaElement ) )
2025-07-30 14:12:10 +08:00
{
2025-08-31 08:04:46 +08:00
// 如果当前有选中的元素,取消选中状态
if ( currentSelectedElement ! = null )
2025-07-30 14:12:10 +08:00
{
2025-09-07 00:49:10 +08:00
// 取消选中元素
2025-08-31 08:04:46 +08:00
UnselectElement ( currentSelectedElement ) ;
currentSelectedElement = null ;
2025-09-07 13:30:46 +08:00
2025-09-07 00:49:10 +08:00
// 重置为选择模式,确保用户可以继续选择其他元素
SetCurrentToolMode ( InkCanvasEditingMode . Select ) ;
// 更新模式缓存
UpdateCurrentToolMode ( "select" ) ;
// 刷新浮动栏高光显示
SetFloatingBarHighlightPosition ( "select" ) ;
2025-07-30 14:12:10 +08:00
}
}
2025-06-12 11:21:45 +08:00
}
// 手写笔输入
private void inkCanvas_StylusDown ( object sender , StylusDownEventArgs e )
{
2025-06-18 09:08:38 +08:00
// 使用辅助方法设置光标
2025-07-19 10:56:30 +08:00
SetCursorBasedOnEditingMode ( sender as InkCanvas ) ;
2025-06-12 11:21:45 +08:00
}
2025-06-18 09:08:38 +08:00
// 触摸结束,恢复光标
#endregion Definations and Loading
#region Navigation Sidebar Methods
// 侧边栏导航按钮事件处理
private void NavStartup_Click ( object sender , RoutedEventArgs e )
{
// 切换到启动设置页面
ShowSettingsSection ( "startup" ) ;
}
private void NavCanvas_Click ( object sender , RoutedEventArgs e )
{
// 切换到画布设置页面
ShowSettingsSection ( "canvas" ) ;
}
private void NavGesture_Click ( object sender , RoutedEventArgs e )
{
// 切换到手势设置页面
ShowSettingsSection ( "gesture" ) ;
}
private void NavInkRecognition_Click ( object sender , RoutedEventArgs e )
{
// 切换到墨迹识别设置页面
ShowSettingsSection ( "inkrecognition" ) ;
}
private void NavCrashAction_Click ( object sender , RoutedEventArgs e )
{
// 切换到崩溃处理设置页面
ShowSettingsSection ( "crashaction" ) ;
}
private void NavPPT_Click ( object sender , RoutedEventArgs e )
{
// 切换到PPT设置页面
ShowSettingsSection ( "ppt" ) ;
}
private void NavAdvanced_Click ( object sender , RoutedEventArgs e )
{
// 切换到高级设置页面
ShowSettingsSection ( "advanced" ) ;
}
private void NavAutomation_Click ( object sender , RoutedEventArgs e )
{
// 切换到自动化设置页面
ShowSettingsSection ( "automation" ) ;
}
private void NavRandomWindow_Click ( object sender , RoutedEventArgs e )
{
// 切换到随机窗口设置页面
ShowSettingsSection ( "randomwindow" ) ;
}
private void NavAbout_Click ( object sender , RoutedEventArgs e )
{
// 切换到关于页面
ShowSettingsSection ( "about" ) ;
2025-07-26 14:29:24 +08:00
// 刷新设备信息
RefreshDeviceInfo ( ) ;
2025-06-18 09:08:38 +08:00
}
2025-10-06 14:11:32 +08:00
// 个性化设置
2025-06-18 09:08:38 +08:00
private void NavTheme_Click ( object sender , RoutedEventArgs e )
{
// 切换到个性化设置页面
ShowSettingsSection ( "theme" ) ;
}
2025-10-06 14:11:32 +08:00
// 快捷键设置
2025-06-18 09:08:38 +08:00
private void NavShortcuts_Click ( object sender , RoutedEventArgs e )
{
2025-08-23 21:39:00 +08:00
OpenHotkeySettingsWindow ( ) ;
2025-06-18 09:08:38 +08:00
}
private void BtnCloseSettings_Click ( object sender , RoutedEventArgs e )
{
// 关闭设置面板
BorderSettings . Visibility = Visibility . Collapsed ;
2025-06-19 14:30:24 +08:00
// 设置蒙版为不可点击,并清除背景
2025-06-18 09:08:38 +08:00
BorderSettingsMask . IsHitTestVisible = false ;
2025-06-19 14:30:24 +08:00
BorderSettingsMask . Background = null ; // 确保清除蒙层背景
2025-06-18 09:08:38 +08:00
}
2025-07-26 14:29:24 +08:00
/// <summary>
/// 刷新设备信息按钮点击事件
/// </summary>
private void RefreshDeviceInfo_Click ( object sender , RoutedEventArgs e )
{
RefreshDeviceInfo ( ) ;
}
/// <summary>
/// 刷新设备信息显示
/// </summary>
private void RefreshDeviceInfo ( )
{
try
{
// 获取设备ID
string deviceId = DeviceIdentifier . GetDeviceId ( ) ;
DeviceIdTextBlock . Text = deviceId ;
// 获取使用频率
var usageFrequency = DeviceIdentifier . GetUsageFrequency ( ) ;
string frequencyText ;
switch ( usageFrequency )
{
case DeviceIdentifier . UsageFrequency . High :
frequencyText = "高频用户" ;
break ;
case DeviceIdentifier . UsageFrequency . Medium :
frequencyText = "中频用户" ;
break ;
case DeviceIdentifier . UsageFrequency . Low :
frequencyText = "低频用户" ;
break ;
default :
frequencyText = "未知" ;
break ;
}
UsageFrequencyTextBlock . Text = frequencyText ;
// 获取更新优先级
var updatePriority = DeviceIdentifier . GetUpdatePriority ( ) ;
string priorityText ;
switch ( updatePriority )
{
case DeviceIdentifier . UpdatePriority . High :
priorityText = "高优先级(优先推送更新)" ;
break ;
case DeviceIdentifier . UpdatePriority . Medium :
priorityText = "中优先级(正常推送更新)" ;
break ;
case DeviceIdentifier . UpdatePriority . Low :
priorityText = "低优先级(延迟推送更新)" ;
break ;
default :
priorityText = "未知" ;
break ;
}
UpdatePriorityTextBlock . Text = priorityText ;
2025-07-28 11:35:47 +08:00
// 获取使用统计(秒级精度)
var ( launchCount , totalSeconds , avgSessionSeconds , _ ) = DeviceIdentifier . GetUsageStats ( ) ;
2025-07-26 14:29:24 +08:00
LaunchCountTextBlock . Text = launchCount . ToString ( ) ;
2025-07-28 11:35:47 +08:00
// 使用新的格式化方法显示秒级精度的使用时长
string totalUsageText = DeviceIdentifier . FormatDuration ( totalSeconds ) ;
2025-07-26 14:29:24 +08:00
TotalUsageTextBlock . Text = totalUsageText ;
LogHelper . WriteLogToFile ( $"MainWindow | 设备信息已刷新 - ID: {deviceId}, 频率: {frequencyText}, 优先级: {priorityText}" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"MainWindow | 刷新设备信息失败: {ex.Message}" , LogHelper . LogType . Error ) ;
2025-08-03 16:46:33 +08:00
2025-07-26 14:29:24 +08:00
// 显示错误信息
DeviceIdTextBlock . Text = "获取失败" ;
UsageFrequencyTextBlock . Text = "获取失败" ;
UpdatePriorityTextBlock . Text = "获取失败" ;
LaunchCountTextBlock . Text = "获取失败" ;
TotalUsageTextBlock . Text = "获取失败" ;
}
}
2025-10-06 14:11:32 +08:00
// 折叠侧边栏
2025-06-18 09:08:38 +08:00
private void CollapseNavSidebar_Click ( object sender , RoutedEventArgs e )
{
// 折叠/展开侧边栏
var columnDefinitions = ( ( Grid ) BorderSettings . Child ) . ColumnDefinitions ;
if ( columnDefinitions [ 0 ] . Width . Value = = 50 )
2025-06-17 13:20:06 +08:00
{
2025-06-18 09:08:38 +08:00
// 折叠侧边栏
columnDefinitions [ 0 ] . Width = new GridLength ( 0 ) ;
2025-06-17 13:20:06 +08:00
}
else
{
2025-06-18 09:08:38 +08:00
// 展开侧边栏
columnDefinitions [ 0 ] . Width = new GridLength ( 50 ) ;
2025-06-13 11:43:50 +08:00
}
2025-06-12 11:21:45 +08:00
}
2025-08-03 16:46:33 +08:00
2025-10-06 14:11:32 +08:00
// 显示侧边栏
2025-06-18 09:08:38 +08:00
private void ShowNavSidebar_Click ( object sender , RoutedEventArgs e )
{
// 确保侧边栏展开
var columnDefinitions = ( ( Grid ) BorderSettings . Child ) . ColumnDefinitions ;
columnDefinitions [ 0 ] . Width = new GridLength ( 50 ) ;
}
2025-06-12 11:21:45 +08:00
2025-10-06 14:11:32 +08:00
// 显示指定的设置部分
2025-07-15 21:10:49 +08:00
private async void ShowSettingsSection ( string sectionTag )
2025-06-12 11:21:45 +08:00
{
2025-06-18 09:08:38 +08:00
// 显示设置面板
BorderSettings . Visibility = Visibility . Visible ;
2025-06-19 14:32:16 +08:00
// 设置蒙版为可点击,并添加半透明背景
2025-06-18 09:08:38 +08:00
BorderSettingsMask . IsHitTestVisible = true ;
2025-07-28 14:40:44 +08:00
BorderSettingsMask . Background = new SolidColorBrush ( Color . FromArgb ( 1 , 0 , 0 , 0 ) ) ;
2025-08-03 16:46:33 +08:00
2025-06-18 09:08:38 +08:00
// 获取SettingsPanelScrollViewer中的所有GroupBox
2025-09-30 19:15:03 +08:00
var stackPanel = SettingsPanelScrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 确保所有GroupBox都是可见的
2025-06-18 09:08:38 +08:00
foreach ( var child in stackPanel . Children )
{
if ( child is GroupBox groupBox )
{
2025-07-15 21:10:49 +08:00
groupBox . Visibility = Visibility . Visible ;
2025-06-18 09:08:38 +08:00
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 确保UI完全更新
2025-08-03 16:46:33 +08:00
await Dispatcher . InvokeAsync ( ( ) = > { } , DispatcherPriority . Render ) ;
2025-07-15 21:10:49 +08:00
// 根据传入的sectionTag滚动到相应的设置部分
GroupBox targetGroupBox = null ;
2025-08-03 16:46:33 +08:00
2025-06-18 09:08:38 +08:00
switch ( sectionTag . ToLower ( ) )
{
case "startup" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "启动" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "canvas" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "画板和墨迹" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "gesture" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "手势" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "inkrecognition" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxInkRecognition ;
2025-06-18 09:08:38 +08:00
break ;
case "crashaction" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "崩溃后操作" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "ppt" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "PPT联动" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "advanced" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "高级设置" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "automation" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "自动化" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "randomwindow" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxRandWindow ;
2025-06-18 09:08:38 +08:00
break ;
case "theme" :
2025-07-15 21:10:49 +08:00
targetGroupBox = GroupBoxAppearanceNewUI ;
2025-06-18 09:08:38 +08:00
break ;
case "shortcuts" :
// 快捷键设置部分可能尚未实现
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "快捷键" ) ;
2025-06-18 09:08:38 +08:00
break ;
case "about" :
2025-07-15 21:10:49 +08:00
targetGroupBox = FindGroupBoxByHeader ( stackPanel , "关于" ) ;
2025-06-18 09:08:38 +08:00
break ;
2025-07-16 13:35:17 +08:00
case "plugins" :
targetGroupBox = GroupBoxPlugins ;
break ;
2025-06-18 09:08:38 +08:00
default :
2025-07-15 21:10:49 +08:00
// 默认滚动到顶部
SettingsPanelScrollViewer . ScrollToTop ( ) ;
return ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 如果找到目标GroupBox,则滚动到它的位置
if ( targetGroupBox ! = null )
{
// 使用动画平滑滚动到目标位置
ScrollToElement ( targetGroupBox ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 高亮显示当前选中的导航项
UpdateNavigationButtonState ( sectionTag ) ;
}
else
{
// 如果没有找到目标GroupBox,则滚动到顶部
SettingsPanelScrollViewer . ScrollToTop ( ) ;
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 根据Header文本查找GroupBox
private GroupBox FindGroupBoxByHeader ( StackPanel parent , string headerText )
{
foreach ( var child in parent . Children )
{
if ( child is GroupBox groupBox )
{
// 查找GroupBox的Header
2025-08-03 16:46:33 +08:00
if ( groupBox . Header is TextBlock headerTextBlock & &
headerTextBlock . Text ! = null & &
2025-07-15 21:10:49 +08:00
headerTextBlock . Text . Contains ( headerText ) )
{
return groupBox ;
}
}
}
return null ;
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 平滑滚动到指定元素
private async void ScrollToElement ( FrameworkElement element )
{
if ( element = = null | | SettingsPanelScrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
try
{
// 暂时禁用滚动事件处理
SettingsPanelScrollViewer . ScrollChanged - = SettingsPanelScrollViewer_ScrollChanged ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 记录当前滚动位置
double originalOffset = SettingsPanelScrollViewer . VerticalOffset ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 将ScrollViewer内部的位置信息重置到顶部(不会触发视觉更新)
SettingsPanelScrollViewer . ScrollToHome ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 使用Dispatcher进行延迟处理,确保布局更新
2025-08-03 16:46:33 +08:00
await Dispatcher . InvokeAsync ( ( ) = >
{
2025-07-15 21:10:49 +08:00
try
{
// 强制更新布局
SettingsPanelScrollViewer . UpdateLayout ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 获取元素相对于顶部的准确位置
Point elementPosition = element . TransformToAncestor ( SettingsPanelScrollViewer ) . Transform ( new Point ( 0 , 0 ) ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 计算目标位置,减去一些偏移,使元素不会贴在顶部
double targetPosition = elementPosition . Y - 20 ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 确保目标位置不小于0
targetPosition = Math . Max ( 0 , targetPosition ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 直接设置滚动位置,不使用动画
SettingsPanelScrollViewer . ScrollToVerticalOffset ( targetPosition ) ;
}
2025-08-31 01:47:00 +08:00
catch ( Exception )
2025-06-18 09:08:38 +08:00
{
2025-07-15 21:10:49 +08:00
// 如果出现异常,恢复到原来的滚动位置
SettingsPanelScrollViewer . ScrollToVerticalOffset ( originalOffset ) ;
2025-06-18 09:08:38 +08:00
}
2025-07-15 21:10:49 +08:00
finally
{
// 重新启用滚动事件处理
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
2025-07-28 14:40:44 +08:00
} , DispatcherPriority . Render ) ;
2025-07-15 21:10:49 +08:00
}
catch ( Exception )
{
// 确保在异常情况下也重新启用滚动事件处理
SettingsPanelScrollViewer . ScrollChanged + = SettingsPanelScrollViewer_ScrollChanged ;
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 滚动条变化事件处理
2025-07-28 14:40:44 +08:00
private void SettingsPanelScrollViewer_ScrollChanged ( object sender , ScrollChangedEventArgs e )
2025-07-15 21:10:49 +08:00
{
// 可以在这里添加滚动事件的处理逻辑,如果需要的话
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 更新导航按钮状态
private void UpdateNavigationButtonState ( string activeTag )
{
// 清除所有导航按钮的Tag属性
ClearAllNavButtonTags ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 设置当前活动按钮的Tag属性
switch ( activeTag . ToLower ( ) )
{
case "startup" :
SetNavButtonTag ( "startup" ) ;
break ;
case "canvas" :
SetNavButtonTag ( "canvas" ) ;
break ;
case "gesture" :
SetNavButtonTag ( "gesture" ) ;
break ;
case "inkrecognition" :
SetNavButtonTag ( "inkrecognition" ) ;
break ;
case "crashaction" :
SetNavButtonTag ( "crashaction" ) ;
break ;
case "ppt" :
SetNavButtonTag ( "ppt" ) ;
break ;
case "advanced" :
SetNavButtonTag ( "advanced" ) ;
break ;
case "automation" :
SetNavButtonTag ( "automation" ) ;
break ;
case "randomwindow" :
SetNavButtonTag ( "randomwindow" ) ;
break ;
case "theme" :
SetNavButtonTag ( "theme" ) ;
2025-06-18 09:08:38 +08:00
break ;
2025-07-15 21:10:49 +08:00
case "shortcuts" :
SetNavButtonTag ( "shortcuts" ) ;
break ;
case "about" :
SetNavButtonTag ( "about" ) ;
break ;
2025-07-16 13:35:17 +08:00
case "plugins" :
SetNavButtonTag ( "plugins" ) ;
break ;
2025-07-15 21:10:49 +08:00
}
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 清除所有导航按钮的Tag属性
private void ClearAllNavButtonTags ( )
{
2025-09-30 19:15:03 +08:00
var grid = BorderSettings . Child as Grid ;
if ( grid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navSidebar = grid . Children [ 0 ] as Border ;
if ( navSidebar = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navGrid = navSidebar . Child as Grid ;
if ( navGrid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var scrollViewer = navGrid . Children [ 1 ] as ScrollViewer ;
if ( scrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var stackPanel = scrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
foreach ( var child in stackPanel . Children )
{
if ( child is Button button )
{
button . Tag = null ;
}
2025-06-18 09:08:38 +08:00
}
2025-07-15 21:10:49 +08:00
}
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 设置导航按钮的Tag属性
private void SetNavButtonTag ( string tag )
{
2025-09-30 19:15:03 +08:00
var grid = BorderSettings . Child as Grid ;
if ( grid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navSidebar = grid . Children [ 0 ] as Border ;
if ( navSidebar = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var navGrid = navSidebar . Child as Grid ;
if ( navGrid = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var scrollViewer = navGrid . Children [ 1 ] as ScrollViewer ;
if ( scrollViewer = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-09-30 19:15:03 +08:00
var stackPanel = scrollViewer . Content as StackPanel ;
if ( stackPanel = = null ) return ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
foreach ( var child in stackPanel . Children )
{
if ( child is Button button )
{
// 检查按钮的ToolTip属性,根据tag设置对应的按钮
2025-09-30 19:15:03 +08:00
string buttonTag = button . Tag as string ;
2025-08-03 16:46:33 +08:00
2025-07-15 21:10:49 +08:00
// 如果按钮的Tag与要设置的tag匹配,则设置Tag
2025-09-30 19:15:03 +08:00
if ( buttonTag ! = null & & buttonTag . ToLower ( ) = = tag . ToLower ( ) )
2025-07-15 21:10:49 +08:00
{
button . Tag = tag ;
return ;
}
}
}
2025-06-18 09:08:38 +08:00
}
2025-08-03 16:46:33 +08:00
2025-06-18 09:08:38 +08:00
// 根据Header文本查找并显示GroupBox
private void ShowGroupBoxByHeader ( StackPanel parent , string headerText )
{
foreach ( var child in parent . Children )
{
if ( child is GroupBox groupBox )
{
// 查找GroupBox的Header
2025-08-03 16:46:33 +08:00
if ( groupBox . Header is TextBlock headerTextBlock & &
headerTextBlock . Text ! = null & &
2025-06-18 09:08:38 +08:00
headerTextBlock . Text . Contains ( headerText ) )
{
groupBox . Visibility = Visibility . Visible ;
return ;
}
}
2025-06-13 11:43:50 +08:00
}
2025-06-12 10:13:47 +08:00
}
2025-06-18 09:08:38 +08:00
#endregion Navigation Sidebar Methods
2025-07-16 13:35:17 +08:00
2025-08-09 13:16:22 +08:00
#region 插 件 ? ? ?
2025-07-16 13:35:17 +08:00
// 添加插件系统初始化方法
private void InitializePluginSystem ( )
{
try
{
// 初始化插件管理器
2025-07-28 14:40:44 +08:00
PluginManager . Instance . Initialize ( ) ;
LogHelper . WriteLogToFile ( "插件系统已初始化" ) ;
2025-07-16 13:35:17 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化插件系统时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
// 添加插件管理导航点击事件处理
private void NavPlugins_Click ( object sender , RoutedEventArgs e )
{
ShowSettingsSection ( "plugins" ) ;
}
// 添加打开插件管理器按钮点击事件
private void BtnOpenPluginManager_Click ( object sender , RoutedEventArgs e )
{
try
{
// 暂时隐藏设置面板
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 创建并显示插件设置窗口
2025-07-28 14:40:44 +08:00
PluginSettingsWindow pluginSettingsWindow = new PluginSettingsWindow ( ) ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 设置窗口关闭事件,用于在插件管理窗口关闭后恢复设置面板
pluginSettingsWindow . Closed + = ( s , args ) = >
{
// 恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
} ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
// 显示插件设置窗口
pluginSettingsWindow . ShowDialog ( ) ;
}
catch ( Exception ex )
{
// 确保在发生错误时也恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-08-03 16:46:33 +08:00
2025-07-16 13:35:17 +08:00
LogHelper . WriteLogToFile ( $"打开插件管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
MessageBox . Show ( $"打开插件管理器时出错: {ex.Message}" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
}
2025-08-09 13:16:22 +08:00
#endregion 插 件 ? ? ?
2025-07-18 16:12:04 +08:00
2025-08-31 15:35:28 +08:00
#region 新 设 置 窗 口
2026-02-18 22:09:40 +08:00
private async void BtnOpenNewSettings_Click ( object sender , RoutedEventArgs e )
2025-08-31 15:35:28 +08:00
{
2026-02-18 22:09:40 +08:00
if ( isOpeningOrHidingSettingsPane ) return ;
HideSubPanels ( ) ;
2026-02-16 20:22:41 +08:00
{
2026-02-18 22:09:40 +08:00
try
2026-02-16 20:22:41 +08:00
{
2026-02-18 22:09:40 +08:00
if ( SecurityManager . IsPasswordRequiredForEnterSettings ( Settings ) )
{
bool ok = await SecurityManager . PromptAndVerifyAsync ( Settings , this , "进入设置" , "请输入安全密码以进入设置。" ) ;
if ( ! ok ) return ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"安全密码校验失败: {ex}" , LogHelper . LogType . Error ) ;
return ;
2026-02-16 20:22:41 +08:00
}
2026-02-18 22:09:40 +08:00
var settingsWindow = new SettingsWindow ( ) ;
settingsWindow . Owner = this ;
settingsWindow . ShowDialog ( ) ;
}
2025-08-31 15:35:28 +08:00
}
#endregion 新 设 置 窗 口
2025-07-18 16:12:04 +08:00
// 在MainWindow类中添加:
private void ApplyCurrentEraserShape ( )
{
double k = 1 ;
switch ( Settings . Canvas . EraserSize )
{
case 0 :
k = Settings . Canvas . EraserShapeType = = 0 ? 0.5 : 0.7 ;
break ;
case 1 :
k = Settings . Canvas . EraserShapeType = = 0 ? 0.8 : 0.9 ;
break ;
case 3 :
k = Settings . Canvas . EraserShapeType = = 0 ? 1.25 : 1.2 ;
break ;
case 4 :
k = Settings . Canvas . EraserShapeType = = 0 ? 1.5 : 1.3 ;
break ;
}
if ( Settings . Canvas . EraserShapeType = = 0 )
{
inkCanvas . EraserShape = new EllipseStylusShape ( k * 90 , k * 90 ) ;
}
else if ( Settings . Canvas . EraserShapeType = = 1 )
{
inkCanvas . EraserShape = new RectangleStylusShape ( k * 90 * 0.6 , k * 90 ) ;
}
}
2025-07-18 19:20:06 +08:00
// 显示指定页
private void ShowPage ( int index )
{
if ( index < 0 | | index > = whiteboardPages . Count ) return ;
// 只切换可见性
for ( int i = 0 ; i < whiteboardPages . Count ; i + + )
{
2025-07-28 14:40:44 +08:00
whiteboardPages [ i ] . Visibility = ( i = = index ) ? Visibility . Visible : Visibility . Collapsed ;
2025-07-18 19:20:06 +08:00
}
currentCanvas = whiteboardPages [ index ] ;
currentPageIndex = index ;
}
// 新建页面
private void AddNewPage ( )
{
var newCanvas = new System . Windows . Controls . Canvas ( ) ;
whiteboardPages . Add ( newCanvas ) ;
InkCanvasGridForInkReplay . Children . Add ( newCanvas ) ;
ShowPage ( whiteboardPages . Count - 1 ) ;
}
// 删除当前页面
private void DeleteCurrentPage ( )
{
if ( whiteboardPages . Count < = 1 ) return ;
InkCanvasGridForInkReplay . Children . Remove ( currentCanvas ) ;
whiteboardPages . RemoveAt ( currentPageIndex ) ;
if ( currentPageIndex > = whiteboardPages . Count )
currentPageIndex = whiteboardPages . Count - 1 ;
ShowPage ( currentPageIndex ) ;
}
2025-07-21 16:43:29 +08:00
// 快速面板退出PPT放映按钮事件
private void ExitPPTSlideShow_MouseUp ( object sender , MouseButtonEventArgs e )
{
// 直接调用PPT放映结束按钮的逻辑
BtnPPTSlideShowEnd_Click ( BtnPPTSlideShowEnd , null ) ;
}
2025-07-22 17:36:02 +08:00
private void HistoryRollbackButton_Click ( object sender , RoutedEventArgs e )
{
// 收起设置面板(与插件面板一致)
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
var win = new HistoryRollbackWindow ( Settings . Startup . UpdateChannel ) ;
win . ShowDialog ( ) ;
// 可选:回滚窗口关闭后恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
}
2025-07-25 18:21:16 +08:00
[DllImport("user32.dll")]
private static extern int SetWindowLong ( IntPtr hWnd , int nIndex , int dwNewLong ) ;
[DllImport("user32.dll")]
private static extern int GetWindowLong ( IntPtr hWnd , int nIndex ) ;
2025-08-13 11:54:36 +08:00
[DllImport("user32.dll")]
private static extern bool SetWindowPos ( IntPtr hWnd , IntPtr hWndInsertAfter , int X , int Y , int cx , int cy , uint uFlags ) ;
2025-08-13 12:52:56 +08:00
[DllImport("user32.dll")]
private static extern bool BringWindowToTop ( IntPtr hWnd ) ;
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow ( IntPtr hWnd ) ;
2025-08-24 11:08:13 +08:00
[DllImport("user32.dll")]
private static extern bool PostMessage ( IntPtr hWnd , uint Msg , IntPtr wParam , IntPtr lParam ) ;
2025-08-30 16:25:10 +08:00
[DllImport("kernel32.dll")]
private static extern uint GetCurrentProcessId ( ) ;
2025-12-20 13:56:46 +08:00
2025-11-15 20:24:28 +08:00
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx ( int idHook , LowLevelKeyboardProc lpfn , IntPtr hMod , uint dwThreadId ) ;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx ( IntPtr hhk ) ;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx ( IntPtr hhk , int nCode , IntPtr wParam , IntPtr lParam ) ;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle ( string lpModuleName ) ;
private const int WH_KEYBOARD_LL = 13 ;
private const int WM_KEYDOWN = 0x0100 ;
private const int WM_KEYUP = 0x0101 ;
private const int WM_SYSKEYDOWN = 0x0104 ;
private const int WM_SYSKEYUP = 0x0105 ;
private delegate IntPtr LowLevelKeyboardProc ( int nCode , IntPtr wParam , IntPtr lParam ) ;
[StructLayout(LayoutKind.Sequential)]
private struct KBDLLHOOKSTRUCT
{
public uint vkCode ;
public uint scanCode ;
public uint flags ;
public uint time ;
public IntPtr dwExtraInfo ;
}
2025-08-13 12:52:56 +08:00
2025-11-15 20:24:28 +08:00
private LowLevelKeyboardProc _keyboardProc ;
private IntPtr _keyboardHookId = IntPtr . Zero ;
2025-08-31 11:43:52 +08:00
2025-07-25 18:21:16 +08:00
private const int GWL_EXSTYLE = - 20 ;
private const int WS_EX_NOACTIVATE = 0x08000000 ;
2025-08-13 12:52:56 +08:00
private const int WS_EX_TOPMOST = 0x00000008 ;
2025-08-13 11:54:36 +08:00
private static readonly IntPtr HWND_TOPMOST = new IntPtr ( - 1 ) ;
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr ( - 2 ) ;
private const uint SWP_NOMOVE = 0x0002 ;
private const uint SWP_NOSIZE = 0x0001 ;
private const uint SWP_NOACTIVATE = 0x0010 ;
2025-08-13 12:52:56 +08:00
private const uint SWP_SHOWWINDOW = 0x0040 ;
private const uint SWP_NOOWNERZORDER = 0x0200 ;
// 添加定时器来维护置顶状态
private DispatcherTimer topmostMaintenanceTimer ;
2025-11-01 20:42:18 +08:00
private DispatcherTimer autoSaveStrokesTimer ;
2025-08-31 09:54:13 +08:00
private bool isTopmostMaintenanceEnabled ;
2025-07-25 18:21:16 +08:00
2025-11-15 20:24:28 +08:00
private IntPtr KeyboardHookProc ( int nCode , IntPtr wParam , IntPtr lParam )
{
return CallNextHookEx ( _keyboardHookId , nCode , wParam , lParam ) ;
}
private void InstallKeyboardHook ( )
{
if ( _keyboardHookId = = IntPtr . Zero )
{
_keyboardProc = KeyboardHookProc ;
_keyboardHookId = SetWindowsHookEx ( WH_KEYBOARD_LL , _keyboardProc ,
GetModuleHandle ( null ) , 0 ) ;
if ( _keyboardHookId = = IntPtr . Zero )
{
LogHelper . WriteLogToFile ( "安装低级键盘钩子失败" , LogHelper . LogType . Error ) ;
}
}
}
private void UninstallKeyboardHook ( )
{
if ( _keyboardHookId ! = IntPtr . Zero )
{
UnhookWindowsHookEx ( _keyboardHookId ) ;
_keyboardHookId = IntPtr . Zero ;
_keyboardProc = null ;
}
}
2025-07-25 18:21:16 +08:00
private void ApplyNoFocusMode ( )
{
var hwnd = new WindowInteropHelper ( this ) . Handle ;
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
2025-12-20 13:56:46 +08:00
bool shouldBeNoFocus = isTemporarilyDisablingNoFocusMode ?
2025-10-18 18:29:52 +08:00
false : Settings . Advanced . IsNoFocusMode ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:29:52 +08:00
if ( shouldBeNoFocus )
2025-07-25 18:21:16 +08:00
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_NOACTIVATE ) ;
2025-11-15 20:24:28 +08:00
InstallKeyboardHook ( ) ;
2025-07-25 18:21:16 +08:00
}
else
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle & ~ WS_EX_NOACTIVATE ) ;
2025-11-15 20:24:28 +08:00
UninstallKeyboardHook ( ) ;
2025-07-25 18:21:16 +08:00
}
}
2025-08-13 11:54:36 +08:00
private void ApplyAlwaysOnTop ( )
{
2025-08-13 12:52:56 +08:00
try
2025-08-13 11:54:36 +08:00
{
2025-09-06 13:48:44 +08:00
var hwnd = new WindowInteropHelper ( this ) . Handle ;
2025-08-13 12:52:56 +08:00
if ( Settings . Advanced . IsAlwaysOnTop )
{
Topmost = true ;
2025-08-31 11:43:52 +08:00
2025-09-06 13:48:44 +08:00
// 1. 设置窗口样式为置顶
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_TOPMOST ) ;
// 2. 使用SetWindowPos确保窗口在最顶层
SetWindowPos ( hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
2025-09-06 13:16:10 +08:00
2025-10-18 20:41:09 +08:00
// 3. 如果启用了无焦点模式且未启用UIA置顶,需要特殊处理
if ( Settings . Advanced . IsNoFocusMode & & ! Settings . Advanced . EnableUIAccessTopMost )
2025-08-13 12:52:56 +08:00
{
// 启动置顶维护定时器
StartTopmostMaintenance ( ) ;
}
else
{
// 停止置顶维护定时器
StopTopmostMaintenance ( ) ;
}
}
else
{
// 取消置顶时
2025-09-06 13:48:44 +08:00
// 1. 先使用Win32 API取消置顶
SetWindowPos ( hwnd , HWND_NOTOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
// 2. 移除置顶窗口样式
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle & ~ WS_EX_TOPMOST ) ;
// 3. 停止置顶维护定时器
2025-08-13 12:52:56 +08:00
StopTopmostMaintenance ( ) ;
}
2025-08-13 11:54:36 +08:00
}
2025-08-13 12:52:56 +08:00
catch ( Exception ex )
2025-08-13 11:54:36 +08:00
{
2025-08-13 12:52:56 +08:00
LogHelper . WriteLogToFile ( $"应用窗口置顶失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 启动置顶维护定时器
/// </summary>
private void StartTopmostMaintenance ( )
{
2025-10-18 20:41:09 +08:00
if ( Settings . Advanced . EnableUIAccessTopMost )
{
return ;
}
2025-08-13 12:52:56 +08:00
if ( isTopmostMaintenanceEnabled ) return ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
if ( topmostMaintenanceTimer = = null )
{
topmostMaintenanceTimer = new DispatcherTimer ( ) ;
topmostMaintenanceTimer . Interval = TimeSpan . FromMilliseconds ( 500 ) ; // 每500ms检查一次
topmostMaintenanceTimer . Tick + = TopmostMaintenanceTimer_Tick ;
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
topmostMaintenanceTimer . Start ( ) ;
isTopmostMaintenanceEnabled = true ;
LogHelper . WriteLogToFile ( "启动置顶维护定时器" , LogHelper . LogType . Trace ) ;
}
/// <summary>
/// 停止置顶维护定时器
/// </summary>
private void StopTopmostMaintenance ( )
{
if ( topmostMaintenanceTimer ! = null & & isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Stop ( ) ;
isTopmostMaintenanceEnabled = false ;
LogHelper . WriteLogToFile ( "停止置顶维护定时器" , LogHelper . LogType . Trace ) ;
}
}
2025-11-15 18:37:23 +08:00
public void PauseTopmostMaintenance ( )
{
if ( topmostMaintenanceTimer ! = null & & isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Stop ( ) ;
}
}
public void ResumeTopmostMaintenance ( )
{
2025-12-20 13:56:46 +08:00
if ( Settings . Advanced . IsAlwaysOnTop & &
Settings . Advanced . IsNoFocusMode & &
2025-11-15 18:37:23 +08:00
! Settings . Advanced . EnableUIAccessTopMost )
{
if ( topmostMaintenanceTimer ! = null & & ! isTopmostMaintenanceEnabled )
{
topmostMaintenanceTimer . Start ( ) ;
isTopmostMaintenanceEnabled = true ;
}
}
}
2025-08-13 12:52:56 +08:00
/// <summary>
/// 置顶维护定时器事件
/// </summary>
private void TopmostMaintenanceTimer_Tick ( object sender , EventArgs e )
{
try
{
2025-10-18 20:41:09 +08:00
if ( Settings . Advanced . EnableUIAccessTopMost )
{
StopTopmostMaintenance ( ) ;
return ;
}
2025-08-13 12:52:56 +08:00
if ( ! Settings . Advanced . IsAlwaysOnTop | | ! Settings . Advanced . IsNoFocusMode )
{
StopTopmostMaintenance ( ) ;
return ;
}
var hwnd = new WindowInteropHelper ( this ) . Handle ;
if ( hwnd = = IntPtr . Zero ) return ;
// 检查窗口是否仍然可见且不是最小化状态
if ( ! IsWindow ( hwnd ) | | ! IsWindowVisible ( hwnd ) | | IsIconic ( hwnd ) )
{
return ;
}
2025-09-06 12:38:57 +08:00
// 检查是否有子窗口在前景
2025-09-06 13:48:44 +08:00
var foregroundWindow = GetForegroundWindow ( ) ;
if ( foregroundWindow ! = hwnd )
2025-09-06 12:38:57 +08:00
{
2025-09-06 13:48:44 +08:00
// 检查前景窗口是否是当前应用程序的子窗口
var foregroundWindowProcessId = GetWindowThreadProcessId ( foregroundWindow , out uint processId ) ;
var currentProcessId = GetCurrentProcessId ( ) ;
if ( processId = = currentProcessId )
{
// 如果有子窗口在前景,暂停置顶维护
return ;
}
// 如果窗口不在最顶层且没有子窗口,重新设置置顶
SetWindowPos ( hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 ,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER ) ;
// 确保窗口样式正确
int exStyle = GetWindowLong ( hwnd , GWL_EXSTYLE ) ;
if ( ( exStyle & WS_EX_TOPMOST ) = = 0 )
{
SetWindowLong ( hwnd , GWL_EXSTYLE , exStyle | WS_EX_TOPMOST ) ;
}
2025-08-13 12:52:56 +08:00
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"置顶维护定时器出错: {ex.Message}" , LogHelper . LogType . Error ) ;
2025-08-13 11:54:36 +08:00
}
}
/// <summary>
/// 根据窗口置顶设置和当前模式设置窗口的Topmost属性
/// </summary>
/// <param name="shouldBeTopmost">当前模式是否需要窗口置顶</param>
public void SetTopmostBasedOnSettings ( bool shouldBeTopmost )
{
if ( Settings . Advanced . IsAlwaysOnTop )
{
// 如果启用了窗口置顶设置,则始终置顶
Topmost = true ;
ApplyAlwaysOnTop ( ) ;
}
else
{
// 如果未启用窗口置顶设置,则根据当前模式决定
Topmost = shouldBeTopmost ;
if ( ! shouldBeTopmost )
{
ApplyAlwaysOnTop ( ) ; // 确保取消置顶
}
}
}
2025-07-25 18:21:16 +08:00
private void ToggleSwitchNoFocusMode_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . Advanced . IsNoFocusMode = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:29:52 +08:00
if ( isTemporarilyDisablingNoFocusMode )
{
isTemporarilyDisablingNoFocusMode = false ;
}
2025-12-20 13:56:46 +08:00
2025-07-25 18:21:16 +08:00
ApplyNoFocusMode ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
// 如果启用了窗口置顶,需要重新应用置顶设置以处理无焦点模式的变化
if ( Settings . Advanced . IsAlwaysOnTop )
{
ApplyAlwaysOnTop ( ) ;
}
2025-09-13 10:55:27 +08:00
2025-07-25 18:21:16 +08:00
}
2025-07-28 19:02:45 +08:00
2025-08-13 11:54:36 +08:00
private void ToggleSwitchAlwaysOnTop_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . Advanced . IsAlwaysOnTop = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
ApplyAlwaysOnTop ( ) ;
2025-10-18 18:26:13 +08:00
UpdateUIAccessTopMostVisibility ( ) ;
}
private void ToggleSwitchUIAccessTopMost_Toggled ( object sender , RoutedEventArgs e )
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
bool newValue = toggle ! = null & & toggle . IsOn ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
Settings . Advanced . EnableUIAccessTopMost = newValue ;
SaveSettingsToFile ( ) ;
ApplyUIAccessTopMost ( ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
App . IsUIAccessTopMostEnabled = newValue ;
2025-12-20 13:56:46 +08:00
2025-08-13 11:54:36 +08:00
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
private void Window_Activated ( object sender , EventArgs e )
{
// 窗口激活时,如果启用了置顶功能,重新应用置顶设置
if ( Settings . Advanced . IsAlwaysOnTop )
{
// 使用Dispatcher.BeginInvoke确保在UI线程上执行
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
ApplyAlwaysOnTop ( ) ;
2025-08-30 19:40:14 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-08-13 12:52:56 +08:00
}
}
2025-08-31 11:43:52 +08:00
2025-08-13 12:52:56 +08:00
/// <summary>
/// 窗口失去焦点时的处理
/// </summary>
private void Window_Deactivated ( object sender , EventArgs e )
{
// 窗口失去焦点时,如果启用了置顶功能且处于无焦点模式,重新应用置顶设置
if ( Settings . Advanced . IsAlwaysOnTop & & Settings . Advanced . IsNoFocusMode )
{
// 使用Dispatcher.BeginInvoke确保在UI线程上执行
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
ApplyAlwaysOnTop ( ) ;
2025-08-30 19:40:14 +08:00
} ) , DispatcherPriority . Loaded ) ;
2025-08-13 12:52:56 +08:00
}
}
2025-08-13 11:54:36 +08:00
2025-07-31 13:23:33 +08:00
2025-08-23 21:39:00 +08:00
#region 全 局 快 捷 键 管 理
2025-08-23 23:13:39 +08:00
/// <summary>
/// 初始化墨迹渐隐管理器
/// </summary>
private void InitializeInkFadeManager ( )
{
try
{
// 确保墨迹渐隐管理器已初始化
if ( _inkFadeManager = = null )
{
_inkFadeManager = new InkFadeManager ( this ) ;
}
// 同步设置状态
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
_inkFadeManager . UpdateFadeTime ( Settings . Canvas . InkFadeTime ) ;
LogHelper . WriteLogToFile ( "墨迹渐隐管理器已初始化" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化墨迹渐隐管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-23 21:39:00 +08:00
/// <summary>
/// 初始化全局快捷键管理器
/// </summary>
private void InitializeGlobalHotkeyManager ( )
{
try
{
_globalHotkeyManager = new GlobalHotkeyManager ( this ) ;
2025-08-30 11:29:16 +08:00
// 启动时加载快捷键,但默认为鼠标模式,禁用快捷键以放行键盘操作
2025-08-30 11:20:53 +08:00
_globalHotkeyManager . EnableHotkeyRegistration ( ) ;
2025-08-30 11:29:16 +08:00
// 启动时默认为鼠标模式,禁用快捷键
_globalHotkeyManager . UpdateHotkeyStateForToolMode ( true ) ;
LogHelper . WriteLogToFile ( "全局快捷键管理器已初始化,启动时默认为鼠标模式并禁用快捷键" , LogHelper . LogType . Event ) ;
2025-08-23 21:39:00 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"初始化全局快捷键管理器时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 打开快捷键设置窗口
/// </summary>
private void OpenHotkeySettingsWindow ( )
{
try
{
if ( _globalHotkeyManager = = null )
{
MessageBox . Show ( "快捷键管理器尚未初始化,请稍后重试。" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
return ;
}
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 暂时隐藏设置面板
BorderSettings . Visibility = Visibility . Hidden ;
BorderSettingsMask . Visibility = Visibility . Hidden ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:07:26 +08:00
// 创建快捷键设置窗口
2025-08-23 21:39:00 +08:00
var hotkeySettingsWindow = new HotkeySettingsWindow ( this , _globalHotkeyManager ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 设置窗口关闭事件,用于在快捷键设置窗口关闭后恢复设置面板
2025-09-06 15:07:26 +08:00
hotkeySettingsWindow . Closed + = ( s , e ) = >
{
2025-09-06 15:50:16 +08:00
// 恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-09-06 15:07:26 +08:00
} ;
2025-09-07 13:30:46 +08:00
2025-09-06 15:50:16 +08:00
// 显示快捷键设置窗口
2025-08-23 21:39:00 +08:00
hotkeySettingsWindow . ShowDialog ( ) ;
}
catch ( Exception ex )
{
2025-09-06 15:50:16 +08:00
// 确保在发生错误时也恢复设置面板显示
BorderSettings . Visibility = Visibility . Visible ;
BorderSettingsMask . Visibility = Visibility . Visible ;
2025-09-07 13:30:46 +08:00
2025-08-23 21:39:00 +08:00
LogHelper . WriteLogToFile ( $"打开快捷键设置窗口时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
MessageBox . Show ( $"打开快捷键设置窗口时出错: {ex.Message}" , "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
}
}
#endregion
2025-08-23 23:13:39 +08:00
#region 墨 迹 渐 隐 功 能
/// <summary>
/// 墨迹渐隐开关切换事件处理
/// </summary>
private void ToggleSwitchEnableInkFade_Toggled ( object sender , RoutedEventArgs e )
{
try
{
Settings . Canvas . EnableInkFade = ToggleSwitchEnableInkFade . IsOn ;
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
// 同步批注子面板中的开关状态
if ( ToggleSwitchInkFadeInPanel ! = null )
{
ToggleSwitchInkFadeInPanel . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-24 09:09:48 +08:00
// 同步普通画笔面板中的开关状态
if ( ToggleSwitchInkFadeInPanel2 ! = null )
{
ToggleSwitchInkFadeInPanel2 . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"墨迹渐隐功能已{(Settings.Canvas.EnableInkFade ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换墨迹渐隐功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 墨迹渐隐时间滑块值改变事件处理
/// </summary>
private void InkFadeTimeSlider_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
try
{
Settings . Canvas . InkFadeTime = ( int ) e . NewValue ;
2025-08-31 01:00:59 +08:00
if ( _inkFadeManager ! = null )
{
_inkFadeManager . UpdateFadeTime ( Settings . Canvas . InkFadeTime ) ;
}
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"墨迹渐隐时间已更新为 {Settings.Canvas.InkFadeTime}ms" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新墨迹渐隐时间时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 批注子面板中墨迹渐隐开关切换事件处理
/// </summary>
private void ToggleSwitchInkFadeInPanel_Toggled ( object sender , RoutedEventArgs e )
{
try
{
Settings . Canvas . EnableInkFade = ToggleSwitchInkFadeInPanel . IsOn ;
_inkFadeManager . IsEnabled = Settings . Canvas . EnableInkFade ;
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
// 同步设置面板中的开关状态
if ( ToggleSwitchEnableInkFade ! = null )
{
ToggleSwitchEnableInkFade . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-24 09:09:48 +08:00
// 同步普通画笔面板中的开关状态
if ( ToggleSwitchInkFadeInPanel2 ! = null )
{
ToggleSwitchInkFadeInPanel2 . IsOn = Settings . Canvas . EnableInkFade ;
}
2025-08-31 11:43:52 +08:00
2025-08-23 23:13:39 +08:00
LogHelper . WriteLogToFile ( $"批注子面板中墨迹渐隐功能已{(Settings.Canvas.EnableInkFade ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"批注子面板中切换墨迹渐隐功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 19:27:17 +08:00
2026-01-23 12:21:28 +08:00
/// <summary>
/// 在笔工具菜单中隐藏墨迹渐隐控制开关切换事件处理
/// </summary>
private void ToggleSwitchHideInkFadeControlInPenMenu_Toggled ( object sender , RoutedEventArgs e )
{
try
{
if ( isLoaded )
{
Settings . Canvas . HideInkFadeControlInPenMenu = ToggleSwitchHideInkFadeControlInPenMenu . IsOn ;
SaveSettingsToFile ( ) ;
}
// 立即更新墨迹渐隐控制开关的可见性
UpdateInkFadeControlVisibility ( ) ;
LogHelper . WriteLogToFile ( $"在笔工具菜单中隐藏墨迹渐隐控制开关已{(Settings.Canvas.HideInkFadeControlInPenMenu ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换在笔工具菜单中隐藏墨迹渐隐控制开关时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2026-02-14 00:23:05 +08:00
private void ToggleSwitchBrushAutoRestore_Toggled ( object sender , RoutedEventArgs e )
{
try
{
if ( ! isLoaded ) return ;
Settings . Canvas . EnableBrushAutoRestore = ToggleSwitchBrushAutoRestore . IsOn ;
SaveSettingsToFile ( ) ;
if ( Settings . Canvas . EnableBrushAutoRestore )
{
InitBrushAutoRestoreTimer ( ) ;
ScheduleBrushAutoRestore ( ) ;
}
else
{
if ( _brushAutoRestoreTimer ! = null )
{
_brushAutoRestoreTimer . Stop ( ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换画笔自动恢复功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
private void BrushAutoRestoreTimesTextBox_TextChanged ( object sender , TextChangedEventArgs e )
{
try
{
if ( ! isLoaded ) return ;
if ( Settings ? . Canvas = = null ) return ;
Settings . Canvas . BrushAutoRestoreTimes = BrushAutoRestoreTimesTextBox . Text ? ? string . Empty ;
SaveSettingsToFile ( ) ;
2026-02-14 12:49:39 +08:00
if ( Settings . Canvas . EnableBrushAutoRestore )
{
ScheduleBrushAutoRestore ( ) ;
}
2026-02-14 00:23:05 +08:00
}
catch ( Exception ex )
{
2026-02-14 12:49:39 +08:00
LogHelper . WriteLogToFile ( $"BrushAutoRestoreTimes: {ex.Message}" , LogHelper . LogType . Error ) ;
2026-02-14 00:23:05 +08:00
}
}
private void ComboBoxBrushAutoRestoreColor_SelectionChanged ( object sender , SelectionChangedEventArgs e )
{
try
{
if ( ! isLoaded ) return ;
if ( Settings ? . Canvas = = null ) return ;
if ( ComboBoxBrushAutoRestoreColor . SelectedItem is ComboBoxItem item )
{
string hex = item . Tag as string ? ? string . Empty ;
Settings . Canvas . BrushAutoRestoreColor = hex ;
SaveSettingsToFile ( ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新画笔自动恢复目标颜色时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
private void BrushAutoRestoreWidthSlider_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
try
{
if ( ! isLoaded ) return ;
if ( Settings ? . Canvas = = null ) return ;
Settings . Canvas . BrushAutoRestoreWidth = e . NewValue ;
SaveSettingsToFile ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新画笔自动恢复目标粗细时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
private void BrushAutoRestoreAlphaSlider_ValueChanged ( object sender , RoutedPropertyChangedEventArgs < double > e )
{
try
{
if ( ! isLoaded ) return ;
if ( Settings ? . Canvas = = null ) return ;
Settings . Canvas . BrushAutoRestoreAlpha = ( int ) e . NewValue ;
SaveSettingsToFile ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新画笔自动恢复目标透明度时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2026-01-23 12:21:28 +08:00
/// <summary>
/// 更新墨迹渐隐控制开关的可见性
/// </summary>
private void UpdateInkFadeControlVisibility ( )
{
try
{
bool isHidden = Settings . Canvas . HideInkFadeControlInPenMenu ;
// 控制 InkFadeControlPanel1(批注子面板中)的可见性
if ( InkFadeControlPanel1 ! = null )
{
InkFadeControlPanel1 . Visibility = isHidden ? Visibility . Collapsed : Visibility . Visible ;
}
// 控制 InkFadeControlPanel2(普通画笔面板中)的可见性
if ( InkFadeControlPanel2 ! = null )
{
InkFadeControlPanel2 . Visibility = isHidden ? Visibility . Collapsed : Visibility . Visible ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新墨迹渐隐控制面板可见性时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 19:27:17 +08:00
/// <summary>
/// PPT放映模式显示手势按钮开关切换事件处理
/// </summary>
private void ToggleSwitchShowGestureButtonInSlideShow_Toggled ( object sender , RoutedEventArgs e )
{
try
{
if ( ! isLoaded ) return ;
var toggle = sender as ToggleSwitch ;
Settings . PowerPointSettings . ShowGestureButtonInSlideShow = toggle ! = null & & toggle . IsOn ;
SaveSettingsToFile ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 19:27:17 +08:00
// 如果当前在PPT放映模式,需要立即更新手势按钮的显示状态
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
UpdateGestureButtonVisibilityInPPTMode ( ) ;
}
2025-09-07 13:30:46 +08:00
2025-09-06 19:27:17 +08:00
LogHelper . WriteLogToFile ( $"PPT放映模式显示手势按钮已{(Settings.PowerPointSettings.ShowGestureButtonInSlideShow ? " 启 用 " : " 禁 用 ")}" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换PPT放映模式显示手势按钮时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-12-27 22:41:24 +08:00
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 ( ) ;
2025-12-31 18:42:26 +08:00
// 如果当前在PPT放映模式,需要立即更新时间胶囊和快捷面板的显示状态
2025-12-27 22:41:24 +08:00
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
UpdatePPTTimeCapsuleVisibility ( ) ;
2025-12-31 18:42:26 +08:00
UpdatePPTQuickPanelVisibility ( ) ;
2025-12-27 22:41:24 +08:00
}
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 ) ;
}
}
2025-09-06 19:27:17 +08:00
/// <summary>
/// 更新PPT模式下手势按钮的显示状态
/// </summary>
private void UpdateGestureButtonVisibilityInPPTMode ( )
{
try
{
if ( Settings . PowerPointSettings . ShowGestureButtonInSlideShow )
{
2025-09-07 00:20:48 +08:00
// 如果启用了PPT放映模式显示手势按钮,则检查是否在批注模式下显示手势按钮
2025-09-06 19:27:17 +08:00
CheckEnableTwoFingerGestureBtnVisibility ( true ) ;
}
else
{
// 如果禁用了PPT放映模式显示手势按钮,则隐藏手势按钮
EnableTwoFingerGestureBorder . Visibility = Visibility . Collapsed ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新PPT模式下手势按钮显示状态时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-12-27 22:41:24 +08:00
/// <summary>
/// 更新PPT时间胶囊的显示状态
/// </summary>
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 ) ;
}
}
2025-12-31 18:42:26 +08:00
/// <summary>
/// 更新PPT快捷面板的显示状态
/// </summary>
public void UpdatePPTQuickPanelVisibility ( )
{
try
{
if ( PPTQuickPanelContainer = = null | | PPTQuickPanel = = null ) return ;
// 仅在PPT模式下显示
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
PPTQuickPanelContainer . Visibility = Visibility . Visible ;
PPTQuickPanel ? . UpdateVisibility ( true ) ;
}
else
{
PPTQuickPanelContainer . Visibility = Visibility . Collapsed ;
PPTQuickPanel ? . UpdateVisibility ( false ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新PPT快捷面板显示状态时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-12-27 22:41:24 +08:00
/// <summary>
/// 更新PPT时间胶囊的位置
/// </summary>
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 ) ;
}
}
2025-08-23 23:13:39 +08:00
#endregion
2025-08-24 11:08:13 +08:00
2025-08-30 11:20:53 +08:00
2025-08-31 12:03:58 +08:00
/// <summary>
/// 初始化文件关联状态显示
/// </summary>
private void InitializeFileAssociationStatus ( )
{
try
{
bool isRegistered = FileAssociationManager . IsFileAssociationRegistered ( ) ;
if ( isRegistered )
{
TextBlockFileAssociationStatus . Text = "✓ .icstk文件关联已注册" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightGreen ) ;
}
else
{
TextBlockFileAssociationStatus . Text = "✗ .icstk文件关联未注册" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightCoral ) ;
}
}
catch ( Exception ex )
{
TextBlockFileAssociationStatus . Text = "✗ 检查文件关联状态时出错" ;
TextBlockFileAssociationStatus . Foreground = new SolidColorBrush ( Colors . LightCoral ) ;
LogHelper . WriteLogToFile ( $"初始化文件关联状态显示时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-31 11:49:00 +08:00
/// <summary>
/// 处理命令行参数中的文件路径
/// </summary>
private void HandleCommandLineFileOpen ( )
{
try
{
// 检查启动参数中是否有.icstk文件
string icstkFile = FileAssociationManager . GetIcstkFileFromArgs ( App . StartArgs ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
if ( ! string . IsNullOrEmpty ( icstkFile ) )
{
LogHelper . WriteLogToFile ( $"检测到命令行参数中的.icstk文件: {icstkFile}" , LogHelper . LogType . Event ) ;
2025-09-07 13:30:46 +08:00
2025-08-31 11:49:00 +08:00
// 延迟执行,确保UI已完全加载
Dispatcher . BeginInvoke ( new Action ( ( ) = >
{
try
{
// 打开文件
OpenSingleStrokeFile ( icstkFile ) ;
ShowNotification ( $"已加载墨迹文件: {Path.GetFileName(icstkFile)}" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"打开命令行参数中的文件失败: {ex.Message}" , LogHelper . LogType . Error ) ;
ShowNotification ( "打开墨迹文件失败" ) ;
}
} ) , DispatcherPriority . Loaded ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"处理命令行文件打开时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-08-30 11:20:53 +08:00
/// <summary>
/// 集中管理工具模式切换和快捷键状态更新
/// 避免在每个工具按钮点击时重复刷新快捷键状态
/// </summary>
/// <param name="newMode">新的编辑模式</param>
/// <param name="additionalActions">可选的额外操作委托</param>
internal void SetCurrentToolMode ( InkCanvasEditingMode newMode , Action additionalActions = null )
{
try
{
2026-01-18 04:23:51 +08:00
// 如果切换到非橡皮擦模式,禁用橡皮擦覆盖层并重置橡皮擦状态
if ( newMode ! = InkCanvasEditingMode . EraseByPoint & & newMode ! = InkCanvasEditingMode . EraseByStroke )
{
DisableEraserOverlay ( ) ;
}
2025-08-30 11:20:53 +08:00
// 执行模式切换
inkCanvas . EditingMode = newMode ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 根据模式确定是否为鼠标模式(无工具模式)
bool isMouseMode = newMode = = InkCanvasEditingMode . None ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 更新快捷键状态
if ( _globalHotkeyManager ! = null )
{
_globalHotkeyManager . UpdateHotkeyStateForToolMode ( isMouseMode ) ;
}
2025-09-07 00:20:48 +08:00
// 在PPT放映模式下,工具模式切换时需要更新手势按钮的显示状态
if ( BtnPPTSlideShowEnd . Visibility = = Visibility . Visible )
{
UpdateGestureButtonVisibilityInPPTMode ( ) ;
}
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
// 执行额外的操作(如果有)
additionalActions ? . Invoke ( ) ;
2025-08-31 11:43:52 +08:00
2025-08-30 11:20:53 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"设置工具模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 13:16:10 +08:00
#region 滑 块 触 摸 支 持
/// <summary>
/// 为所有滑块控件添加触摸和手写笔事件支持
/// </summary>
private void AddTouchSupportToSliders ( )
{
try
{
// 获取所有滑块控件并添加触摸支持
var sliders = new List < Slider >
{
InkFadeTimeSlider ,
AutoStraightenLineThresholdSlider ,
LineStraightenSensitivitySlider ,
LineEndpointSnappingThresholdSlider ,
ViewboxFloatingBarScaleTransformValueSlider ,
ViewboxFloatingBarOpacityValueSlider ,
ViewboxFloatingBarOpacityInPPTValueSlider ,
PPTButtonLeftPositionValueSlider ,
PPTButtonRightPositionValueSlider ,
PPTButtonLBPositionValueSlider ,
PPTButtonRBPositionValueSlider ,
2025-12-20 22:56:13 +08:00
PPTLSButtonOpacityValueSlider ,
PPTRSButtonOpacityValueSlider ,
PPTLBButtonOpacityValueSlider ,
PPTRBButtonOpacityValueSlider ,
2025-09-06 13:16:10 +08:00
TouchMultiplierSlider ,
NibModeBoundsWidthSlider ,
FingerModeBoundsWidthSlider ,
SideControlMinimumAutomationSlider ,
RandWindowOnceCloseLatencySlider ,
RandWindowOnceMaxStudentsSlider ,
2025-10-06 20:44:09 +08:00
TimerVolumeSlider ,
2025-10-12 17:44:44 +08:00
ProgressiveReminderVolumeSlider ,
2025-09-06 13:16:10 +08:00
BoardInkWidthSlider ,
BoardInkAlphaSlider ,
BoardHighlighterWidthSlider ,
InkWidthSlider ,
InkAlphaSlider ,
2025-10-26 00:21:10 +08:00
HighlighterWidthSlider ,
MLAvoidanceHistorySlider ,
2026-02-14 00:23:05 +08:00
MLAvoidanceWeightSlider ,
BrushAutoRestoreWidthSlider ,
BrushAutoRestoreAlphaSlider
2025-09-06 13:16:10 +08:00
} ;
foreach ( var slider in sliders )
{
if ( slider ! = null )
{
AddTouchSupportToSlider ( slider ) ;
}
}
LogHelper . WriteLogToFile ( "已为所有滑块控件添加触摸支持" , LogHelper . LogType . Trace ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"添加滑块触摸支持时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 为单个滑块控件添加触摸和手写笔事件支持
/// </summary>
/// <param name="slider">要添加触摸支持的滑块控件</param>
private void AddTouchSupportToSlider ( Slider slider )
{
if ( slider = = null ) return ;
// 启用触摸和手写笔支持
slider . IsManipulationEnabled = true ;
2025-09-06 13:30:32 +08:00
// 添加触摸事件 - 使用更简单直接的方法
slider . TouchDown + = ( s , e ) = > HandleSliderTouch ( s , e , slider ) ;
slider . TouchMove + = ( s , e ) = > HandleSliderTouch ( s , e , slider ) ;
slider . TouchUp + = ( s , e ) = > HandleSliderTouchEnd ( s , e , slider ) ;
2025-09-06 13:16:10 +08:00
// 添加手写笔事件
2025-09-06 13:30:32 +08:00
slider . StylusDown + = ( s , e ) = > HandleSliderStylus ( s , e , slider ) ;
slider . StylusMove + = ( s , e ) = > HandleSliderStylus ( s , e , slider ) ;
slider . StylusUp + = ( s , e ) = > HandleSliderStylusEnd ( s , e , slider ) ;
2025-09-06 13:16:10 +08:00
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块触摸事件(按下和移动)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderTouch ( object sender , TouchEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 捕获触摸设备
2025-09-07 00:11:22 +08:00
if ( e . RoutedEvent = = TouchDownEvent )
2025-09-06 13:30:32 +08:00
{
slider . CaptureTouch ( e . TouchDevice ) ;
}
2025-09-06 13:16:10 +08:00
// 计算触摸位置对应的滑块值
var touchPoint = e . GetTouchPoint ( slider ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
// 使用更精确的位置计算方法
UpdateSliderValueFromPositionImproved ( slider , touchPoint . Position ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块触摸结束事件
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderTouchEnd ( object sender , TouchEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 释放触摸捕获
slider . ReleaseTouchCapture ( e . TouchDevice ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块手写笔事件(按下和移动)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderStylus ( object sender , StylusEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 捕获手写笔设备
2025-09-07 00:11:22 +08:00
if ( e . RoutedEvent = = StylusDownEvent )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
slider . CaptureStylus ( ) ;
2025-09-06 13:16:10 +08:00
}
// 计算手写笔位置对应的滑块值
var stylusPoint = e . GetStylusPoints ( slider ) ;
if ( stylusPoint . Count > 0 )
{
2025-09-06 13:30:32 +08:00
UpdateSliderValueFromPositionImproved ( slider , stylusPoint [ 0 ] . ToPoint ( ) ) ;
2025-09-06 13:16:10 +08:00
}
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 处理滑块手写笔结束事件
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
private void HandleSliderStylusEnd ( object sender , StylusEventArgs e , Slider slider )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
// 释放手写笔捕获
slider . ReleaseStylusCapture ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
e . Handled = true ;
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 根据触摸/手写笔位置更新滑块值(改进版本)
2025-09-06 13:16:10 +08:00
/// </summary>
2025-09-06 13:30:32 +08:00
/// <param name="slider">滑块控件</param>
/// <param name="position">触摸/手写笔位置</param>
private void UpdateSliderValueFromPositionImproved ( Slider slider , Point position )
2025-09-06 13:16:10 +08:00
{
if ( slider = = null ) return ;
2025-09-06 13:30:32 +08:00
try
{
// 获取滑块的轨道元素
2025-09-30 19:15:03 +08:00
var track = slider . Template . FindName ( "PART_Track" , slider ) as Track ;
if ( track = = null )
2025-09-06 13:30:32 +08:00
{
// 如果找不到轨道,使用简单方法
UpdateSliderValueFromPosition ( slider , position ) ;
return ;
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 获取轨道的实际边界
var trackBounds = track . TransformToAncestor ( slider ) . TransformBounds ( new Rect ( 0 , 0 , track . ActualWidth , track . ActualHeight ) ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
double relativePosition = 0 ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
if ( slider . Orientation = = System . Windows . Controls . Orientation . Horizontal )
{
// 水平滑块
if ( trackBounds . Width > 0 )
{
// 计算相对于轨道的相对位置
var relativeX = position . X - trackBounds . X ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , relativeX / trackBounds . Width ) ) ;
}
}
else
{
// 垂直滑块
if ( trackBounds . Height > 0 )
{
// 计算相对于轨道的相对位置
var relativeY = position . Y - trackBounds . Y ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , relativeY / trackBounds . Height ) ) ;
}
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 计算新的滑块值
var newValue = slider . Minimum + relativePosition * ( slider . Maximum - slider . Minimum ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:30:32 +08:00
// 如果启用了吸附到刻度,则调整到最近的刻度
if ( slider . IsSnapToTickEnabled & & slider . TickFrequency > 0 )
{
var tickCount = ( int ) ( ( slider . Maximum - slider . Minimum ) / slider . TickFrequency ) ;
var tickIndex = ( int ) Math . Round ( relativePosition * tickCount ) ;
newValue = slider . Minimum + tickIndex * slider . TickFrequency ;
}
2025-09-06 13:16:10 +08:00
2025-09-06 13:30:32 +08:00
// 更新滑块值
slider . Value = Math . Max ( slider . Minimum , Math . Min ( slider . Maximum , newValue ) ) ;
}
catch ( Exception ex )
{
// 如果改进方法失败,回退到简单方法
UpdateSliderValueFromPosition ( slider , position ) ;
LogHelper . WriteLogToFile ( $"更新滑块值时出错,使用回退方法: {ex.Message}" , LogHelper . LogType . Error ) ;
}
2025-09-06 13:16:10 +08:00
}
/// <summary>
2025-09-06 13:30:32 +08:00
/// 根据触摸/手写笔位置更新滑块值(简单版本)
2025-09-06 13:16:10 +08:00
/// </summary>
/// <param name="slider">滑块控件</param>
/// <param name="position">触摸/手写笔位置</param>
private void UpdateSliderValueFromPosition ( Slider slider , Point position )
{
if ( slider = = null ) return ;
try
{
2025-09-06 13:30:32 +08:00
// 使用更简单直接的方法计算滑块值
2025-09-06 13:16:10 +08:00
double relativePosition = 0 ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
if ( slider . Orientation = = System . Windows . Controls . Orientation . Horizontal )
{
2025-09-06 13:30:32 +08:00
// 水平滑块 - 使用滑块的实际宽度
var sliderWidth = slider . ActualWidth ;
if ( sliderWidth > 0 )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
// 考虑滑块的边距和拇指大小
var thumbSize = 20 ; // 假设拇指大小约为20像素
var effectiveWidth = sliderWidth - thumbSize ;
var adjustedX = position . X - thumbSize / 2 ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , adjustedX / effectiveWidth ) ) ;
2025-09-06 13:16:10 +08:00
}
}
else
{
2025-09-06 13:30:32 +08:00
// 垂直滑块 - 使用滑块的实际高度
var sliderHeight = slider . ActualHeight ;
if ( sliderHeight > 0 )
2025-09-06 13:16:10 +08:00
{
2025-09-06 13:30:32 +08:00
// 考虑滑块的边距和拇指大小
var thumbSize = 20 ; // 假设拇指大小约为20像素
var effectiveHeight = sliderHeight - thumbSize ;
var adjustedY = position . Y - thumbSize / 2 ;
relativePosition = Math . Max ( 0 , Math . Min ( 1 , adjustedY / effectiveHeight ) ) ;
2025-09-06 13:16:10 +08:00
}
}
// 计算新的滑块值
var newValue = slider . Minimum + relativePosition * ( slider . Maximum - slider . Minimum ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 13:16:10 +08:00
// 如果启用了吸附到刻度,则调整到最近的刻度
if ( slider . IsSnapToTickEnabled & & slider . TickFrequency > 0 )
{
var tickCount = ( int ) ( ( slider . Maximum - slider . Minimum ) / slider . TickFrequency ) ;
var tickIndex = ( int ) Math . Round ( relativePosition * tickCount ) ;
newValue = slider . Minimum + tickIndex * slider . TickFrequency ;
}
// 更新滑块值
slider . Value = Math . Max ( slider . Minimum , Math . Min ( slider . Maximum , newValue ) ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新滑块值时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
#endregion
2025-09-06 21:26:46 +08:00
#region 模 式 切 换 相 关
/// <summary>
/// 模式切换开关事件处理
/// </summary>
private void ToggleSwitchMode_Toggled ( object sender , RoutedEventArgs e )
{
try
{
2025-09-30 19:15:03 +08:00
var toggle = sender as ToggleSwitch ;
if ( toggle ! = null )
2025-09-06 21:26:46 +08:00
{
Settings . ModeSettings . IsPPTOnlyMode = toggle . IsOn ;
2025-10-03 17:08:46 +08:00
2025-09-13 10:51:08 +08:00
// 保存设置到文件
SaveSettingsToFile ( ) ;
2025-09-07 13:30:46 +08:00
2025-09-06 21:26:46 +08:00
// 如果切换到仅PPT模式,立即隐藏主窗口
if ( Settings . ModeSettings . IsPPTOnlyMode )
{
Hide ( ) ;
LogHelper . WriteLogToFile ( "已切换到仅PPT模式,主窗口已隐藏" , LogHelper . LogType . Event ) ;
}
else
{
// 如果切换到正常模式,显示主窗口
Show ( ) ;
LogHelper . WriteLogToFile ( "已切换到正常模式,主窗口已显示" , LogHelper . LogType . Event ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 检查是否应该显示主窗口(基于PPT模式和PPT放映状态)
/// </summary>
private void CheckMainWindowVisibility ( )
{
try
{
if ( Settings . ModeSettings . IsPPTOnlyMode )
{
// 仅PPT模式下,只有在PPT放映时才显示
bool isInSlideShow = BtnPPTSlideShowEnd . Visibility = = Visibility . Visible ;
if ( isInSlideShow & & ! IsVisible )
{
Show ( ) ;
LogHelper . WriteLogToFile ( "PPT放映开始,显示主窗口(仅PPT模式)" , LogHelper . LogType . Trace ) ;
}
else if ( ! isInSlideShow & & IsVisible )
{
Hide ( ) ;
}
}
else
{
// 正常模式下,确保主窗口可见
if ( ! IsVisible )
{
Show ( ) ;
}
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"检查主窗口可见性时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-13 14:59:47 +08:00
/// <summary>
/// 切换到白板模式(用于--board参数和IPC命令)
/// 调用浮动栏上的白板功能
/// </summary>
public void SwitchToBoardMode ( )
{
try
{
LogHelper . WriteLogToFile ( "开始切换到白板模式" , LogHelper . LogType . Event ) ;
// 调用浮动栏上的白板功能
ImageBlackboard_MouseUp ( null , null ) ;
LogHelper . WriteLogToFile ( "已成功切换到白板模式" , LogHelper . LogType . Event ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换到白板模式时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-09-06 21:26:46 +08:00
#endregion
2025-09-21 00:25:09 +08:00
#region Theme Toggle
/// <summary>
/// 主题下拉框选择变化事件
/// </summary>
private void ComboBoxTheme_SelectionChanged ( object sender , SelectionChangedEventArgs e )
{
if ( ! isLoaded ) return ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
try
{
2025-09-30 19:15:03 +08:00
System . Windows . Controls . ComboBox comboBox = sender as System . Windows . Controls . ComboBox ;
if ( comboBox ! = null )
2025-09-21 00:25:09 +08:00
{
Settings . Appearance . Theme = comboBox . SelectedIndex ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 应用新主题
ApplyTheme ( comboBox . SelectedIndex ) ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 保存设置
SaveSettingsToFile ( ) ;
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 显示通知
string themeName ;
switch ( comboBox . SelectedIndex )
{
case 0 :
themeName = "浅色主题" ;
break ;
case 1 :
themeName = "深色主题" ;
break ;
case 2 :
themeName = "跟随系统" ;
break ;
default :
themeName = "未知主题" ;
break ;
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
ShowNotification ( $"已切换到{themeName}" ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"切换主题时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
ShowNotification ( "主题切换失败" ) ;
}
}
/// <summary>
/// 应用指定主题
/// </summary>
/// <param name="themeIndex">主题索引:0-浅色,1-深色,2-跟随系统</param>
private void ApplyTheme ( int themeIndex )
{
try
{
switch ( themeIndex )
{
case 0 : // 浅色主题
2025-10-04 17:14:14 +08:00
SetTheme ( "Light" , true ) ;
2025-09-21 00:25:09 +08:00
// 浅色主题下设置浮动栏为完全不透明
ViewboxFloatingBar . Opacity = 1.0 ;
break ;
case 1 : // 深色主题
2025-10-04 17:14:14 +08:00
SetTheme ( "Dark" , true ) ;
2025-09-21 00:25:09 +08:00
// 深色主题下设置浮动栏为完全不透明
ViewboxFloatingBar . Opacity = 1.0 ;
break ;
case 2 : // 跟随系统
if ( IsSystemThemeLight ( ) )
{
2025-10-04 17:14:14 +08:00
SetTheme ( "Light" , true ) ;
2025-09-21 00:25:09 +08:00
ViewboxFloatingBar . Opacity = 1.0 ;
}
else
{
2025-10-04 17:14:14 +08:00
SetTheme ( "Dark" , true ) ;
2025-09-21 00:25:09 +08:00
ViewboxFloatingBar . Opacity = 1.0 ;
}
break ;
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
// 强制刷新通知框的颜色资源
RefreshNotificationColors ( ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"应用主题时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
/// <summary>
/// 刷新通知框的颜色资源
/// </summary>
private void RefreshNotificationColors ( )
{
try
{
// 强制刷新通知框的背景和前景色
var border = GridNotifications . Children . OfType < Border > ( ) . FirstOrDefault ( ) ;
if ( border ! = null )
{
border . Background = ( Brush ) Application . Current . FindResource ( "SettingsPageBackground" ) ;
border . BorderBrush = new SolidColorBrush ( Color . FromRgb ( 185 , 28 , 28 ) ) ; // 保持红色边框
}
2025-10-03 17:08:46 +08:00
2025-09-21 00:25:09 +08:00
TextBlockNotice . Foreground = ( Brush ) Application . Current . FindResource ( "SettingsPageForeground" ) ;
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"刷新通知框颜色时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
#endregion
2025-10-18 18:26:13 +08:00
#region UIA置顶功能
/// <summary>
/// 更新UIA置顶开关的可见性
/// </summary>
private void UpdateUIAccessTopMostVisibility ( )
{
try
{
var visibility = Settings . Advanced . IsAlwaysOnTop ? Visibility . Visible : Visibility . Collapsed ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( UIAccessTopMostPanel ! = null )
{
UIAccessTopMostPanel . Visibility = visibility ;
}
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( UIAccessTopMostDescription ! = null )
{
UIAccessTopMostDescription . Visibility = visibility ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"更新UIA置顶开关可见性时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
/// <summary>
/// 应用UIA置顶功能
/// </summary>
private void ApplyUIAccessTopMost ( )
{
try
{
if ( Settings . Advanced . EnableUIAccessTopMost & & Settings . Advanced . IsAlwaysOnTop )
{
// 检查是否以管理员权限运行
var identity = WindowsIdentity . GetCurrent ( ) ;
var principal = new WindowsPrincipal ( identity ) ;
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
if ( principal . IsInRole ( WindowsBuiltInRole . Administrator ) )
{
try
{
2025-10-25 19:57:56 +08:00
timerKillProcess . Stop ( ) ;
if ( App . watchdogProcess ! = null & & ! App . watchdogProcess . HasExited )
{
App . watchdogProcess . Kill ( ) ;
App . watchdogProcess = null ;
}
2025-12-20 13:56:46 +08:00
2025-10-18 18:26:13 +08:00
// 调用UIAccess DLL
if ( Environment . Is64BitProcess )
{
PrepareUIAccessX64 ( ) ;
}
else
{
PrepareUIAccessX86 ( ) ;
}
2025-12-20 13:56:46 +08:00
2025-10-25 19:57:56 +08:00
App . StartWatchdogIfNeeded ( ) ;
timerKillProcess . Start ( ) ;
2025-10-18 18:26:13 +08:00
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"启用UIA置顶功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
else
{
LogHelper . WriteLogToFile ( "UIA置顶功能需要管理员权限" , LogHelper . LogType . Warning ) ;
}
}
else
{
LogHelper . WriteLogToFile ( "UIA置顶功能已禁用" , LogHelper . LogType . Trace ) ;
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"应用UIA置顶功能时出错: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-26 00:21:10 +08:00
/// <summary>
/// 显示快抽悬浮按钮
/// </summary>
private void ShowQuickDrawFloatingButton ( )
{
try
{
2025-11-08 22:17:52 +08:00
var quickDrawButton = FindName ( "QuickDrawFloatingButton" ) as Controls . QuickDrawFloatingButtonControl ;
2025-11-08 22:07:38 +08:00
if ( quickDrawButton = = null ) return ;
2025-10-26 00:21:10 +08:00
// 检查设置是否启用快抽功能
2025-11-08 22:07:38 +08:00
if ( Settings ? . RandSettings ? . EnableQuickDraw = = true )
2025-10-26 00:21:10 +08:00
{
2025-11-08 22:07:38 +08:00
quickDrawButton . Visibility = Visibility . Visible ;
2025-10-26 00:21:10 +08:00
}
2025-11-08 22:07:38 +08:00
else
2025-10-26 00:21:10 +08:00
{
2025-11-08 22:07:38 +08:00
quickDrawButton . Visibility = Visibility . Collapsed ;
2025-10-26 00:21:10 +08:00
}
}
catch ( Exception ex )
{
LogHelper . WriteLogToFile ( $"显示快抽悬浮按钮失败: {ex.Message}" , LogHelper . LogType . Error ) ;
}
}
2025-10-18 18:26:13 +08:00
#endregion
2025-05-25 09:29:48 +08:00
}
2025-08-13 21:59:35 +08:00
}