diff --git a/.gitignore b/.gitignore
index 2b9a7ea6..c4df8dfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,428 @@
-obj/
-bin/
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+*.env
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+
+[Dd]ebug/x64/
+[Dd]ebugPublic/x64/
+[Rr]elease/x64/
+[Rr]eleases/x64/
+bin/x64/
+obj/x64/
+
+[Dd]ebug/x86/
+[Dd]ebugPublic/x86/
+[Rr]elease/x86/
+[Rr]eleases/x86/
+bin/x86/
+obj/x86/
+
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+[Aa][Rr][Mm]64[Ee][Cc]/
+bld/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Build results on 'Bin' directories
+**/[Bb]in/*
+# Uncomment if you have tasks that rely on *.refresh files to move binaries
+# (https://github.com/github/gitignore/pull/3736)
+#!**/[Bb]in/*.refresh
+
+# Visual Studio 2015/2017 cache/options directory
.vs/
-/Ink Canvas/obj
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+*.trx
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Approval Tests result files
+*.received.*
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.idb
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+# but not Directory.Build.rsp, as it configures directory-level build defaults
+!Directory.Build.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.tlog
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio 6 workspace and project file (working project files containing files to include in project)
+*.dsw
+*.dsp
+
+# Visual Studio 6 technical files
+*.ncb
+*.aps
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+**/.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+**/.fake/
+
+# CodeRush personal settings
+**/.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+**/__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+#tools/**
+#!tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+MSBuild_Logs/
+
+# AWS SAM Build and Temporary Artifacts folder
+.aws-sam
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+**/.mfractor/
+
+# Local History for Visual Studio
+**/.localhistory/
+
+# Visual Studio History (VSHistory) files
+.vshistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+**/.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# VS Code files for those working on multiple tools
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+!.vscode/*.code-snippets
+
+# Local History for Visual Studio Code
+.history/
+
+# Built Visual Studio Code Extensions
+*.vsix
+
+# Windows Installer files from build outputs
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
\ No newline at end of file
diff --git a/AutomaticUpdateVersionControl.txt b/AutomaticUpdateVersionControl.txt
index 5ad6231c..b1522148 100644
--- a/AutomaticUpdateVersionControl.txt
+++ b/AutomaticUpdateVersionControl.txt
@@ -1 +1 @@
-1.7.13.0
+1.7.14.0
diff --git a/Ink Canvas.sln.DotSettings.user b/Ink Canvas.sln.DotSettings.user
deleted file mode 100644
index 21fc1f49..00000000
--- a/Ink Canvas.sln.DotSettings.user
+++ /dev/null
@@ -1,6 +0,0 @@
-
- WARNING
- C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\amd64\MSBuild.exe
- 1114112
- True
- True
\ No newline at end of file
diff --git a/Ink Canvas/App.xaml b/Ink Canvas/App.xaml
index 83167e38..aba31b16 100644
--- a/Ink Canvas/App.xaml
+++ b/Ink Canvas/App.xaml
@@ -232,7 +232,7 @@
ContextMenu="{StaticResource SysTrayMenu}"
IconSource="/Resources/icc.ico"/>
-
+
diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index 1e04932d..973fd1ee 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -41,6 +41,8 @@ namespace Ink_Canvas
private static Process watchdogProcess;
// 新增:标记是否为软件内主动退出
public static bool IsAppExitByUser;
+ // 新增:标记是否启用了UIA置顶功能
+ public static bool IsUIAccessTopMostEnabled;
// 新增:退出信号文件路径
private static string watchdogExitSignalFile = Path.Combine(Path.GetTempPath(), "icc_watchdog_exit_" + Process.GetCurrentProcess().Id + ".flag");
// 新增:崩溃日志文件路径
@@ -629,7 +631,7 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile("App | 检测到最终应用启动(更新后的应用)");
}
- // 在应用启动时自动释放IACore相关DLL
+ // 释放IACore相关DLL
if (_isSplashScreenShown)
{
SetSplashMessage("正在初始化组件...");
@@ -645,6 +647,22 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"释放IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
}
+ // 释放UIAccess DLL
+ if (_isSplashScreenShown)
+ {
+ SetSplashMessage("正在初始化组件...");
+ SetSplashProgress(50);
+ await Task.Delay(300);
+ }
+ try
+ {
+ UIAccessDllExtractor.ExtractUIAccessDlls();
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"释放UIAccess DLL时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+
// 记录应用启动(设备标识符)
if (_isSplashScreenShown)
{
@@ -1055,7 +1073,15 @@ namespace Ink_Canvas
Thread.Sleep(2000);
}
// 主进程异常退出,自动重启前判断崩溃后操作
- SyncCrashActionFromSettings(); // 新增:同步设置
+ SyncCrashActionFromSettings(); // 同步设置
+
+ if (IsUIAccessTopMostEnabled)
+ {
+ string exePath = Process.GetCurrentProcess().MainModule.FileName;
+ Process.Start(exePath);
+ Environment.Exit(0);
+ }
+
if (CrashAction == CrashActionType.SilentRestart)
{
StartupCount.Increment();
@@ -1068,7 +1094,6 @@ namespace Ink_Canvas
string exePath = Process.GetCurrentProcess().MainModule.FileName;
Process.Start(exePath);
}
- // CrashActionType.NoAction 时不重启,直接退出
}
catch { }
Environment.Exit(0);
diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs
index 567d00bd..5d61c823 100644
--- a/Ink Canvas/AssemblyInfo.cs
+++ b/Ink Canvas/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.13.0")]
-[assembly: AssemblyFileVersion("1.7.13.0")]
+[assembly: AssemblyVersion("1.7.14.0")]
+[assembly: AssemblyFileVersion("1.7.14.0")]
diff --git a/Ink Canvas/FodyWeavers.xsd b/Ink Canvas/FodyWeavers.xsd
deleted file mode 100644
index f2dbece7..00000000
--- a/Ink Canvas/FodyWeavers.xsd
+++ /dev/null
@@ -1,176 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
-
-
-
-
- A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
-
-
-
-
- A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
-
-
-
-
- A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
-
-
-
-
- Obsolete, use UnmanagedWinX86Assemblies instead
-
-
-
-
- A list of unmanaged X86 (32 bit) assembly names to include, delimited with line breaks.
-
-
-
-
- Obsolete, use UnmanagedWinX64Assemblies instead.
-
-
-
-
- A list of unmanaged X64 (64 bit) assembly names to include, delimited with line breaks.
-
-
-
-
- A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with line breaks.
-
-
-
-
- The order of preloaded assemblies, delimited with line breaks.
-
-
-
-
-
- This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
-
-
-
-
- Controls if .pdbs for reference assemblies are also embedded.
-
-
-
-
- Controls if runtime assemblies are also embedded.
-
-
-
-
- Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
-
-
-
-
- Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
-
-
-
-
- As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
-
-
-
-
- The attach method no longer subscribes to the `AppDomain.AssemblyResolve` (.NET 4.x) and `AssemblyLoadContext.Resolving` (.NET 6.0+) events.
-
-
-
-
- Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
-
-
-
-
- Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
-
-
-
-
- A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
-
-
-
-
- A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
-
-
-
-
- A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
-
-
-
-
- A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
-
-
-
-
- Obsolete, use UnmanagedWinX86Assemblies instead
-
-
-
-
- A list of unmanaged X86 (32 bit) assembly names to include, delimited with |.
-
-
-
-
- Obsolete, use UnmanagedWinX64Assemblies instead
-
-
-
-
- A list of unmanaged X64 (64 bit) assembly names to include, delimited with |.
-
-
-
-
- A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with |.
-
-
-
-
- The order of preloaded assemblies, delimited with |.
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/Ink Canvas/Helpers/Converters.cs b/Ink Canvas/Helpers/Converters.cs
index ac5f7612..31feedb0 100644
--- a/Ink Canvas/Helpers/Converters.cs
+++ b/Ink Canvas/Helpers/Converters.cs
@@ -112,4 +112,27 @@ namespace Ink_Canvas.Converter
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }
}
+
+ public class InverseBooleanToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if ((bool)value)
+ {
+ return Visibility.Collapsed;
+ }
+
+ return Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if ((bool)value)
+ {
+ return Visibility.Collapsed;
+ }
+
+ return Visibility.Visible;
+ }
+ }
}
diff --git a/Ink Canvas/FloatingWindowInterceptorManager.cs b/Ink Canvas/Helpers/FloatingWindowInterceptorManager.cs
similarity index 100%
rename from Ink Canvas/FloatingWindowInterceptorManager.cs
rename to Ink Canvas/Helpers/FloatingWindowInterceptorManager.cs
diff --git a/Ink Canvas/Helpers/GlobalHotkeyManager.cs b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
index 8df7924d..11fea5a5 100644
--- a/Ink Canvas/Helpers/GlobalHotkeyManager.cs
+++ b/Ink Canvas/Helpers/GlobalHotkeyManager.cs
@@ -865,7 +865,7 @@ namespace Ink_Canvas.Helpers
{
// 检查当前是否处于鼠标模式
bool isMouseMode = IsInSelectMode();
-
+
if (isMouseMode)
{
// 鼠标模式下,根据设置决定是否启用快捷键
@@ -874,7 +874,7 @@ namespace Ink_Canvas.Helpers
else
{
// 非鼠标模式下,需要检查焦点和屏幕位置
-
+
// 策略1:鼠标在窗口上时启用热键(最高优先级)
if (_isMouseOverWindow)
{
diff --git a/Ink Canvas/Helpers/PPTInkManager.cs b/Ink Canvas/Helpers/PPTInkManager.cs
index cad06029..b450aae8 100644
--- a/Ink Canvas/Helpers/PPTInkManager.cs
+++ b/Ink Canvas/Helpers/PPTInkManager.cs
@@ -162,7 +162,7 @@ namespace Ink_Canvas.Helpers
}
///
- /// 强制保存指定页面的墨迹(忽略锁定状态)
+ /// 强制保存指定页面的墨迹
///
public void ForceSaveSlideStrokes(int slideIndex, StrokeCollection strokes)
{
diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs
index 11e1bbed..732409ad 100644
--- a/Ink Canvas/Helpers/PPTManager.cs
+++ b/Ink Canvas/Helpers/PPTManager.cs
@@ -678,14 +678,25 @@ namespace Ink_Canvas.Helpers
{
try
{
- if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false;
+ if (!IsConnected || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false;
- var slideShowWindow = PPTApplication.SlideShowWindows[1];
- if (slideShowWindow?.View != null)
+ if (IsInSlideShow && PPTApplication.SlideShowWindows.Count >= 1)
{
- slideShowWindow.View.GotoSlide(slideNumber);
- return true;
+ var slideShowWindow = PPTApplication.SlideShowWindows[1];
+ if (slideShowWindow?.View != null)
+ {
+ slideShowWindow.View.GotoSlide(slideNumber);
+ return true;
+ }
+ }
+ else if (CurrentPresentation != null)
+ {
+ if (CurrentPresentation.Windows?.Count >= 1)
+ {
+ CurrentPresentation.Windows[1].View.GotoSlide(slideNumber);
+ return true;
+ }
}
return false;
}
diff --git a/Ink Canvas/Helpers/UIAccessDllExtractor.cs b/Ink Canvas/Helpers/UIAccessDllExtractor.cs
new file mode 100644
index 00000000..f9448167
--- /dev/null
+++ b/Ink Canvas/Helpers/UIAccessDllExtractor.cs
@@ -0,0 +1,165 @@
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// UIAccess DLL释放器
+ ///
+ public static class UIAccessDllExtractor
+ {
+ private static readonly string[] RequiredDlls = {
+ "UIAccessDLL_x64.dll",
+ "UIAccessDLL_x86.dll"
+ };
+
+ ///
+ /// 在应用启动时释放UIAccess相关DLL
+ ///
+ public static void ExtractUIAccessDlls()
+ {
+ try
+ {
+ string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
+ LogHelper.WriteLogToFile("开始检查并释放UIAccess相关DLL文件");
+
+ foreach (string dllName in RequiredDlls)
+ {
+ string targetPath = Path.Combine(appDirectory, dllName);
+
+ // 检查文件是否已存在且有效
+ if (File.Exists(targetPath) && IsValidDll(targetPath))
+ {
+ LogHelper.WriteLogToFile($"{dllName} 已存在且有效,跳过释放");
+ continue;
+ }
+
+ // 从嵌入资源中释放DLL
+ if (ExtractDllFromResource(dllName, targetPath))
+ {
+ LogHelper.WriteLogToFile($"成功释放 {dllName} 到 {targetPath}");
+ }
+ else
+ {
+ LogHelper.WriteLogToFile($"警告:无法释放 {dllName},可能影响UIA置顶功能", LogHelper.LogType.Warning);
+ }
+ }
+
+ LogHelper.WriteLogToFile("UIAccess DLL释放检查完成");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"释放UIAccess DLL时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 从嵌入资源中提取DLL文件
+ ///
+ private static bool ExtractDllFromResource(string dllName, string targetPath)
+ {
+ try
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly();
+ string resourceName = $"Ink_Canvas.{dllName}";
+
+ using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
+ {
+ if (resourceStream == null)
+ {
+ LogHelper.WriteLogToFile($"未找到嵌入资源: {resourceName}", LogHelper.LogType.Warning);
+ return false;
+ }
+
+ // 确保目标目录存在
+ string targetDirectory = Path.GetDirectoryName(targetPath);
+ if (!Directory.Exists(targetDirectory))
+ {
+ Directory.CreateDirectory(targetDirectory);
+ }
+
+ // 写入文件
+ using (FileStream fileStream = new FileStream(targetPath, FileMode.Create, FileAccess.Write))
+ {
+ resourceStream.CopyTo(fileStream);
+ }
+
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"从资源提取 {dllName} 失败: {ex.Message}", LogHelper.LogType.Error);
+ return false;
+ }
+ }
+
+ ///
+ /// 检查DLL文件是否有效
+ ///
+ private static bool IsValidDll(string filePath)
+ {
+ try
+ {
+ if (!File.Exists(filePath))
+ return false;
+
+ FileInfo fileInfo = new FileInfo(filePath);
+
+ // 检查文件大小(空文件或过小的文件可能无效)
+ if (fileInfo.Length < 1024) // 小于1KB可能无效
+ return false;
+
+ // 简单检查PE头(DLL文件应该以MZ开头)
+ using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
+ {
+ byte[] buffer = new byte[2];
+ if (fs.Read(buffer, 0, 2) == 2)
+ {
+ return buffer[0] == 0x4D && buffer[1] == 0x5A; // "MZ"
+ }
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 清理释放的DLL文件(可选,在应用退出时调用)
+ ///
+ public static void CleanupExtractedDlls()
+ {
+ try
+ {
+ string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
+
+ foreach (string dllName in RequiredDlls)
+ {
+ string filePath = Path.Combine(appDirectory, dllName);
+
+ if (File.Exists(filePath))
+ {
+ try
+ {
+ File.Delete(filePath);
+ LogHelper.WriteLogToFile($"已清理 {dllName}");
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"清理 {dllName} 失败: {ex.Message}", LogHelper.LogType.Warning);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"清理UIAccess DLL时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+ }
+}
diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj
index 24d06117..1c25b961 100644
--- a/Ink Canvas/InkCanvasForClass.csproj
+++ b/Ink Canvas/InkCanvasForClass.csproj
@@ -192,11 +192,14 @@
+
+
+
@@ -243,6 +246,10 @@
+
+
+
+
diff --git a/Ink Canvas/InkCanvasForClass.csproj.user b/Ink Canvas/InkCanvasForClass.csproj.user
deleted file mode 100644
index 92ed6d11..00000000
--- a/Ink Canvas/InkCanvasForClass.csproj.user
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- <_LastSelectedProfileId>D:\vs\ica\Ink Canvas\Properties\PublishProfiles\FolderProfile.pubxml
-
-
\ No newline at end of file
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index d7958e35..98aba723 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -40,6 +40,7 @@
+
@@ -609,6 +610,14 @@
IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchAlwaysOnTop_Toggled" />
+
+
+
+
+
+
+
+
@@ -2013,7 +2029,7 @@
FontFamily="Microsoft YaHei UI" FontWeight="Bold"
Toggled="ToggleSwitchNotifyPreviousPage_Toggled" />
-
-
+ Toggled="ToggleSwitchUseNewStyleUI_Toggled" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4418,7 +4481,7 @@
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="0,3,0,0">
-
@@ -4449,7 +4512,7 @@
Orientation="Horizontal" Spacing="4"
VerticalAlignment="Center"
HorizontalAlignment="Center">
-
@@ -4480,7 +4543,7 @@
Orientation="Horizontal" Spacing="4"
VerticalAlignment="Center"
HorizontalAlignment="Center">
-
@@ -4509,7 +4572,7 @@
Orientation="Horizontal" Spacing="4"
VerticalAlignment="Center"
HorizontalAlignment="Center">
-
@@ -9335,7 +9398,7 @@
-
@@ -9359,7 +9422,7 @@
-
@@ -9384,7 +9447,7 @@
Opacity="{Binding ElementName=TwoFingerGestureSimpleStackPanel, Path=Opacity}"
Orientation="Horizontal" Spacing="4" VerticalAlignment="Center"
HorizontalAlignment="Center">
-
@@ -9409,7 +9472,7 @@
Opacity="{Binding ElementName=TwoFingerGestureSimpleStackPanel, Path=Opacity}"
Orientation="Horizontal" Spacing="4" VerticalAlignment="Center"
HorizontalAlignment="Center">
-
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 02e19d44..d82f1d64 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -12,6 +12,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
@@ -38,6 +39,12 @@ namespace Ink_Canvas
{
public partial class MainWindow : Window
{
+ [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();
+
// 每一页一个Canvas对象
private List whiteboardPages = new List();
private int currentPageIndex;
@@ -55,6 +62,7 @@ namespace Ink_Canvas
// 设置面板相关状态
private bool userChangedNoFocusModeInSettings;
+ private bool isTemporarilyDisablingNoFocusMode = false;
@@ -550,6 +558,12 @@ namespace Ink_Canvas
ToggleSwitchAlwaysOnTop.IsOn = Settings.Advanced.IsAlwaysOnTop;
ApplyAlwaysOnTop();
+ // 初始化UIA置顶开关
+ ToggleSwitchUIAccessTopMost.IsOn = Settings.Advanced.EnableUIAccessTopMost;
+ UpdateUIAccessTopMostVisibility();
+
+ App.IsUIAccessTopMostEnabled = Settings.Advanced.EnableUIAccessTopMost;
+
// 初始化剪贴板监控
InitializeClipboardMonitoring();
@@ -787,7 +801,7 @@ namespace Ink_Canvas
}
}
- // 辅助方法:使用多线路组下载更新
+ // 使用多线路组下载更新
private async Task DownloadUpdateWithFallback(string version, AutoUpdateHelper.UpdateLineGroup primaryGroup, UpdateChannel channel)
{
try
@@ -832,12 +846,14 @@ namespace Ink_Canvas
// 声明下载状态变量,用于整个方法
bool isDownloadSuccessful = false;
+ bool hasValidLineGroup = lineGroup != null;
+
if (AvailableLatestVersion != null)
{
// 检测到新版本,停止重试定时器
timerCheckAutoUpdateRetry.Stop();
updateCheckRetryCount = 0;
-
+
// 检测到新版本
LogHelper.WriteLogToFile($"AutoUpdate | New version available: {AvailableLatestVersion}");
@@ -1001,17 +1017,25 @@ namespace Ink_Canvas
break;
}
}
+ else if (hasValidLineGroup)
+ {
+ LogHelper.WriteLogToFile("AutoUpdate | Current version is already the latest, no retry needed");
+
+ // 停止重试定时器
+ timerCheckAutoUpdateRetry.Stop();
+ updateCheckRetryCount = 0;
+ }
else
{
// 检查更新失败,启动重试定时器
LogHelper.WriteLogToFile("AutoUpdate | Update check failed, starting retry timer");
-
+
// 重置重试计数
updateCheckRetryCount = 0;
-
+
// 启动重试定时器,10分钟后重新检查
timerCheckAutoUpdateRetry.Start();
-
+
// 清理更新文件夹
AutoUpdateHelper.DeleteUpdatesFolder();
}
@@ -1194,14 +1218,14 @@ namespace Ink_Canvas
RefreshDeviceInfo();
}
- // 新增:个性化设置
+ // 个性化设置
private void NavTheme_Click(object sender, RoutedEventArgs e)
{
// 切换到个性化设置页面
ShowSettingsSection("theme");
}
- // 新增:快捷键设置
+ // 快捷键设置
private void NavShortcuts_Click(object sender, RoutedEventArgs e)
{
OpenHotkeySettingsWindow();
@@ -1298,7 +1322,7 @@ namespace Ink_Canvas
}
}
- // 新增:折叠侧边栏
+ // 折叠侧边栏
private void CollapseNavSidebar_Click(object sender, RoutedEventArgs e)
{
// 折叠/展开侧边栏
@@ -1315,7 +1339,7 @@ namespace Ink_Canvas
}
}
- // 新增:显示侧边栏
+ // 显示侧边栏
private void ShowNavSidebar_Click(object sender, RoutedEventArgs e)
{
// 确保侧边栏展开
@@ -1323,7 +1347,7 @@ namespace Ink_Canvas
columnDefinitions[0].Width = new GridLength(50);
}
- // 辅助方法:显示指定的设置部分
+ // 显示指定的设置部分
private async void ShowSettingsSection(string sectionTag)
{
// 显示设置面板
@@ -1816,7 +1840,11 @@ namespace Ink_Canvas
{
var hwnd = new WindowInteropHelper(this).Handle;
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
- if (Settings.Advanced.IsNoFocusMode)
+
+ bool shouldBeNoFocus = isTemporarilyDisablingNoFocusMode ?
+ false : Settings.Advanced.IsNoFocusMode;
+
+ if (shouldBeNoFocus)
{
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
}
@@ -1998,6 +2026,12 @@ namespace Ink_Canvas
var toggle = sender as ToggleSwitch;
Settings.Advanced.IsNoFocusMode = toggle != null && toggle.IsOn;
SaveSettingsToFile();
+
+ if (isTemporarilyDisablingNoFocusMode)
+ {
+ isTemporarilyDisablingNoFocusMode = false;
+ }
+
ApplyNoFocusMode();
// 如果启用了窗口置顶,需要重新应用置顶设置以处理无焦点模式的变化
@@ -2020,6 +2054,21 @@ namespace Ink_Canvas
Settings.Advanced.IsAlwaysOnTop = toggle != null && toggle.IsOn;
SaveSettingsToFile();
ApplyAlwaysOnTop();
+ UpdateUIAccessTopMostVisibility();
+ }
+
+ private void ToggleSwitchUIAccessTopMost_Toggled(object sender, RoutedEventArgs e)
+ {
+ if (!isLoaded) return;
+ var toggle = sender as ToggleSwitch;
+ bool newValue = toggle != null && toggle.IsOn;
+
+ Settings.Advanced.EnableUIAccessTopMost = newValue;
+ SaveSettingsToFile();
+ ApplyUIAccessTopMost();
+
+ App.IsUIAccessTopMostEnabled = newValue;
+
}
private void Window_Activated(object sender, EventArgs e)
@@ -2449,6 +2498,8 @@ namespace Ink_Canvas
SideControlMinimumAutomationSlider,
RandWindowOnceCloseLatencySlider,
RandWindowOnceMaxStudentsSlider,
+ TimerVolumeSlider,
+ ProgressiveReminderVolumeSlider,
BoardInkWidthSlider,
BoardInkAlphaSlider,
BoardHighlighterWidthSlider,
@@ -2911,5 +2962,82 @@ namespace Ink_Canvas
}
#endregion
+
+ #region UIA置顶功能
+
+ ///
+ /// 更新UIA置顶开关的可见性
+ ///
+ private void UpdateUIAccessTopMostVisibility()
+ {
+ try
+ {
+ var visibility = Settings.Advanced.IsAlwaysOnTop ? Visibility.Visible : Visibility.Collapsed;
+
+ if (UIAccessTopMostPanel != null)
+ {
+ UIAccessTopMostPanel.Visibility = visibility;
+ }
+
+ if (UIAccessTopMostDescription != null)
+ {
+ UIAccessTopMostDescription.Visibility = visibility;
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"更新UIA置顶开关可见性时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ ///
+ /// 应用UIA置顶功能
+ ///
+ private void ApplyUIAccessTopMost()
+ {
+ try
+ {
+ if (Settings.Advanced.EnableUIAccessTopMost && Settings.Advanced.IsAlwaysOnTop)
+ {
+ // 检查是否以管理员权限运行
+ var identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+
+ if (principal.IsInRole(WindowsBuiltInRole.Administrator))
+ {
+ try
+ {
+ // 调用UIAccess DLL
+ if (Environment.Is64BitProcess)
+ {
+ PrepareUIAccessX64();
+ }
+ else
+ {
+ PrepareUIAccessX86();
+ }
+ }
+ 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);
+ }
+ }
+
+ #endregion
}
}
diff --git a/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs b/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
index 88c967be..b8032e3a 100644
--- a/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
+++ b/Ink Canvas/MainWindow_cs/MW_AutoTheme.cs
@@ -74,7 +74,7 @@ namespace Ink_Canvas
// 强制刷新UI
window.InvalidateVisual();
-
+
// 通知其他窗口刷新主题
RefreshOtherWindowsTheme();
}
@@ -118,7 +118,7 @@ namespace Ink_Canvas
// 强制刷新UI
window.InvalidateVisual();
-
+
// 通知其他窗口刷新主题
RefreshOtherWindowsTheme();
}
@@ -184,18 +184,18 @@ namespace Ink_Canvas
// 根据主题设置高光颜色
Color highlightBackgroundColor;
Color highlightBarColor;
- bool isDarkTheme = Settings.Appearance.Theme == 1 ||
+ bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
-
+
if (isDarkTheme)
{
- highlightBackgroundColor = Color.FromArgb(21, 102, 204, 255);
- highlightBarColor = Color.FromRgb(102, 204, 255);
+ highlightBackgroundColor = Color.FromArgb(21, 102, 204, 255);
+ highlightBarColor = Color.FromRgb(102, 204, 255);
}
else
{
- highlightBackgroundColor = Color.FromArgb(21, 59, 130, 246);
- highlightBarColor = Color.FromRgb(37, 99, 235);
+ highlightBackgroundColor = Color.FromArgb(21, 59, 130, 246);
+ highlightBarColor = Color.FromRgb(37, 99, 235);
}
// 设置高光背景颜色
@@ -224,16 +224,16 @@ namespace Ink_Canvas
{
// 根据主题选择高光颜色
Color selectedColor;
- bool isDarkTheme = Settings.Appearance.Theme == 1 ||
+ bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
-
+
if (isDarkTheme)
{
- selectedColor = Color.FromRgb(102, 204, 255);
+ selectedColor = Color.FromRgb(102, 204, 255);
}
else
{
- selectedColor = Color.FromRgb(30, 58, 138);
+ selectedColor = Color.FromRgb(30, 58, 138);
}
// 根据当前模式设置按钮颜色
@@ -376,7 +376,7 @@ namespace Ink_Canvas
{
// 强制刷新墨迹选中栏的视觉状态
BorderStrokeSelectionControl.InvalidateVisual();
-
+
// 刷新墨迹选中栏内的所有图标
var viewbox = BorderStrokeSelectionControl.Child as Viewbox;
if (viewbox?.Child is ui.SimpleStackPanel stackPanel)
@@ -434,7 +434,7 @@ namespace Ink_Canvas
{
// 强制刷新图片选中栏的视觉状态
BorderImageSelectionControl.InvalidateVisual();
-
+
// 刷新图片选中栏内的所有图标
var viewbox = BorderImageSelectionControl.Child as Viewbox;
if (viewbox?.Child is ui.SimpleStackPanel stackPanel)
diff --git a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
index adc46b19..a1b2319b 100644
--- a/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
+++ b/Ink Canvas/MainWindow_cs/MW_BoardControls.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -120,15 +121,25 @@ namespace Ink_Canvas
_currentCommitType = CommitReason.ClearingCanvas;
if (isErasedByCode) _currentCommitType = CommitReason.CodeInput;
-
-
- // 只清除笔画,不清除图片元素
- // 图片元素的清除由调用方决定
inkCanvas.Strokes.Clear();
+ // 执行内存清理
+ PerformLightweightMemoryCleanup();
+
_currentCommitType = CommitReason.UserInput;
}
+ ///
+ /// 执行内存清理
+ ///
+ private void PerformLightweightMemoryCleanup()
+ {
+ Task.Run(() =>
+ {
+ GC.Collect();
+ });
+ }
+
// 恢复每页白板图片信息
private void RestoreStrokes(bool isBackupMain = false)
{
@@ -418,7 +429,7 @@ namespace Ink_Canvas
// 获取主题颜色资源
var iconForegroundBrush = Application.Current.FindResource("IconForeground") as SolidColorBrush;
-
+
// 设置下一页按钮颜色
if (iconForegroundBrush != null)
{
diff --git a/Ink Canvas/MainWindow_cs/MW_Colors.cs b/Ink Canvas/MainWindow_cs/MW_Colors.cs
index 00eee032..2088c57d 100644
--- a/Ink Canvas/MainWindow_cs/MW_Colors.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Colors.cs
@@ -18,7 +18,10 @@ namespace Ink_Canvas
private void ColorSwitchCheck()
{
- HideSubPanels("color");
+ if (penType != 1)
+ {
+ HideSubPanels("color");
+ }
if (GridTransparencyFakeBackground.Background == Brushes.Transparent)
{
if (currentMode == 1)
diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
index d83f45e8..2d535209 100644
--- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
+++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs
@@ -383,7 +383,7 @@ namespace Ink_Canvas
var slideAnimation = new DoubleAnimation
{
From = 0, // 滑动距离
- To = BorderSettings.RenderTransform.Value.OffsetX - 440,
+ To = BorderSettings.RenderTransform.Value.OffsetX - 490,
Duration = TimeSpan.FromSeconds(0.6)
};
slideAnimation.EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut };
@@ -396,18 +396,13 @@ namespace Ink_Canvas
{
BorderSettings.Visibility = Visibility.Collapsed;
isOpeningOrHidingSettingsPane = false;
- // 在设置面板完全关闭后,根据情况恢复无焦点模式状态
- if (!userChangedNoFocusModeInSettings && wasNoFocusModeBeforeSettings)
+ if (isTemporarilyDisablingNoFocusMode)
{
- // 如果用户没有在设置中修改无焦点模式,则恢复之前的状态
- Settings.Advanced.IsNoFocusMode = true;
- ToggleSwitchNoFocusMode.IsOn = true; // 同步更新设置面板中的开关状态
+ isTemporarilyDisablingNoFocusMode = false;
ApplyNoFocusMode();
}
- // 如果用户在设置中修改了无焦点模式,则保持用户的修改
};
- BorderSettings.Visibility = Visibility.Visible;
BorderSettings.RenderTransform = new TranslateTransform();
isOpeningOrHidingSettingsPane = true;
@@ -477,9 +472,9 @@ namespace Ink_Canvas
// 根据主题选择高光颜色
Color highlightColor;
- bool isDarkTheme = Settings.Appearance.Theme == 1 ||
+ bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
-
+
if (isDarkTheme)
{
highlightColor = Color.FromRgb(102, 204, 255); // #66ccff for dark theme
@@ -929,7 +924,7 @@ namespace Ink_Canvas
#region 主要的工具按鈕事件
///
- /// 浮動工具欄的"套索選"按鈕事件,重定向到舊UI的BtnSelect_Click方法
+ /// 浮動工具欄的"套索選"按鈕事件,重定向到舊UI的BtnSelect_Click方法
///
/// sender
/// MouseButtonEventArgs
@@ -2708,11 +2703,9 @@ namespace Ink_Canvas
// 如果当前在设置面板中,需要先恢复无焦点模式状态
if (BorderSettings.Visibility == Visibility.Visible)
{
- // 如果用户没有在设置中修改无焦点模式,则恢复之前的状态
- if (!userChangedNoFocusModeInSettings && wasNoFocusModeBeforeSettings)
+ if (isTemporarilyDisablingNoFocusMode)
{
- Settings.Advanced.IsNoFocusMode = true;
- ToggleSwitchNoFocusMode.IsOn = true;
+ isTemporarilyDisablingNoFocusMode = false;
ApplyNoFocusMode();
}
SaveSettingsToFile();
@@ -2727,11 +2720,9 @@ namespace Ink_Canvas
{
if (BorderSettings.Visibility == Visibility.Visible)
{
- // 如果用户没有在设置中修改无焦点模式,则恢复之前的状态
- if (!userChangedNoFocusModeInSettings && wasNoFocusModeBeforeSettings)
+ if (isTemporarilyDisablingNoFocusMode)
{
- Settings.Advanced.IsNoFocusMode = true;
- ToggleSwitchNoFocusMode.IsOn = true;
+ isTemporarilyDisablingNoFocusMode = false;
ApplyNoFocusMode();
}
SaveSettingsToFile();
@@ -2777,12 +2768,12 @@ namespace Ink_Canvas
}
else
{
- // 临时禁用无焦点模式以避免下拉选项被遮挡
+ BorderSettings.Visibility = Visibility.Visible;
wasNoFocusModeBeforeSettings = Settings.Advanced.IsNoFocusMode;
userChangedNoFocusModeInSettings = false; // 重置用户修改标志
if (wasNoFocusModeBeforeSettings)
{
- Settings.Advanced.IsNoFocusMode = false;
+ isTemporarilyDisablingNoFocusMode = true;
ApplyNoFocusMode();
}
@@ -2795,7 +2786,7 @@ namespace Ink_Canvas
// 滑动动画
var slideAnimation = new DoubleAnimation
{
- From = BorderSettings.RenderTransform.Value.OffsetX - 440, // 滑动距离
+ From = BorderSettings.RenderTransform.Value.OffsetX - 490, // 滑动距离
To = 0,
Duration = TimeSpan.FromSeconds(0.6)
};
@@ -2807,7 +2798,6 @@ namespace Ink_Canvas
sb.Completed += (s, _) => { isOpeningOrHidingSettingsPane = false; };
- BorderSettings.Visibility = Visibility.Visible;
BorderSettings.RenderTransform = new TranslateTransform();
isOpeningOrHidingSettingsPane = true;
@@ -3717,18 +3707,18 @@ namespace Ink_Canvas
// 根据主题设置高光颜色
Color highlightBackgroundColor;
Color highlightBarColor;
- bool isDarkTheme = Settings.Appearance.Theme == 1 ||
+ bool isDarkTheme = Settings.Appearance.Theme == 1 ||
(Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
-
+
if (isDarkTheme)
{
- highlightBackgroundColor = Color.FromArgb(21, 102, 204, 255);
- highlightBarColor = Color.FromRgb(102, 204, 255);
+ highlightBackgroundColor = Color.FromArgb(21, 102, 204, 255);
+ highlightBarColor = Color.FromRgb(102, 204, 255);
}
else
{
- highlightBackgroundColor = Color.FromArgb(21, 59, 130, 246);
- highlightBarColor = Color.FromRgb(37, 99, 235);
+ highlightBackgroundColor = Color.FromArgb(21, 59, 130, 246);
+ highlightBarColor = Color.FromRgb(37, 99, 235);
}
// 设置高光背景颜色
diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs
index 52124a35..d271eb10 100644
--- a/Ink Canvas/MainWindow_cs/MW_PPT.cs
+++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs
@@ -11,6 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Threading;
using Application = System.Windows.Application;
@@ -77,7 +78,7 @@ namespace Ink_Canvas
#region PPT State Management
private bool wasFloatingBarFoldedWhenEnterSlideShow;
- private bool isEnteredSlideShowEndEvent; //防止重复调用本函数导致墨迹保存失效
+ private bool isEnteredSlideShowEndEvent;
private bool isPresentationHaveBlackSpace;
// 长按翻页相关字段
@@ -104,10 +105,11 @@ namespace Ink_Canvas
#region PPT Managers
private PPTManager _pptManager;
private MultiPPTInkManager _multiPPTInkManager;
+ private PPTInkManager _singlePPTInkManager;
private PPTUIManager _pptUIManager;
///
- /// 获取PPT管理器实例(供UI管理器使用)
+ /// 获取PPT管理器实例
///
public PPTManager PPTManager => _pptManager;
#endregion
@@ -133,11 +135,19 @@ namespace Ink_Canvas
_pptManager.PresentationClose += OnPPTPresentationClose;
_pptManager.SlideShowStateChanged += OnPPTSlideShowStateChanged;
- // 初始化多PPT墨迹管理器
- _multiPPTInkManager = new MultiPPTInkManager();
- _multiPPTInkManager.IsAutoSaveEnabled = Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint;
- _multiPPTInkManager.AutoSaveLocation = Settings.Automation.AutoSavedStrokesLocation;
- _multiPPTInkManager.PPTManager = _pptManager;
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager = new PPTInkManager();
+ _singlePPTInkManager.IsAutoSaveEnabled = Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint;
+ _singlePPTInkManager.AutoSaveLocation = Settings.Automation.AutoSavedStrokesLocation;
+ }
+ else
+ {
+ _multiPPTInkManager = new MultiPPTInkManager();
+ _multiPPTInkManager.IsAutoSaveEnabled = Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint;
+ _multiPPTInkManager.AutoSaveLocation = Settings.Automation.AutoSavedStrokesLocation;
+ _multiPPTInkManager.PPTManager = _pptManager;
+ }
// 初始化UI管理器
_pptUIManager = new PPTUIManager(this);
@@ -421,10 +431,12 @@ namespace Ink_Canvas
{
_pptManager?.Dispose();
_multiPPTInkManager?.Dispose();
+ _singlePPTInkManager?.Dispose();
_longPressTimer?.Stop();
_longPressTimer = null;
_pptManager = null;
_multiPPTInkManager = null;
+ _singlePPTInkManager = null;
_pptUIManager = null;
// 清理PowerPoint进程守护
@@ -509,8 +521,14 @@ namespace Ink_Canvas
else
{
LogHelper.WriteLogToFile("PPT连接已断开", LogHelper.LogType.Event);
- // 清理墨迹管理器
- _multiPPTInkManager?.ClearAllStrokes();
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.ClearAllStrokes();
+ }
+ else
+ {
+ _multiPPTInkManager?.ClearAllStrokes();
+ }
}
});
}
@@ -535,8 +553,14 @@ namespace Ink_Canvas
TimeMachineHistories[0] = null;
}
- // 初始化多PPT墨迹管理器
- _multiPPTInkManager?.InitializePresentation(pres);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.InitializePresentation(pres);
+ }
+ else
+ {
+ _multiPPTInkManager?.InitializePresentation(pres);
+ }
// 处理跳转到首页或上次播放页的逻辑
HandlePresentationOpenNavigation(pres);
@@ -570,11 +594,15 @@ namespace Ink_Canvas
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
- // 保存所有墨迹
- _multiPPTInkManager?.SaveAllStrokesToFile(pres);
-
- // 移除演示文稿管理器
- _multiPPTInkManager?.RemovePresentation(pres);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveAllStrokesToFile(pres);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveAllStrokesToFile(pres);
+ _multiPPTInkManager?.RemovePresentation(pres);
+ }
_pptUIManager?.UpdateConnectionStatus(false);
});
@@ -615,7 +643,7 @@ namespace Ink_Canvas
}
}
- private async void OnPPTSlideShowBegin(SlideShowWindow wn)
+ private void OnPPTSlideShowBegin(SlideShowWindow wn)
{
try
{
@@ -629,22 +657,26 @@ namespace Ink_Canvas
}
else
{
- // 如果关闭了PPT自动收纳功能,但用户当前在收纳模式下,进入PPT时取消收纳以提供更好的使用体验
if (isFloatingBarFolded)
{
- await UnFoldFloatingBar(new object());
+ UnFoldFloatingBar(new object());
}
}
isStopInkReplay = true;
- await Application.Current.Dispatcher.InvokeAsync(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
- // 获取当前活跃的演示文稿并切换到对应的墨迹管理器
var activePresentation = _pptManager?.GetCurrentActivePresentation();
if (activePresentation != null)
{
- _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ }
+ else
+ {
+ _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ }
}
// 处理跳转到首页或上次播放位置
@@ -720,6 +752,22 @@ namespace Ink_Canvas
{
UpdateCurrentToolMode("pen");
SetFloatingBarHighlightPosition("pen");
+ if (Settings.Appearance.IsShowQuickColorPalette && QuickColorPalettePanel != null && QuickColorPaletteSingleRowPanel != null)
+ {
+ // 根据显示模式选择显示哪个面板
+ if (Settings.Appearance.QuickColorPaletteDisplayMode == 0)
+ {
+ // 单行显示模式
+ QuickColorPalettePanel.Visibility = Visibility.Collapsed;
+ QuickColorPaletteSingleRowPanel.Visibility = Visibility.Visible;
+ }
+ else
+ {
+ // 双行显示模式
+ QuickColorPalettePanel.Visibility = Visibility.Visible;
+ QuickColorPaletteSingleRowPanel.Visibility = Visibility.Collapsed;
+ }
+ }
}
}
catch (Exception ex)
@@ -737,14 +785,7 @@ namespace Ink_Canvas
if (!isFloatingBarFolded)
{
- new Thread(() =>
- {
- Thread.Sleep(100);
- Application.Current.Dispatcher.Invoke(() =>
- {
- ViewboxFloatingBarMarginAnimation(60);
- });
- }).Start();
+ ViewboxFloatingBarMarginAnimation(60);
}
}
catch (Exception ex)
@@ -759,11 +800,16 @@ namespace Ink_Canvas
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
- // 获取当前活跃的演示文稿并确保切换到正确的墨迹管理器
var activePresentation = _pptManager?.GetCurrentActivePresentation();
if (activePresentation != null)
{
- _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ }
+ else
+ {
+ _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ }
}
var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
@@ -784,7 +830,6 @@ namespace Ink_Canvas
{
try
{
- // 处理浮动栏状态:根据"退出PPT放映后自动恢复浮动栏状态"设置决定是否恢复
if (Settings.Automation.IsAutoFoldAfterPPTSlideShow)
{
if (wasFloatingBarFoldedWhenEnterSlideShow)
@@ -807,7 +852,6 @@ namespace Ink_Canvas
}
else
{
- // 如果两个功能都关闭,确保浮动栏展开
if (isFloatingBarFolded)
{
await UnFoldFloatingBar(new object());
@@ -818,8 +862,14 @@ namespace Ink_Canvas
if (isEnteredSlideShowEndEvent) return;
isEnteredSlideShowEndEvent = true;
- // 保存所有墨迹
- _multiPPTInkManager?.SaveAllStrokesToFile(pres);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveAllStrokesToFile(pres);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveAllStrokesToFile(pres);
+ }
await Application.Current.Dispatcher.InvokeAsync(() =>
{
@@ -879,6 +929,9 @@ namespace Ink_Canvas
if (GridTransparencyFakeBackground.Background != Brushes.Transparent)
BtnHideInkCanvas_Click(BtnHideInkCanvas, null);
SetCurrentToolMode(InkCanvasEditingMode.None);
+
+ UpdateCurrentToolMode("cursor");
+ SetFloatingBarHighlightPosition("cursor");
}
catch (Exception ex)
{
@@ -889,8 +942,6 @@ namespace Ink_Canvas
await Task.Delay(100);
await Application.Current.Dispatcher.InvokeAsync(() =>
{
- // 强制重新计算浮动栏位置,确保在退出PPT模式后正确复位
- // 先调用桌面模式的复位方法,然后调用通用的位置计算方法
PureViewboxFloatingBarMarginAnimationInDesktopMode();
ViewboxFloatingBarMarginAnimation(100, true);
});
@@ -941,7 +992,24 @@ namespace Ink_Canvas
_lastPlaybackPage = page;
new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () =>
{
- _shouldNavigateToLastPage = true;
+ try
+ {
+ if (_pptManager?.PPTApplication != null)
+ {
+ if (_pptManager.PPTApplication.SlideShowWindows.Count >= 1)
+ {
+ pres.SlideShowWindow.View.GotoSlide(page);
+ }
+ else
+ {
+ pres.Windows[1].View.GotoSlide(page);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"跳转到第{page}页失败: {ex}", LogHelper.LogType.Error);
+ }
}).ShowDialog();
}
}
@@ -1060,7 +1128,16 @@ namespace Ink_Canvas
{
try
{
- var strokes = _multiPPTInkManager?.LoadSlideStrokes(slideIndex);
+ StrokeCollection strokes = null;
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ strokes = _singlePPTInkManager?.LoadSlideStrokes(slideIndex);
+ }
+ else
+ {
+ strokes = _multiPPTInkManager?.LoadSlideStrokes(slideIndex);
+ }
+
if (strokes != null)
{
inkCanvas.Strokes.Clear();
@@ -1080,15 +1157,18 @@ namespace Ink_Canvas
{
try
{
- // 获取当前活跃的演示文稿
- var activePresentation = _pptManager?.GetCurrentActivePresentation();
- if (activePresentation != null)
+ if (Settings.PowerPointSettings.IsSupportWPS)
{
- // 切换到对应的墨迹管理器
- _multiPPTInkManager?.SwitchToPresentation(activePresentation);
-
- // 重置锁定状态
- _multiPPTInkManager?.ResetCurrentPresentationLockState();
+ _singlePPTInkManager?.ResetLockState();
+ }
+ else
+ {
+ var activePresentation = _pptManager?.GetCurrentActivePresentation();
+ if (activePresentation != null)
+ {
+ _multiPPTInkManager?.SwitchToPresentation(activePresentation);
+ _multiPPTInkManager?.ResetCurrentPresentationLockState();
+ }
}
}
catch (Exception ex)
@@ -1202,26 +1282,41 @@ namespace Ink_Canvas
// 如果有当前墨迹且不是第一次切换,先保存到当前页面
if (inkCanvas.Strokes.Count > 0 && currentSlideIndex > 0 && currentSlideIndex != newSlideIndex)
{
- // 检查是否可以写入墨迹
- bool canWrite = _multiPPTInkManager?.CanWriteInk(currentSlideIndex) == true;
-
- if (canWrite)
+ bool canWrite = false;
+ if (Settings.PowerPointSettings.IsSupportWPS)
{
- // 正常保存
- _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlideIndex, inkCanvas.Strokes);
+ canWrite = _singlePPTInkManager?.CanWriteInk(currentSlideIndex) == true;
}
else
{
- // 墨迹被锁定,跳过保存以避免墨迹错页
+ canWrite = _multiPPTInkManager?.CanWriteInk(currentSlideIndex) == true;
+ }
+
+ if (canWrite)
+ {
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlideIndex, inkCanvas.Strokes);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlideIndex, inkCanvas.Strokes);
+ }
}
}
else if (inkCanvas.Strokes.Count > 0 && currentSlideIndex <= 0)
{
- // 无法获取当前页面索引时,不保存墨迹,直接清空
}
- // 切换到新页面并加载墨迹
- var newStrokes = _multiPPTInkManager?.SwitchToSlide(newSlideIndex, null);
+ StrokeCollection newStrokes = null;
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ newStrokes = _singlePPTInkManager?.SwitchToSlide(newSlideIndex, null);
+ }
+ else
+ {
+ newStrokes = _multiPPTInkManager?.SwitchToSlide(newSlideIndex, null);
+ }
if (newStrokes != null)
{
inkCanvas.Strokes.Clear();
@@ -1362,7 +1457,14 @@ namespace Ink_Canvas
var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (currentSlide > 0)
{
- _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
}
// 保存截图(如果启用)
@@ -1402,7 +1504,14 @@ namespace Ink_Canvas
var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0;
if (currentSlide > 0)
{
- _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
}
// 保存截图(如果启用)
@@ -1432,7 +1541,7 @@ namespace Ink_Canvas
});
}
- private async void PPTNavigationBtn_MouseDown(object sender, MouseButtonEventArgs e)
+ private void PPTNavigationBtn_MouseDown(object sender, MouseButtonEventArgs e)
{
lastBorderMouseDownObject = sender;
if (!Settings.PowerPointSettings.EnablePPTButtonPageClickable) return;
@@ -1454,7 +1563,7 @@ namespace Ink_Canvas
}
}
- private async void PPTNavigationBtn_MouseLeave(object sender, MouseEventArgs e)
+ private void PPTNavigationBtn_MouseLeave(object sender, MouseEventArgs e)
{
lastBorderMouseDownObject = null;
if (sender == PPTLSPageButton)
@@ -1475,7 +1584,7 @@ namespace Ink_Canvas
}
}
- private async void PPTNavigationBtn_MouseUp(object sender, MouseButtonEventArgs e)
+ private void PPTNavigationBtn_MouseUp(object sender, MouseButtonEventArgs e)
{
if (lastBorderMouseDownObject != sender) return;
@@ -1524,7 +1633,6 @@ namespace Ink_Canvas
// 控制居中
if (!isFloatingBarFolded)
{
- await Task.Delay(100);
ViewboxFloatingBarMarginAnimation(60);
}
}
@@ -1562,7 +1670,14 @@ namespace Ink_Canvas
{
Application.Current.Dispatcher.Invoke(() =>
{
- _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ if (Settings.PowerPointSettings.IsSupportWPS)
+ {
+ _singlePPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
+ else
+ {
+ _multiPPTInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes);
+ }
timeMachine.ClearStrokeHistory();
});
}
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index aafe6dd1..e5e06c8f 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -433,8 +433,8 @@ namespace Ink_Canvas
public void UpdateCustomIconsInComboBox()
{
- // 保留前11个内置图标选项
- while (ComboBoxFloatingBarImg.Items.Count > 11)
+ // 保留前12个内置图标选项
+ while (ComboBoxFloatingBarImg.Items.Count > 12)
{
ComboBoxFloatingBarImg.Items.RemoveAt(ComboBoxFloatingBarImg.Items.Count - 1);
}
@@ -2611,17 +2611,17 @@ namespace Ink_Canvas
Settings.RandSettings.UseLegacyTimerUI = ToggleSwitchUseLegacyTimerUI.IsOn;
if (ToggleSwitchUseLegacyTimerUI.IsOn)
{
- ToggleSwitchUseSeewoStyleUI.IsOn = false;
- Settings.RandSettings.UseSeewoStyleUI = false;
+ ToggleSwitchUseNewStyleUI.IsOn = false;
+ Settings.RandSettings.UseNewStyleUI = false;
}
SaveSettingsToFile();
}
- private void ToggleSwitchUseSeewoStyleUI_Toggled(object sender, RoutedEventArgs e)
+ private void ToggleSwitchUseNewStyleUI_Toggled(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
- Settings.RandSettings.UseSeewoStyleUI = ToggleSwitchUseSeewoStyleUI.IsOn;
- if (ToggleSwitchUseSeewoStyleUI.IsOn)
+ Settings.RandSettings.UseNewStyleUI = ToggleSwitchUseNewStyleUI.IsOn;
+ if (ToggleSwitchUseNewStyleUI.IsOn)
{
ToggleSwitchUseLegacyTimerUI.IsOn = false;
Settings.RandSettings.UseLegacyTimerUI = false;
@@ -2629,6 +2629,34 @@ namespace Ink_Canvas
SaveSettingsToFile();
}
+ private void ToggleSwitchEnableOvertimeCountUp_Toggled(object sender, RoutedEventArgs e)
+ {
+ if (!isLoaded) return;
+ Settings.RandSettings.EnableOvertimeCountUp = ToggleSwitchEnableOvertimeCountUp.IsOn;
+
+ if (!ToggleSwitchEnableOvertimeCountUp.IsOn)
+ {
+ ToggleSwitchEnableOvertimeRedText.IsOn = false;
+ Settings.RandSettings.EnableOvertimeRedText = false;
+ }
+
+ SaveSettingsToFile();
+ }
+
+ private void ToggleSwitchEnableOvertimeRedText_Toggled(object sender, RoutedEventArgs e)
+ {
+ if (!isLoaded) return;
+
+ if (ToggleSwitchEnableOvertimeRedText.IsOn && !ToggleSwitchEnableOvertimeCountUp.IsOn)
+ {
+ ToggleSwitchEnableOvertimeRedText.IsOn = false;
+ return;
+ }
+
+ Settings.RandSettings.EnableOvertimeRedText = ToggleSwitchEnableOvertimeRedText.IsOn;
+ SaveSettingsToFile();
+ }
+
private void TimerVolumeSlider_ValueChanged(object sender, RoutedEventArgs e)
{
if (!isLoaded) return;
@@ -2636,6 +2664,42 @@ namespace Ink_Canvas
SaveSettingsToFile();
}
+ private void ToggleSwitchEnableProgressiveReminder_Toggled(object sender, RoutedEventArgs e)
+ {
+ if (!isLoaded) return;
+ Settings.RandSettings.EnableProgressiveReminder = ToggleSwitchEnableProgressiveReminder.IsOn;
+ SaveSettingsToFile();
+ }
+
+ private void ProgressiveReminderVolumeSlider_ValueChanged(object sender, RoutedEventArgs e)
+ {
+ if (!isLoaded) return;
+ Settings.RandSettings.ProgressiveReminderVolume = ProgressiveReminderVolumeSlider.Value;
+ SaveSettingsToFile();
+ }
+
+ private void ButtonSelectCustomProgressiveReminderSound_Click(object sender, RoutedEventArgs e)
+ {
+ Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog
+ {
+ Title = "选择渐进提醒音频文件",
+ Filter = "音频文件 (*.wav)|*.wav|所有文件 (*.*)|*.*",
+ DefaultExt = "wav"
+ };
+
+ if (openFileDialog.ShowDialog() == true)
+ {
+ Settings.RandSettings.ProgressiveReminderSoundPath = openFileDialog.FileName;
+ SaveSettingsToFile();
+ }
+ }
+
+ private void ButtonResetProgressiveReminderSound_Click(object sender, RoutedEventArgs e)
+ {
+ Settings.RandSettings.ProgressiveReminderSoundPath = "";
+ SaveSettingsToFile();
+ }
+
private void ButtonSelectCustomTimerSound_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog
@@ -3181,6 +3245,149 @@ namespace Ink_Canvas
}
}
+ private async void ManualUpdateButton_Click(object sender, RoutedEventArgs e)
+ {
+ ManualUpdateButton.IsEnabled = false;
+ ManualUpdateButton.Content = "正在检查更新...";
+
+ try
+ {
+ LogHelper.WriteLogToFile("ManualUpdate | Manual update button clicked");
+
+ // 使用当前选择的更新通道检查更新
+ var (remoteVersion, lineGroup, apiReleaseNotes) = await AutoUpdateHelper.CheckForUpdates(Settings.Startup.UpdateChannel, true, false);
+
+ if (remoteVersion != null)
+ {
+ LogHelper.WriteLogToFile($"ManualUpdate | Found new version: {remoteVersion}");
+
+ // 获取当前版本
+ string currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+
+ // 创建并显示更新窗口
+ HasNewUpdateWindow updateWindow = new HasNewUpdateWindow(currentVersion, remoteVersion, "", apiReleaseNotes);
+ updateWindow.Owner = Application.Current.MainWindow;
+ bool? dialogResult = updateWindow.ShowDialog();
+
+ // 如果窗口被关闭但没有点击按钮,则不执行任何操作
+ if (dialogResult != true)
+ {
+ LogHelper.WriteLogToFile("ManualUpdate | Update dialog closed without selection");
+ return;
+ }
+
+ // 根据用户选择处理更新
+ switch (updateWindow.Result)
+ {
+ case HasNewUpdateWindow.UpdateResult.UpdateNow:
+ // 立即更新:显示下载进度,下载完成后立即安装
+ LogHelper.WriteLogToFile("ManualUpdate | User chose to update now");
+
+ // 显示下载进度提示
+ MessageBox.Show("开始下载更新,请稍候...", "正在更新", MessageBoxButton.OK, MessageBoxImage.Information);
+
+ // 下载更新文件,使用多线路组下载功能
+ bool isDownloadSuccessful = await DownloadUpdateWithFallback(remoteVersion, lineGroup, Settings.Startup.UpdateChannel);
+
+ if (isDownloadSuccessful)
+ {
+ // 下载成功,提示用户准备安装
+ MessageBoxResult result = MessageBox.Show("更新已下载完成,点击确定后将关闭软件并安装新版本!", "安装更新", MessageBoxButton.OKCancel, MessageBoxImage.Information);
+
+ // 只有当用户点击确定按钮后才关闭软件
+ if (result == MessageBoxResult.OK)
+ {
+ // 设置为用户主动退出,避免被看门狗判定为崩溃
+ App.IsAppExitByUser = true;
+
+ // 准备批处理脚本
+ AutoUpdateHelper.InstallNewVersionApp(remoteVersion, true);
+
+ // 关闭软件,让安装程序接管
+ Application.Current.Shutdown();
+ }
+ else
+ {
+ LogHelper.WriteLogToFile("ManualUpdate | User cancelled update installation");
+ }
+ }
+ else
+ {
+ // 下载失败
+ MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ break;
+
+ case HasNewUpdateWindow.UpdateResult.UpdateLater:
+ // 稍后更新:静默下载,在软件关闭时自动安装
+ LogHelper.WriteLogToFile("ManualUpdate | User chose to update later");
+
+ // 不管设置如何,都进行下载,使用多线路组下载功能
+ isDownloadSuccessful = await DownloadUpdateWithFallback(remoteVersion, lineGroup, Settings.Startup.UpdateChannel);
+
+ if (isDownloadSuccessful)
+ {
+ LogHelper.WriteLogToFile("ManualUpdate | Update downloaded successfully, will install when application closes");
+
+ // 设置标志,在应用程序关闭时安装
+ Settings.Startup.IsAutoUpdate = true;
+ Settings.Startup.IsAutoUpdateWithSilence = true;
+
+ // 启动检查定时器
+ timerCheckAutoUpdateWithSilence.Start();
+
+ // 通知用户
+ MessageBox.Show("更新已下载完成,将在软件关闭时自动安装。", "更新已准备就绪", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ else
+ {
+ LogHelper.WriteLogToFile("ManualUpdate | Update download failed", LogHelper.LogType.Error);
+ MessageBox.Show("更新下载失败,请检查网络连接后重试。", "下载失败", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ break;
+
+ case HasNewUpdateWindow.UpdateResult.SkipVersion:
+ // 跳过该版本:记录到设置中
+ LogHelper.WriteLogToFile($"ManualUpdate | User chose to skip version {remoteVersion}");
+
+ // 记录要跳过的版本号
+ Settings.Startup.SkippedVersion = remoteVersion;
+
+ // 保存设置到文件
+ SaveSettingsToFile();
+
+ // 通知用户
+ MessageBox.Show($"已设置跳过版本 {remoteVersion},在下次发布新版本之前不会再提示更新。",
+ "已跳过此版本",
+ MessageBoxButton.OK,
+ MessageBoxImage.Information);
+ break;
+ }
+ }
+ else
+ {
+ // 没有更新
+ LogHelper.WriteLogToFile("ManualUpdate | No updates available");
+ MessageBox.Show("当前已是最新版本!", "无可用更新", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"Error in ManualUpdateButton_Click: {ex.Message}", LogHelper.LogType.Error);
+ MessageBox.Show(
+ $"手动更新过程中发生错误: {ex.Message}",
+ "更新错误",
+ MessageBoxButton.OK,
+ MessageBoxImage.Error);
+ }
+ finally
+ {
+ // 恢复按钮状态
+ ManualUpdateButton.IsEnabled = true;
+ ManualUpdateButton.Content = "手动更新";
+ }
+ }
+
private async void FixVersionButton_Click(object sender, RoutedEventArgs e)
{
// 显示确认对话框
diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
index c0841c61..c9699bb8 100644
--- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
@@ -883,9 +883,22 @@ namespace Ink_Canvas
// 计时器设置
ToggleSwitchUseLegacyTimerUI.IsOn = Settings.RandSettings.UseLegacyTimerUI;
- ToggleSwitchUseSeewoStyleUI.IsOn = Settings.RandSettings.UseSeewoStyleUI;
+ ToggleSwitchUseNewStyleUI.IsOn = Settings.RandSettings.UseNewStyleUI;
+ ToggleSwitchEnableOvertimeCountUp.IsOn = Settings.RandSettings.EnableOvertimeCountUp;
+
+ bool canEnableRedText = Settings.RandSettings.EnableOvertimeCountUp && Settings.RandSettings.EnableOvertimeRedText;
+ ToggleSwitchEnableOvertimeRedText.IsOn = canEnableRedText;
+ if (!canEnableRedText)
+ {
+ Settings.RandSettings.EnableOvertimeRedText = false;
+ }
+
TimerVolumeSlider.Value = Settings.RandSettings.TimerVolume;
+ // 渐进提醒设置
+ ToggleSwitchEnableProgressiveReminder.IsOn = Settings.RandSettings.EnableProgressiveReminder;
+ ProgressiveReminderVolumeSlider.Value = Settings.RandSettings.ProgressiveReminderVolume;
+
// 加载自定义点名背景
UpdatePickNameBackgroundsInComboBox();
@@ -905,8 +918,21 @@ namespace Ink_Canvas
ToggleSwitchExternalCaller.IsOn = Settings.RandSettings.DirectCallCiRand;
ComboBoxExternalCallerType.SelectedIndex = Settings.RandSettings.ExternalCallerType;
ToggleSwitchUseLegacyTimerUI.IsOn = Settings.RandSettings.UseLegacyTimerUI;
- ToggleSwitchUseSeewoStyleUI.IsOn = Settings.RandSettings.UseSeewoStyleUI;
+ ToggleSwitchUseNewStyleUI.IsOn = Settings.RandSettings.UseNewStyleUI;
+ ToggleSwitchEnableOvertimeCountUp.IsOn = Settings.RandSettings.EnableOvertimeCountUp;
+
+ bool canEnableRedText = Settings.RandSettings.EnableOvertimeCountUp && Settings.RandSettings.EnableOvertimeRedText;
+ ToggleSwitchEnableOvertimeRedText.IsOn = canEnableRedText;
+ if (!canEnableRedText)
+ {
+ Settings.RandSettings.EnableOvertimeRedText = false;
+ }
+
TimerVolumeSlider.Value = Settings.RandSettings.TimerVolume;
+
+ // 渐进提醒设置
+ ToggleSwitchEnableProgressiveReminder.IsOn = Settings.RandSettings.EnableProgressiveReminder;
+ ProgressiveReminderVolumeSlider.Value = Settings.RandSettings.ProgressiveReminderVolume;
}
// ModeSettings
diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs
index 8b83a223..afb21c29 100644
--- a/Ink Canvas/MainWindow_cs/MW_Timer.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs
@@ -61,10 +61,10 @@ namespace Ink_Canvas
private Timer timerCheckAutoFold = new Timer();
private string AvailableLatestVersion;
private Timer timerCheckAutoUpdateWithSilence = new Timer();
- private Timer timerCheckAutoUpdateRetry = new Timer();
+ private Timer timerCheckAutoUpdateRetry = new Timer();
private bool isHidingSubPanelsWhenInking; // 避免书写时触发二次关闭二级菜单导致动画不连续
- private int updateCheckRetryCount = 0;
- private const int MAX_UPDATE_CHECK_RETRIES = 6;
+ private int updateCheckRetryCount = 0;
+ private const int MAX_UPDATE_CHECK_RETRIES = 6;
private Timer timerDisplayTime = new Timer();
private Timer timerDisplayDate = new Timer();
private Timer timerNtpSync = new Timer();
@@ -120,7 +120,7 @@ namespace Ink_Canvas
timerCheckAutoUpdateWithSilence.Elapsed += timerCheckAutoUpdateWithSilence_Elapsed;
timerCheckAutoUpdateWithSilence.Interval = 1000 * 60 * 10;
timerCheckAutoUpdateRetry.Elapsed += timerCheckAutoUpdateRetry_Elapsed;
- timerCheckAutoUpdateRetry.Interval = 1000 * 60 * 10;
+ timerCheckAutoUpdateRetry.Interval = 1000 * 60 * 10;
WaterMarkTime.DataContext = nowTimeVM;
WaterMarkDate.DataContext = nowTimeVM;
timerDisplayTime.Elapsed += TimerDisplayTime_Elapsed;
@@ -863,7 +863,7 @@ namespace Ink_Canvas
// 执行更新检查
LogHelper.WriteLogToFile("AutoUpdate | Retrying update check after failure");
-
+
// 清除之前的更新状态
AvailableLatestVersion = null;
AvailableLatestLineGroup = null;
@@ -878,7 +878,7 @@ namespace Ink_Canvas
// 检查更新成功,重置重试计数
updateCheckRetryCount = 0;
LogHelper.WriteLogToFile($"AutoUpdate | Retry successful, found new version: {AvailableLatestVersion}");
-
+
// 停止重试定时器,因为已经找到了更新
return;
}
@@ -886,7 +886,7 @@ namespace Ink_Canvas
{
// 检查更新仍然失败,继续重试
LogHelper.WriteLogToFile($"AutoUpdate | Retry {updateCheckRetryCount} failed, will retry in 10 minutes");
-
+
// 重新启动定时器,10分钟后再次尝试
timerCheckAutoUpdateRetry.Start();
}
@@ -894,7 +894,7 @@ namespace Ink_Canvas
catch (Exception ex)
{
LogHelper.WriteLogToFile($"AutoUpdate | Error in retry check: {ex.Message}", LogHelper.LogType.Error);
-
+
// 出错时也重新启动定时器,稍后再检查
if (updateCheckRetryCount <= MAX_UPDATE_CHECK_RETRIES)
{
@@ -910,10 +910,10 @@ namespace Ink_Canvas
{
// 停止重试定时器
timerCheckAutoUpdateRetry.Stop();
-
+
// 重置重试计数
updateCheckRetryCount = 0;
-
+
LogHelper.WriteLogToFile("AutoUpdate | Update check retry state reset");
}
catch (Exception ex)
diff --git a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
index 422ceeec..2cfe181a 100644
--- a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
+++ b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs
@@ -259,7 +259,6 @@ namespace Ink_Canvas
// 只保留普通橡皮逻辑
TouchDownPointsList[e.TouchDevice.Id] = InkCanvasEditingMode.None;
- inkCanvas.EraserShape = new EllipseStylusShape(50, 50);
if (inkCanvas.EditingMode != InkCanvasEditingMode.EraseByPoint
&& inkCanvas.EditingMode != InkCanvasEditingMode.EraseByStroke)
{
@@ -682,8 +681,6 @@ namespace Ink_Canvas
if (Settings.Advanced.IsSpecialScreen)
boundWidth *= Settings.Advanced.TouchMultiplier;
-
- inkCanvas.EraserShape = new EllipseStylusShape(boundWidth, boundWidth);
inkCanvas.EditingMode = InkCanvasEditingMode.EraseByPoint;
isPalmEraserActive = true;
diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs
index 567d00bd..5d61c823 100644
--- a/Ink Canvas/Properties/AssemblyInfo.cs
+++ b/Ink Canvas/Properties/AssemblyInfo.cs
@@ -49,5 +49,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.13.0")]
-[assembly: AssemblyFileVersion("1.7.13.0")]
+[assembly: AssemblyVersion("1.7.14.0")]
+[assembly: AssemblyFileVersion("1.7.14.0")]
diff --git a/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml b/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml
deleted file mode 100644
index c0035a49..00000000
--- a/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- Release
- Any CPU
- dist\
- FileSystem
- <_TargetId>Folder
-
-
\ No newline at end of file
diff --git a/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml.user b/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml.user
deleted file mode 100644
index e9ea52ed..00000000
--- a/Ink Canvas/Properties/PublishProfiles/FolderProfile.pubxml.user
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
diff --git a/Ink Canvas/Properties/Resources.Designer.cs b/Ink Canvas/Properties/Resources.Designer.cs
index 725984ff..9322cc19 100644
--- a/Ink Canvas/Properties/Resources.Designer.cs
+++ b/Ink Canvas/Properties/Resources.Designer.cs
@@ -74,5 +74,14 @@ namespace Ink_Canvas.Properties {
return ResourceManager.GetStream("TimerDownNotice", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream.
+ ///
+ internal static UnmanagedMemoryStream ProgressiveAudio {
+ get {
+ return ResourceManager.GetStream("ProgressiveAudio", resourceCulture);
+ }
+ }
}
}
diff --git a/Ink Canvas/Properties/Resources.resx b/Ink Canvas/Properties/Resources.resx
index 09ce9aea..610b7b0c 100644
--- a/Ink Canvas/Properties/Resources.resx
+++ b/Ink Canvas/Properties/Resources.resx
@@ -121,4 +121,7 @@
..\Resources\TimerDownNotice.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ ..\Resources\ProgressiveAudio.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
\ No newline at end of file
diff --git a/Ink Canvas/Resources/ProgressiveAudio.wav b/Ink Canvas/Resources/ProgressiveAudio.wav
new file mode 100644
index 00000000..0e010cc8
Binary files /dev/null and b/Ink Canvas/Resources/ProgressiveAudio.wav differ
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index 67838de3..ba633b85 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -591,6 +591,9 @@ namespace Ink_Canvas
[JsonProperty("isAlwaysOnTop")]
public bool IsAlwaysOnTop { get; set; } = true;
+
+ [JsonProperty("enableUIAccessTopMost")]
+ public bool EnableUIAccessTopMost { get; set; } = false;
}
public class InkToShape
@@ -631,12 +634,22 @@ namespace Ink_Canvas
public List CustomPickNameBackgrounds { get; set; } = new List();
[JsonProperty("useLegacyTimerUI")]
public bool UseLegacyTimerUI { get; set; } = false;
- [JsonProperty("useSeewoStyleUI")]
- public bool UseSeewoStyleUI { get; set; } = false;
+ [JsonProperty("useNewStyleUI")]
+ public bool UseNewStyleUI { get; set; } = true;
[JsonProperty("timerVolume")]
public double TimerVolume { get; set; } = 1.0;
[JsonProperty("customTimerSoundPath")]
public string CustomTimerSoundPath { get; set; } = "";
+ [JsonProperty("enableOvertimeCountUp")]
+ public bool EnableOvertimeCountUp { get; set; } = false;
+ [JsonProperty("enableOvertimeRedText")]
+ public bool EnableOvertimeRedText { get; set; } = false;
+ [JsonProperty("enableProgressiveReminder")]
+ public bool EnableProgressiveReminder { get; set; } = false;
+ [JsonProperty("progressiveReminderVolume")]
+ public double ProgressiveReminderVolume { get; set; } = 1.0;
+ [JsonProperty("progressiveReminderSoundPath")]
+ public string ProgressiveReminderSoundPath { get; set; } = "";
}
public class CustomPickNameBackground
diff --git a/Ink Canvas/Resources/Styles/Dark.xaml b/Ink Canvas/Resources/Styles/Dark.xaml
index 68ddd12f..fb1ee834 100644
--- a/Ink Canvas/Resources/Styles/Dark.xaml
+++ b/Ink Canvas/Resources/Styles/Dark.xaml
@@ -113,15 +113,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -139,4 +139,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Ink Canvas/Resources/Styles/Light.xaml b/Ink Canvas/Resources/Styles/Light.xaml
index c3ae721d..056d21bf 100644
--- a/Ink Canvas/Resources/Styles/Light.xaml
+++ b/Ink Canvas/Resources/Styles/Light.xaml
@@ -113,15 +113,15 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -139,4 +139,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Ink Canvas/Resources/new-icons/gesture.png b/Ink Canvas/Resources/new-icons/gesture.png
index 8e7c6401..1d5709ce 100644
Binary files a/Ink Canvas/Resources/new-icons/gesture.png and b/Ink Canvas/Resources/new-icons/gesture.png differ
diff --git a/Ink Canvas/Resources/new-icons/gesture_white.png b/Ink Canvas/Resources/new-icons/gesture_white.png
new file mode 100644
index 00000000..bb207bd3
Binary files /dev/null and b/Ink Canvas/Resources/new-icons/gesture_white.png differ
diff --git a/Ink Canvas/Resources/new-icons/hand-move_white.png b/Ink Canvas/Resources/new-icons/hand-move_white.png
new file mode 100644
index 00000000..ceb3a2da
Binary files /dev/null and b/Ink Canvas/Resources/new-icons/hand-move_white.png differ
diff --git a/Ink Canvas/Resources/new-icons/multi-touch_white.png b/Ink Canvas/Resources/new-icons/multi-touch_white.png
new file mode 100644
index 00000000..32adceac
Binary files /dev/null and b/Ink Canvas/Resources/new-icons/multi-touch_white.png differ
diff --git a/Ink Canvas/Resources/new-icons/rotate_white.png b/Ink Canvas/Resources/new-icons/rotate_white.png
new file mode 100644
index 00000000..de5ee62b
Binary files /dev/null and b/Ink Canvas/Resources/new-icons/rotate_white.png differ
diff --git a/Ink Canvas/Resources/new-icons/zoom_white.png b/Ink Canvas/Resources/new-icons/zoom_white.png
new file mode 100644
index 00000000..fc2183ce
Binary files /dev/null and b/Ink Canvas/Resources/new-icons/zoom_white.png differ
diff --git a/Ink Canvas/UIAccessDLL_x64.dll b/Ink Canvas/UIAccessDLL_x64.dll
new file mode 100644
index 00000000..9edc84b8
Binary files /dev/null and b/Ink Canvas/UIAccessDLL_x64.dll differ
diff --git a/Ink Canvas/UIAccessDLL_x86.dll b/Ink Canvas/UIAccessDLL_x86.dll
new file mode 100644
index 00000000..48fe7e3e
Binary files /dev/null and b/Ink Canvas/UIAccessDLL_x86.dll differ
diff --git a/Ink Canvas/Windows/Controls/WinUI3CloseButton.cs b/Ink Canvas/Windows/Controls/WinUI3CloseButton.cs
new file mode 100644
index 00000000..144dca9c
--- /dev/null
+++ b/Ink Canvas/Windows/Controls/WinUI3CloseButton.cs
@@ -0,0 +1,26 @@
+using System.Windows;
+using System.Windows.Controls.Primitives;
+
+namespace Ink_Canvas.Windows.Controls
+{
+ public class WinUI3CloseButton : ButtonBase
+ {
+ static WinUI3CloseButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(WinUI3CloseButton), new FrameworkPropertyMetadata(typeof(WinUI3CloseButton)));
+ }
+
+ public bool IsActive
+ {
+ get => (bool)GetValue(IsActiveProperty);
+ set => SetValue(IsActiveProperty, value);
+ }
+
+ public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
+ nameof(IsActive),
+ typeof(bool),
+ typeof(WinUI3CloseButton),
+ new PropertyMetadata(true)
+ );
+ }
+}
diff --git a/Ink Canvas/Windows/Controls/WinUI3CloseButton.xaml b/Ink Canvas/Windows/Controls/WinUI3CloseButton.xaml
new file mode 100644
index 00000000..202b1e06
--- /dev/null
+++ b/Ink Canvas/Windows/Controls/WinUI3CloseButton.xaml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs b/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs
index 545ea002..a4d1cbae 100644
--- a/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs
+++ b/Ink Canvas/Windows/CountdownTimerWindow.xaml.cs
@@ -22,16 +22,16 @@ namespace Ink_Canvas
timer.Elapsed += Timer_Elapsed;
timer.Interval = 50;
InitializeUI();
-
+
// 应用主题
ApplyTheme();
}
public static Window CreateTimerWindow()
{
- if (MainWindow.Settings.RandSettings?.UseSeewoStyleUI == true)
+ if (MainWindow.Settings.RandSettings?.UseNewStyleUI == true)
{
- return new SeewoStyleTimerWindow();
+ return new NewStyleTimerWindow();
}
else
{
@@ -49,46 +49,72 @@ namespace Ink_Canvas
TimeSpan timeSpan = DateTime.Now - startTime;
TimeSpan totalTimeSpan = new TimeSpan(hour, minute, second);
- TimeSpan leftTimeSpan = totalTimeSpan - timeSpan;
- if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
double spentTimePercent = timeSpan.TotalMilliseconds / (totalSeconds * 1000.0);
+
Application.Current.Dispatcher.Invoke(() =>
{
- ProcessBarTime.CurrentValue = 1 - spentTimePercent;
- TextBlockHour.Text = leftTimeSpan.Hours.ToString("00");
- TextBlockMinute.Text = leftTimeSpan.Minutes.ToString("00");
- TextBlockSecond.Text = leftTimeSpan.Seconds.ToString("00");
- TbCurrentTime.Text = leftTimeSpan.ToString(@"hh\:mm\:ss");
- if (spentTimePercent >= 1)
+ if (!isOvertimeMode)
{
- ProcessBarTime.CurrentValue = 0;
- TextBlockHour.Text = "00";
- TextBlockMinute.Text = "00";
- TextBlockSecond.Text = "00";
- timer.Stop();
- isTimerRunning = false;
- SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Play;
- BtnStartCover.Visibility = Visibility.Visible;
- var textForeground = Application.Current.FindResource("TimerWindowTextForeground") as SolidColorBrush;
- if (textForeground != null)
+ TimeSpan leftTimeSpan = totalTimeSpan - timeSpan;
+ if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
+
+ ProcessBarTime.CurrentValue = 1 - spentTimePercent;
+ TextBlockHour.Text = leftTimeSpan.Hours.ToString("00");
+ TextBlockMinute.Text = leftTimeSpan.Minutes.ToString("00");
+ TextBlockSecond.Text = leftTimeSpan.Seconds.ToString("00");
+ TbCurrentTime.Text = leftTimeSpan.ToString(@"hh\:mm\:ss");
+
+ if (spentTimePercent >= 1 && MainWindow.Settings.RandSettings?.EnableOvertimeCountUp == true)
{
- TextBlockHour.Foreground = textForeground;
+ isOvertimeMode = true;
+ ProcessBarTime.CurrentValue = 0;
+ ProcessBarTime.Visibility = Visibility.Collapsed;
+ BorderStopTime.Visibility = Visibility.Collapsed;
+
+ // 播放提醒音
+ PlayTimerSound();
}
- else
+ else if (spentTimePercent >= 1)
{
- TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F"));
+ ProcessBarTime.CurrentValue = 0;
+ TextBlockHour.Text = "00";
+ TextBlockMinute.Text = "00";
+ TextBlockSecond.Text = "00";
+ timer.Stop();
+ isTimerRunning = false;
+ SymbolIconStart.Symbol = iNKORE.UI.WPF.Modern.Controls.Symbol.Play;
+ BtnStartCover.Visibility = Visibility.Visible;
+ var textForeground = Application.Current.FindResource("TimerWindowTextForeground") as SolidColorBrush;
+ if (textForeground != null)
+ {
+ TextBlockHour.Foreground = textForeground;
+ }
+ else
+ {
+ TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F"));
+ }
+ BorderStopTime.Visibility = Visibility.Collapsed;
+
+ // 播放提醒音
+ PlayTimerSound();
+ }
+ }
+ else
+ {
+ TimeSpan overtimeSpan = timeSpan - totalTimeSpan;
+ TextBlockHour.Text = overtimeSpan.Hours.ToString("00");
+ TextBlockMinute.Text = overtimeSpan.Minutes.ToString("00");
+ TextBlockSecond.Text = overtimeSpan.Seconds.ToString("00");
+ TbCurrentTime.Text = overtimeSpan.ToString(@"hh\:mm\:ss");
+
+ if (MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true)
+ {
+ TextBlockHour.Foreground = Brushes.Red;
+ TextBlockMinute.Foreground = Brushes.Red;
+ TextBlockSecond.Foreground = Brushes.Red;
}
- BorderStopTime.Visibility = Visibility.Collapsed;
}
});
- if (spentTimePercent >= 1)
- {
- Application.Current.Dispatcher.Invoke(() =>
- {
- //Play sound
- PlayTimerSound();
- });
- }
}
SoundPlayer player = new SoundPlayer();
@@ -105,15 +131,16 @@ namespace Ink_Canvas
bool isTimerRunning = false;
bool isPaused = false;
bool useLegacyUI = false;
+ bool isOvertimeMode = false;
Timer timer = new Timer();
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
if (isTimerRunning) return;
-
+
var textForeground = Application.Current.FindResource("TimerWindowTextForeground") as SolidColorBrush;
-
+
if (ProcessBarTime.Visibility == Visibility.Visible && isTimerRunning == false)
{
ProcessBarTime.Visibility = Visibility.Collapsed;
@@ -268,6 +295,9 @@ namespace Ink_Canvas
TextBlockHour.Foreground = textForeground3;
else
TextBlockHour.Foreground = new SolidColorBrush(StringToColor("#FF5B5D5F"));
+
+ isOvertimeMode = false;
+ ProcessBarTime.Visibility = Visibility.Visible;
}
else if (isTimerRunning && isPaused)
{
@@ -288,6 +318,9 @@ namespace Ink_Canvas
isPaused = false;
ProcessBarTime.CurrentValue = 0;
ProcessBarTime.IsPaused = false;
+
+ isOvertimeMode = false;
+ ProcessBarTime.Visibility = Visibility.Visible;
}
else
{
@@ -390,6 +423,8 @@ namespace Ink_Canvas
isPaused = false;
isTimerRunning = true;
+ isOvertimeMode = false;
+ ProcessBarTime.Visibility = Visibility.Visible;
timer.Start();
UpdateStopTime();
BorderStopTime.Visibility = Visibility.Visible;
@@ -439,7 +474,7 @@ namespace Ink_Canvas
{
// 重新应用主题
ApplyTheme();
-
+
// 强制刷新UI
InvalidateVisual();
}
diff --git a/Ink Canvas/Windows/CustomIconWindow.xaml.cs b/Ink Canvas/Windows/CustomIconWindow.xaml.cs
index fb89d189..5fff7008 100644
--- a/Ink Canvas/Windows/CustomIconWindow.xaml.cs
+++ b/Ink Canvas/Windows/CustomIconWindow.xaml.cs
@@ -37,8 +37,8 @@ namespace Ink_Canvas
}
// 如果当前选中的是被删除的图标,重置为默认图标
- if (MainWindow.Settings.Appearance.FloatingBarImg >= 8 &&
- MainWindow.Settings.Appearance.FloatingBarImg - 8 >= MainWindow.Settings.Appearance.CustomFloatingBarImgs.Count)
+ if (MainWindow.Settings.Appearance.FloatingBarImg >= 12 &&
+ MainWindow.Settings.Appearance.FloatingBarImg - 12 >= MainWindow.Settings.Appearance.CustomFloatingBarImgs.Count)
{
MainWindow.Settings.Appearance.FloatingBarImg = 0;
mainWindow.ComboBoxFloatingBarImg.SelectedIndex = 0;
diff --git a/Ink Canvas/Windows/DigitResources.xaml b/Ink Canvas/Windows/DigitResources.xaml
new file mode 100644
index 00000000..312d66c0
--- /dev/null
+++ b/Ink Canvas/Windows/DigitResources.xaml
@@ -0,0 +1,25 @@
+
+
+
+ M2.432,34.536 C2.432,30.6533333 2.83733333,27.2506667 3.648,24.328 C4.45866667,21.4053333 5.6,18.984 7.072,17.064 C8.544,15.144 10.304,13.704 12.352,12.744 C14.4,11.784 16.6826667,11.304 19.2,11.304 C24.576,11.304 28.7146667,13.192 31.616,16.968 C34.5173333,20.744 35.968,26.6 35.968,34.536 C35.968,38.4613333 35.5626667,41.8853333 34.752,44.808 C33.9413333,47.7306667 32.8,50.1626667 31.328,52.104 C29.856,54.0453333 28.0853333,55.496 26.016,56.456 C23.9466667,57.416 21.6533333,57.896 19.136,57.896 C13.7173333,57.896 9.57866667,55.9013333 6.72,51.912 C3.86133333,47.9226667 2.432,42.1306667 2.432,34.536 Z M27.776,34.536 C27.776,33.8533333 27.7653333,33.2026667 27.744,32.584 C27.7226667,31.9653333 27.6906667,31.3573333 27.648,30.76 L11.968,45.032 C13.2906667,48.9146667 15.7013333,50.856 19.2,50.856 C21.9306667,50.856 24.0426667,49.5546667 25.536,46.952 C27.0293333,44.3493333 27.776,40.2106667 27.776,34.536 Z M10.624,34.536 C10.624,35.176 10.6346667,35.784 10.656,36.36 C10.6773333,36.936 10.7093333,37.5226667 10.752,38.12 L26.368,23.848 C25.088,20.1786667 22.6773333,18.344 19.136,18.344 C16.4053333,18.344 14.304,19.656 12.832,22.28 C11.36,24.904 10.624,28.9893333 10.624,34.536 Z
+
+ M46.184 49.96 L55.976 49.96 L55.976 21.992 L47.528 28.456 L43.688 23.336 L58.28 12.2 L63.912 12.2 L63.912 49.96 L73.512 49.96 L73.512 57 L46.184 57 Z
+
+ M112.272,57 L83.728,57 L83.728,50.792 C84.24,50.3653333 84.88,49.8 85.648,49.096 C86.416,48.392 87.28,47.592 88.24,46.696 C89.2,45.8 90.2133333,44.8186667 91.28,43.752 C92.3466667,42.6853333 93.392,41.576 94.416,40.424 C95.8666667,38.8026667 97.136,37.3306667 98.224,36.008 C99.312,34.6853333 100.208,33.4373333 100.912,32.264 C101.616,31.0906667 102.138667,29.9386667 102.48,28.808 C102.821333,27.6773333 102.992,26.5146667 102.992,25.32 C102.992,24.4666667 102.8,23.6346667 102.416,22.824 C102.032,22.0133333 101.498667,21.2986667 100.816,20.68 C100.133333,20.0613333 99.312,19.5706667 98.352,19.208 C97.392,18.8453333 96.336,18.664 95.184,18.664 C93.648,18.664 92.2506667,18.8986667 90.992,19.368 C89.7333333,19.8373333 88.4213333,20.6266667 87.056,21.736 L83.728,15.848 C85.52,14.568 87.5146667,13.4906667 89.712,12.616 C91.9093333,11.7413333 94.4373333,11.304 97.296,11.304 C99.1306667,11.304 100.890667,11.56 102.576,12.072 C104.261333,12.584 105.754667,13.3413333 107.056,14.344 C108.357333,15.3466667 109.392,16.5946667 110.16,18.088 C110.928,19.5813333 111.312,21.3093333 111.312,23.272 C111.312,24.6373333 111.194667,25.9066667 110.96,27.08 C110.725333,28.2533333 110.362667,29.4266667 109.872,30.6 C109.381333,31.7733333 108.741333,32.9786667 107.952,34.216 C107.162667,35.4533333 106.213333,36.7973333 105.104,38.248 C104.250667,39.3573333 103.322667,40.4773333 102.32,41.608 C101.317333,42.7386667 100.325333,43.816 99.344,44.84 C98.3626667,45.864 97.392,46.824 96.432,47.72 C95.472,48.616 94.608,49.3626667 93.84,49.96 L112.272,49.96 L112.272,57 Z
+
+ M134.072,50.856 C135.48,50.856 136.770667,50.6746667 137.944,50.312 C139.117333,49.9493333 140.12,49.4373333 140.952,48.776 C141.784,48.1146667 142.434667,47.3466667 142.904,46.472 C143.373333,45.5973333 143.608,44.648 143.608,43.624 C143.608,42.344 143.341333,41.2666667 142.808,40.392 C142.274667,39.5173333 141.538667,38.824 140.6,38.312 C139.661333,37.8 138.530667,37.4373333 137.208,37.224 C135.885333,37.0106667 134.456,36.904 132.92,36.904 L129.336,36.904 L129.336,32.36 L140.088,19.24 L123.896,19.24 L123.896,12.2 L150.008,12.2 L150.008,18.024 L138.744,30.888 L140.216,30.888 C141.709333,30.888 143.16,31.144 144.568,31.656 C145.976,32.168 147.224,32.936 148.312,33.96 C149.4,34.984 150.274667,36.264 150.936,37.8 C151.597333,39.336 151.928,41.1066667 151.928,43.112 C151.928,45.544 151.426667,47.6773333 150.424,49.512 C149.421333,51.3466667 148.098667,52.8933333 146.456,54.152 C144.813333,55.4106667 142.925333,56.3493333 140.792,56.968 C138.658667,57.5866667 136.461333,57.896 134.2,57.896 C132.28,57.896 130.36,57.7253333 128.44,57.384 C126.52,57.0426667 124.664,56.5093333 122.872,55.784 L125.112,48.872 C125.496,49.0853333 126.018667,49.2986667 126.68,49.512 C127.341333,49.7253333 128.088,49.9386667 128.92,50.152 C129.752,50.3653333 130.605333,50.536 131.48,50.664 C132.354667,50.792 133.218667,50.856 134.072,50.856 Z
+
+ M194.528,44.52 L186.848,44.52 L186.848,57 L179.104,57 L179.104,44.52 L159.712,44.52 L159.712,38.824 L180,11.88 L186.848,11.88 L186.848,37.864 L194.528,37.864 L194.528,44.52 Z M179.104,23.656 L167.968,37.864 L179.104,37.864 L179.104,23.656 Z
+
+ M212.168,50.536 C213.576,50.536 214.877333,50.3653333 216.072,50.024 C217.266667,49.6826667 218.301333,49.192 219.176,48.552 C220.050667,47.912 220.733333,47.1226667 221.224,46.184 C221.714667,45.2453333 221.96,44.1786667 221.96,42.984 C221.96,40.6373333 221.032,38.792 219.176,37.448 C217.32,36.104 214.536,35.432 210.824,35.432 C210.354667,35.432 209.810667,35.4426667 209.192,35.464 C208.573333,35.4853333 207.986667,35.5173333 207.432,35.56 C206.792,35.6026667 206.130667,35.624 205.448,35.624 L205.448,12.2 L228.616,12.2 L228.616,19.88 L212.616,19.88 L212.616,28.648 C212.957333,28.648 213.298667,28.6266667 213.64,28.584 C213.938667,28.584 214.226667,28.5733333 214.504,28.552 C214.781333,28.5306667 215.026667,28.52 215.24,28.52 C217.373333,28.52 219.357333,28.8293333 221.192,29.448 C223.026667,30.0666667 224.626667,30.9946667 225.992,32.232 C227.357333,33.4693333 228.413333,34.9733333 229.16,36.744 C229.906667,38.5146667 230.28,40.4666667 230.28,42.6 C230.28,45.1173333 229.810667,47.3253333 228.872,49.224 C227.933333,51.1226667 226.653333,52.712 225.032,53.992 C223.410667,55.272 221.533333,56.2426667 219.4,56.904 C217.266667,57.5653333 214.984,57.896 212.552,57.896 C211.613333,57.896 210.610667,57.8 209.544,57.608 C208.477333,57.416 207.432,57.1813333 206.408,56.904 C205.384,56.6266667 204.434667,56.3173333 203.56,55.976 C202.685333,55.6346667 201.949333,55.3146667 201.352,55.016 L204.04,48.36 C204.765333,48.7866667 205.885333,49.256 207.4,49.768 C208.914667,50.28 210.504,50.536 212.168,50.536 Z
+
+ M271.344,43.112 C271.344,45.2026667 270.949333,47.144 270.16,48.936 C269.370667,50.728 268.293333,52.2853333 266.928,53.608 C265.562667,54.9306667 263.930667,55.976 262.032,56.744 C260.133333,57.512 258.096,57.896 255.92,57.896 C253.701333,57.896 251.621333,57.544 249.68,56.84 C247.738667,56.136 246.042667,55.08 244.592,53.672 C243.141333,52.264 241.989333,50.4933333 241.136,48.36 C240.282667,46.2266667 239.856,43.752 239.856,40.936 C239.856,36.712 240.592,32.872 242.064,29.416 C243.536,25.96 245.424,22.9626667 247.728,20.424 C250.032,17.8853333 252.613333,15.8373333 255.472,14.28 C258.330667,12.7226667 261.168,11.7306667 263.984,11.304 L265.84,17.896 C263.578667,18.3226667 261.466667,19.016 259.504,19.976 C257.541333,20.936 255.792,22.0986667 254.256,23.464 C252.72,24.8293333 251.429333,26.344 250.384,28.008 C249.338667,29.672 248.624,31.4 248.24,33.192 C249.093333,32.1253333 250.373333,31.2186667 252.08,30.472 C253.786667,29.7253333 255.664,29.352 257.712,29.352 C259.632,29.352 261.424,29.6613333 263.088,30.28 C264.752,30.8986667 266.192,31.7946667 267.408,32.968 C268.624,34.1413333 269.584,35.5813333 270.288,37.288 C270.992,38.9946667 271.344,40.936 271.344,43.112 Z M263.28,43.432 C263.28,41.128 262.586667,39.3786667 261.2,38.184 C259.813333,36.9893333 257.946667,36.392 255.6,36.392 C253.893333,36.392 252.357333,36.776 250.992,37.544 C249.626667,38.312 248.624,39.208 247.984,40.232 C247.898667,40.7013333 247.845333,41.224 247.824,41.8 C247.802667,42.376 247.792,42.8346667 247.792,43.176 C247.792,44.2 247.962667,45.1813333 248.304,46.12 C248.645333,47.0586667 249.146667,47.88 249.808,48.584 C250.469333,49.288 251.269333,49.8426667 252.208,50.248 C253.146667,50.6533333 254.234667,50.856 255.472,50.856 C256.453333,50.856 257.413333,50.6746667 258.352,50.312 C259.290667,49.9493333 260.122667,49.448 260.848,48.808 C261.573333,48.168 262.16,47.3893333 262.608,46.472 C263.056,45.5546667 263.28,44.5413333 263.28,43.432 Z
+
+ M282.328 57 L300.76 19.752 L279.896 19.752 L279.896 12.2 L309.08 12.2 L309.08 18.472 L290.456 57 Z
+
+ M319.04,46.056 C319.04,44.5626667 319.285333,43.1973333 319.776,41.96 C320.266667,40.7226667 320.906667,39.6133333 321.696,38.632 C322.485333,37.6506667 323.392,36.7653333 324.416,35.976 C325.44,35.1866667 326.485333,34.4933333 327.552,33.896 C325.418667,32.6586667 323.648,31.1973333 322.24,29.512 C320.832,27.8266667 320.128,25.704 320.128,23.144 C320.128,21.48 320.490667,19.9226667 321.216,18.472 C321.941333,17.0213333 322.954667,15.7626667 324.256,14.696 C325.557333,13.6293333 327.104,12.7973333 328.896,12.2 C330.688,11.6026667 332.629333,11.304 334.72,11.304 C336.64,11.304 338.442667,11.56 340.128,12.072 C341.813333,12.584 343.296,13.3093333 344.576,14.248 C345.856,15.1866667 346.858667,16.328 347.584,17.672 C348.309333,19.016 348.672,20.4986667 348.672,22.12 C348.672,24.6373333 348.021333,26.8346667 346.72,28.712 C345.418667,30.5893333 343.637333,32.2106667 341.376,33.576 C343.936,34.8986667 345.973333,36.456 347.488,38.248 C349.002667,40.04 349.76,42.344 349.76,45.16 C349.76,47.08 349.344,48.8186667 348.512,50.376 C347.68,51.9333333 346.56,53.2666667 345.152,54.376 C343.744,55.4853333 342.090667,56.3493333 340.192,56.968 C338.293333,57.5866667 336.277333,57.896 334.144,57.896 C332.010667,57.896 330.026667,57.608 328.192,57.032 C326.357333,56.456 324.757333,55.656 323.392,54.632 C322.026667,53.608 320.96,52.3706667 320.192,50.92 C319.424,49.4693333 319.04,47.848 319.04,46.056 Z M341.824,44.968 C341.824,43.9013333 341.546667,42.984 340.992,42.216 C340.437333,41.448 339.744,40.7653333 338.912,40.168 C338.08,39.5706667 337.162667,39.0266667 336.16,38.536 C335.157333,38.0453333 334.186667,37.544 333.248,37.032 C331.114667,37.9706667 329.472,39.176 328.32,40.648 C327.168,42.12 326.592,43.5173333 326.592,44.84 C326.592,46.76 327.274667,48.2426667 328.64,49.288 C330.005333,50.3333333 331.904,50.856 334.336,50.856 C336.512,50.856 338.304,50.3546667 339.712,49.352 C341.12,48.3493333 341.824,46.888 341.824,44.968 Z M328.064,23.528 C328.064,24.552 328.288,25.416 328.736,26.12 C329.184,26.824 329.76,27.4426667 330.464,27.976 C331.168,28.5093333 331.968,28.9893333 332.864,29.416 C333.76,29.8426667 334.656,30.2693333 335.552,30.696 C336.234667,30.3546667 336.906667,29.928 337.568,29.416 C338.229333,28.904 338.816,28.3386667 339.328,27.72 C339.84,27.1013333 340.256,26.4613333 340.576,25.8 C340.896,25.1386667 341.056,24.4666667 341.056,23.784 C341.056,22.888 340.864,22.1093333 340.48,21.448 C340.096,20.7866667 339.594667,20.2213333 338.976,19.752 C338.357333,19.2826667 337.664,18.9306667 336.896,18.696 C336.128,18.4613333 335.338667,18.344 334.528,18.344 C333.589333,18.344 332.725333,18.4933333 331.936,18.792 C331.146667,19.0906667 330.464,19.4746667 329.888,19.944 C329.312,20.4133333 328.864,20.968 328.544,21.608 C328.224,22.248 328.064,22.888 328.064,23.528 Z
+
+ M358.056,26.216 C358.056,24.04 358.429333,22.0346667 359.176,20.2 C359.922667,18.3653333 360.968,16.7973333 362.312,15.496 C363.656,14.1946667 365.277333,13.1706667 367.176,12.424 C369.074667,11.6773333 371.197333,11.304 373.544,11.304 C375.933333,11.304 378.109333,11.6666667 380.072,12.392 C382.034667,13.1173333 383.72,14.1946667 385.128,15.624 C386.536,17.0533333 387.624,18.824 388.392,20.936 C389.16,23.048 389.544,25.4693333 389.544,28.2 C389.544,33.1493333 388.904,37.416 387.624,41 C386.344,44.584 384.626667,47.5706667 382.472,49.96 C380.317333,52.3493333 377.810667,54.184 374.952,55.464 C372.093333,56.744 369.106667,57.5546667 365.992,57.896 L364.2,51.368 C366.546667,51.0693333 368.669333,50.4933333 370.568,49.64 C372.466667,48.7866667 374.12,47.7306667 375.528,46.472 C376.936,45.2133333 378.088,43.784 378.984,42.184 C379.88,40.584 380.456,38.8666667 380.712,37.032 C379.816,37.928 378.664,38.6426667 377.256,39.176 C375.848,39.7093333 374.162667,39.976 372.2,39.976 L372.072,39.976 C370.450667,39.976 368.808,39.6986667 367.144,39.144 C365.48,38.5893333 363.976,37.736 362.632,36.584 C361.288,35.432 360.189333,33.992 359.336,32.264 C358.482667,30.536 358.056,28.52 358.056,26.216 Z M366.12,25.768 C366.12,26.9626667 366.344,28.0186667 366.792,28.936 C367.24,29.8533333 367.826667,30.6213333 368.552,31.24 C369.277333,31.8586667 370.12,32.328 371.08,32.648 C372.04,32.968 373.032,33.128 374.056,33.128 C375.805333,33.128 377.32,32.8506667 378.6,32.296 C379.88,31.7413333 380.818667,31.0586667 381.416,30.248 C381.544,29.352 381.608,28.4986667 381.608,27.688 C381.608,26.4933333 381.437333,25.32 381.096,24.168 C380.754667,23.016 380.242667,21.992 379.56,21.096 C378.877333,20.2 378.024,19.4853333 377,18.952 C375.976,18.4186667 374.802667,18.152 373.48,18.152 C371.389333,18.152 369.64,18.824 368.232,20.168 C366.824,21.512 366.12,23.3786667 366.12,25.768 Z
+
+
diff --git a/Ink Canvas/Windows/FullscreenTimerWindow.xaml b/Ink Canvas/Windows/FullscreenTimerWindow.xaml
new file mode 100644
index 00000000..115730bf
--- /dev/null
+++ b/Ink Canvas/Windows/FullscreenTimerWindow.xaml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs
new file mode 100644
index 00000000..7221e64e
--- /dev/null
+++ b/Ink Canvas/Windows/FullscreenTimerWindow.xaml.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Timers;
+
+namespace Ink_Canvas
+{
+ ///
+ /// 全屏计时器窗口
+ ///
+ public partial class FullscreenTimerWindow : Window
+ {
+ private NewStyleTimerWindow parentWindow;
+ private System.Timers.Timer updateTimer;
+
+ public FullscreenTimerWindow(NewStyleTimerWindow parent)
+ {
+ InitializeComponent();
+ parentWindow = parent;
+
+ // 设置窗口位置和大小
+ this.Left = 0;
+ this.Top = 0;
+ this.Width = SystemParameters.PrimaryScreenWidth;
+ this.Height = SystemParameters.PrimaryScreenHeight;
+
+ // 启动更新定时器
+ updateTimer = new System.Timers.Timer(100);
+ updateTimer.Elapsed += UpdateTimer_Elapsed;
+ updateTimer.Start();
+
+ parentWindow.TimerCompleted += ParentWindow_TimerCompleted;
+ }
+
+ private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ if (parentWindow != null)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ if (ShouldCloseWindow())
+ {
+ this.Close();
+ return;
+ }
+
+ UpdateTimeDisplay();
+ });
+ }
+ }
+
+ private bool ShouldCloseWindow()
+ {
+ if (parentWindow == null) return true;
+
+ if (MainWindow.Settings.RandSettings?.EnableOvertimeCountUp == true)
+ {
+ if (parentWindow.IsTimerRunning)
+ {
+ return false;
+ }
+
+ var remainingTime = parentWindow.GetRemainingTime();
+ if (remainingTime.HasValue && remainingTime.Value.TotalSeconds < 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ return !parentWindow.IsTimerRunning;
+ }
+ }
+
+ private void UpdateTimeDisplay()
+ {
+ if (parentWindow == null) return;
+
+ // 获取剩余时间
+ var remainingTime = parentWindow.GetRemainingTime();
+ if (remainingTime.HasValue)
+ {
+ var timeSpan = remainingTime.Value;
+ bool isOvertimeMode = timeSpan.TotalSeconds < 0;
+ bool shouldShowRed = isOvertimeMode && MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true;
+
+ int hours, minutes, seconds;
+
+ if (isOvertimeMode)
+ {
+ var totalTimeSpan = parentWindow.GetTotalTimeSpan();
+ if (totalTimeSpan.HasValue)
+ {
+ var elapsedTime = parentWindow.GetElapsedTime();
+ if (elapsedTime.HasValue)
+ {
+ var overtimeSpan = elapsedTime.Value - totalTimeSpan.Value;
+ hours = (int)overtimeSpan.TotalHours;
+ minutes = overtimeSpan.Minutes;
+ seconds = overtimeSpan.Seconds;
+ }
+ else
+ {
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+ }
+ }
+ else
+ {
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+ }
+ }
+ else
+ {
+ hours = (int)timeSpan.TotalHours;
+ minutes = timeSpan.Minutes;
+ seconds = timeSpan.Seconds;
+ }
+
+ // 更新小时显示
+ SetDigitDisplay("FullHour1Display", hours / 10, shouldShowRed);
+ SetDigitDisplay("FullHour2Display", hours % 10, shouldShowRed);
+
+ // 更新分钟显示
+ SetDigitDisplay("FullMinute1Display", minutes / 10, shouldShowRed);
+ SetDigitDisplay("FullMinute2Display", minutes % 10, shouldShowRed);
+
+ // 更新秒显示
+ SetDigitDisplay("FullSecond1Display", seconds / 10, shouldShowRed);
+ SetDigitDisplay("FullSecond2Display", seconds % 10, shouldShowRed);
+
+ SetColonDisplay(shouldShowRed);
+ }
+ }
+
+ private void ParentWindow_TimerCompleted(object sender, EventArgs e)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Close();
+ });
+ }
+
+ private void SetDigitDisplay(string pathName, int digit, bool isRed = false)
+ {
+ var path = this.FindName(pathName) as Path;
+ if (path != null)
+ {
+ string resourceKey = $"Digit{digit}";
+ var geometry = this.FindResource(resourceKey) as Geometry;
+ if (geometry != null)
+ {
+ path.Data = geometry;
+ }
+
+ // 设置颜色
+ if (isRed)
+ {
+ path.Fill = Brushes.Red;
+ }
+ else
+ {
+ path.Fill = Brushes.White;
+ }
+ }
+ }
+
+ ///
+ /// 设置全屏窗口冒号显示颜色
+ ///
+ /// 是否显示为红色
+ private void SetColonDisplay(bool isRed = false)
+ {
+ var colon1 = this.FindName("FullColon1Display") as TextBlock;
+ var colon2 = this.FindName("FullColon2Display") as TextBlock;
+
+ if (colon1 != null)
+ {
+ if (isRed)
+ {
+ colon1.Foreground = Brushes.Red;
+ }
+ else
+ {
+ colon1.Foreground = Brushes.White;
+ }
+ }
+
+ if (colon2 != null)
+ {
+ if (isRed)
+ {
+ colon2.Foreground = Brushes.Red;
+ }
+ else
+ {
+ colon2.Foreground = Brushes.White;
+ }
+ }
+ }
+
+ private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ // 点击屏幕退出全屏
+ ExitFullscreen();
+ }
+
+ private void Window_KeyDown(object sender, KeyEventArgs e)
+ {
+ // 按ESC键退出全屏
+ if (e.Key == Key.Escape)
+ {
+ ExitFullscreen();
+ }
+ }
+
+ private void ExitFullscreen()
+ {
+ // 恢复主窗口
+ if (parentWindow != null)
+ {
+ // 清除全屏模式标志
+ parentWindow.SetFullscreenMode(false);
+ parentWindow.Show();
+ parentWindow.Activate();
+ parentWindow.WindowState = WindowState.Normal;
+ }
+ this.Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ if (parentWindow != null)
+ {
+ parentWindow.TimerCompleted -= ParentWindow_TimerCompleted;
+ }
+
+ // 清理资源
+ if (updateTimer != null)
+ {
+ updateTimer.Stop();
+ updateTimer.Dispose();
+ }
+ base.OnClosed(e);
+ }
+ }
+}
diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml b/Ink Canvas/Windows/MinimizedTimerWindow.xaml
new file mode 100644
index 00000000..08b6f91c
--- /dev/null
+++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
new file mode 100644
index 00000000..b8004d2b
--- /dev/null
+++ b/Ink Canvas/Windows/MinimizedTimerWindow.xaml.cs
@@ -0,0 +1,392 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using System.Windows.Media.Animation;
+
+namespace Ink_Canvas
+{
+ ///
+ /// 最小化计时器窗口
+ ///
+ public partial class MinimizedTimerWindow : Window
+ {
+ private NewStyleTimerWindow parentWindow;
+ private System.Timers.Timer updateTimer;
+ private bool isMouseOver = false;
+ private bool isDragging = false;
+ private Point lastMousePosition;
+
+ public MinimizedTimerWindow(NewStyleTimerWindow parent)
+ {
+ InitializeComponent();
+ parentWindow = parent;
+
+ // 设置窗口位置
+ this.Left = parent.Left;
+ this.Top = parent.Top;
+
+ // 启动更新定时器
+ updateTimer = new System.Timers.Timer(100); // 100ms更新一次
+ updateTimer.Elapsed += UpdateTimer_Elapsed;
+ updateTimer.Start();
+
+ parentWindow.TimerCompleted += ParentWindow_TimerCompleted;
+
+ // 应用主题
+ ApplyTheme();
+ }
+
+ private void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ if (parentWindow != null)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ if (ShouldCloseWindow())
+ {
+ this.Close();
+ return;
+ }
+
+ UpdateTimeDisplay();
+ });
+ }
+ }
+
+ private bool ShouldCloseWindow()
+ {
+ if (parentWindow == null) return true;
+
+ if (MainWindow.Settings.RandSettings?.EnableOvertimeCountUp == true)
+ {
+ if (parentWindow.IsTimerRunning)
+ {
+ return false;
+ }
+
+ var remainingTime = parentWindow.GetRemainingTime();
+ if (remainingTime.HasValue && remainingTime.Value.TotalSeconds < 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ else
+ {
+ return !parentWindow.IsTimerRunning;
+ }
+ }
+
+ private void UpdateTimeDisplay()
+ {
+ if (parentWindow == null) return;
+
+ // 获取剩余时间
+ var remainingTime = parentWindow.GetRemainingTime();
+ if (remainingTime.HasValue)
+ {
+ var timeSpan = remainingTime.Value;
+ bool isOvertimeMode = timeSpan.TotalSeconds < 0;
+ bool shouldShowRed = isOvertimeMode && MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true;
+
+ int hours, minutes, seconds;
+
+ if (isOvertimeMode)
+ {
+ var totalTimeSpan = parentWindow.GetTotalTimeSpan();
+ if (totalTimeSpan.HasValue)
+ {
+ var elapsedTime = parentWindow.GetElapsedTime();
+ if (elapsedTime.HasValue)
+ {
+ var overtimeSpan = elapsedTime.Value - totalTimeSpan.Value;
+ hours = (int)overtimeSpan.TotalHours;
+ minutes = overtimeSpan.Minutes;
+ seconds = overtimeSpan.Seconds;
+ }
+ else
+ {
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+ }
+ }
+ else
+ {
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+ }
+ }
+ else
+ {
+ hours = (int)timeSpan.TotalHours;
+ minutes = timeSpan.Minutes;
+ seconds = timeSpan.Seconds;
+ }
+
+ // 更新小时显示
+ SetDigitDisplay("MinHour1Display", hours / 10, shouldShowRed);
+ SetDigitDisplay("MinHour2Display", hours % 10, shouldShowRed);
+
+ // 更新分钟显示
+ SetDigitDisplay("MinMinute1Display", minutes / 10, shouldShowRed);
+ SetDigitDisplay("MinMinute2Display", minutes % 10, shouldShowRed);
+
+ // 更新秒显示
+ SetDigitDisplay("MinSecond1Display", seconds / 10, shouldShowRed);
+ SetDigitDisplay("MinSecond2Display", seconds % 10, shouldShowRed);
+
+ SetColonDisplay(shouldShowRed);
+ }
+ }
+
+ private void ParentWindow_TimerCompleted(object sender, EventArgs e)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Close();
+ });
+ }
+
+ private void SetDigitDisplay(string pathName, int digit, bool isRed = false)
+ {
+ var path = this.FindName(pathName) as Path;
+ if (path != null)
+ {
+ string resourceKey = $"Digit{digit}";
+ var geometry = this.FindResource(resourceKey) as Geometry;
+ if (geometry != null)
+ {
+ path.Data = geometry;
+ }
+
+ // 设置颜色
+ if (isRed)
+ {
+ path.Fill = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ path.Fill = defaultBrush;
+ }
+ else
+ {
+ bool isLightTheme = IsLightTheme();
+ path.Fill = isLightTheme ? Brushes.Black : Brushes.White;
+ }
+ }
+ }
+ }
+
+ ///
+ /// 设置最小化窗口冒号显示颜色
+ ///
+ /// 是否显示为红色
+ private void SetColonDisplay(bool isRed = false)
+ {
+ var colon1 = this.FindName("MinColon1Display") as TextBlock;
+ var colon2 = this.FindName("MinColon2Display") as TextBlock;
+
+ if (colon1 != null)
+ {
+ if (isRed)
+ {
+ colon1.Foreground = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ colon1.Foreground = defaultBrush;
+ }
+ else
+ {
+ bool isLightTheme = IsLightTheme();
+ colon1.Foreground = isLightTheme ? Brushes.Black : Brushes.White;
+ }
+ }
+ }
+
+ if (colon2 != null)
+ {
+ if (isRed)
+ {
+ colon2.Foreground = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ colon2.Foreground = defaultBrush;
+ }
+ else
+ {
+ bool isLightTheme = IsLightTheme();
+ colon2.Foreground = isLightTheme ? Brushes.Black : Brushes.White;
+ }
+ }
+ }
+ }
+
+ private void ApplyTheme()
+ {
+ try
+ {
+
+ bool isLightTheme = IsLightTheme();
+ if (!isLightTheme)
+ {
+ SetDarkThemeBorder();
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"应用主题时出错: {ex.Message}");
+ }
+ }
+
+ private bool IsLightTheme()
+ {
+ try
+ {
+ var mainWindow = Application.Current.MainWindow as MainWindow;
+ if (mainWindow != null)
+ {
+ var currentModeField = mainWindow.GetType().GetField("currentMode",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ if (currentModeField != null)
+ {
+ var currentMode = currentModeField.GetValue(mainWindow);
+ return currentMode?.ToString() == "Light";
+ }
+ }
+ }
+ catch
+ {
+ // 如果获取主题失败,默认使用浅色主题
+ }
+ return true;
+ }
+
+ // 设置深色主题下的灰色边框
+ private void SetDarkThemeBorder()
+ {
+ try
+ {
+ // 找到Border元素并设置灰色边框
+ var border = this.FindName("MainBorder") as Border;
+ if (border != null)
+ {
+ border.BorderBrush = new SolidColorBrush(Color.FromRgb(64, 64, 64));
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ // 记录点击时间
+ lastClickTime = DateTime.Now;
+ // 开始拖动
+ isDragging = true;
+ lastMousePosition = e.GetPosition(this);
+ this.CaptureMouse();
+ }
+
+ private void Window_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (isDragging)
+ {
+ var currentPosition = e.GetPosition(this);
+ var deltaX = currentPosition.X - lastMousePosition.X;
+ var deltaY = currentPosition.Y - lastMousePosition.Y;
+
+ this.Left += deltaX;
+ this.Top += deltaY;
+ }
+ }
+
+ private void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (isDragging)
+ {
+ isDragging = false;
+ this.ReleaseMouseCapture();
+
+ // 如果点击时间很短,认为是单击,恢复主窗口
+ var clickDuration = DateTime.Now - lastClickTime;
+ if (clickDuration.TotalMilliseconds < 200) // 200ms内认为是单击
+ {
+ // 恢复主窗口
+ if (parentWindow != null)
+ {
+ parentWindow.Show();
+ parentWindow.Activate();
+ parentWindow.WindowState = WindowState.Normal;
+ this.Close();
+ }
+ }
+ }
+ }
+
+ private DateTime lastClickTime = DateTime.Now;
+
+ private void Window_MouseEnter(object sender, MouseEventArgs e)
+ {
+ isMouseOver = true;
+ // 鼠标进入时显示关闭按钮
+ if (CloseButton != null)
+ {
+ CloseButton.Opacity = 1.0;
+ }
+ }
+
+ private void Window_MouseLeave(object sender, MouseEventArgs e)
+ {
+ isMouseOver = false;
+ // 鼠标离开时隐藏关闭按钮
+ if (CloseButton != null)
+ {
+ CloseButton.Opacity = 0.7;
+ }
+ }
+
+ private void CloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ // 停止计时器并关闭窗口
+ if (parentWindow != null)
+ {
+ parentWindow.StopTimer();
+ }
+ this.Close();
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ if (parentWindow != null)
+ {
+ parentWindow.TimerCompleted -= ParentWindow_TimerCompleted;
+ }
+
+ // 清理资源
+ if (updateTimer != null)
+ {
+ updateTimer.Stop();
+ updateTimer.Dispose();
+ }
+ base.OnClosed(e);
+ }
+ }
+}
diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.cs b/Ink Canvas/Windows/NewStyleTimerWindow.cs
new file mode 100644
index 00000000..8a8eecae
--- /dev/null
+++ b/Ink Canvas/Windows/NewStyleTimerWindow.cs
@@ -0,0 +1,1357 @@
+using Ink_Canvas.Helpers;
+using System;
+using System.IO;
+using System.Media;
+using System.Timers;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Shapes;
+using Newtonsoft.Json;
+
+namespace Ink_Canvas
+{
+ ///
+ /// 最近计时记录数据模型
+ ///
+ public class RecentTimersData
+ {
+ public string RecentTimer1 { get; set; } = "--:--";
+ public string RecentTimer2 { get; set; } = "--:--";
+ public string RecentTimer3 { get; set; } = "--:--";
+ public string RecentTimer4 { get; set; } = "--:--";
+ public string RecentTimer5 { get; set; } = "--:--";
+ public string RecentTimer6 { get; set; } = "--:--";
+ }
+
+ ///
+ /// 新计时器UI风格的倒计时器窗口
+ ///
+ public partial class NewStyleTimerWindow : Window
+ {
+ public NewStyleTimerWindow()
+ {
+ InitializeComponent();
+ AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
+
+ timer.Elapsed += Timer_Elapsed;
+ timer.Interval = 50;
+ InitializeUI();
+
+ // 应用主题
+ ApplyTheme();
+
+ // 初始化隐藏定时器
+ hideTimer = new Timer(1000); // 每秒检查一次
+ hideTimer.Elapsed += HideTimer_Elapsed;
+ lastActivityTime = DateTime.Now;
+ }
+
+
+ private void Timer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ if (!isTimerRunning || isPaused)
+ {
+ timer.Stop();
+ return;
+ }
+
+ TimeSpan timeSpan = DateTime.Now - startTime;
+ TimeSpan totalTimeSpan = new TimeSpan(hour, minute, second);
+ double spentTimePercent = timeSpan.TotalMilliseconds / (totalTimeSpan.TotalMilliseconds);
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ if (!isOvertimeMode)
+ {
+ TimeSpan leftTimeSpan = totalTimeSpan - timeSpan;
+ if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
+
+ int totalHours = (int)leftTimeSpan.TotalHours;
+ int displayHours = totalHours;
+
+ if (displayHours > 99) displayHours = 99;
+
+ SetDigitDisplay("Digit1Display", displayHours / 10);
+ SetDigitDisplay("Digit2Display", displayHours % 10);
+ SetDigitDisplay("Digit3Display", leftTimeSpan.Minutes / 10);
+ SetDigitDisplay("Digit4Display", leftTimeSpan.Minutes % 10);
+ SetDigitDisplay("Digit5Display", leftTimeSpan.Seconds / 10);
+ SetDigitDisplay("Digit6Display", leftTimeSpan.Seconds % 10);
+
+ SetColonDisplay(false);
+
+ if (leftTimeSpan.TotalSeconds <= 6 && leftTimeSpan.TotalSeconds > 0 &&
+ MainWindow.Settings.RandSettings?.EnableProgressiveReminder == true &&
+ !hasPlayedProgressiveReminder)
+ {
+ PlayProgressiveReminderSound();
+ hasPlayedProgressiveReminder = true;
+ }
+
+ if (leftTimeSpan.TotalSeconds <= 0 && MainWindow.Settings.RandSettings?.EnableOvertimeCountUp == true)
+ {
+ isOvertimeMode = true;
+ PlayTimerSound();
+ }
+ else if (leftTimeSpan.TotalSeconds <= 0)
+ {
+ SetDigitDisplay("Digit1Display", 0);
+ SetDigitDisplay("Digit2Display", 0);
+ SetDigitDisplay("Digit3Display", 0);
+ SetDigitDisplay("Digit4Display", 0);
+ SetDigitDisplay("Digit5Display", 0);
+ SetDigitDisplay("Digit6Display", 0);
+
+ SetColonDisplay(false);
+ timer.Stop();
+ isTimerRunning = false;
+ StartPauseIcon.Data = Geometry.Parse(PlayIconData);
+ PlayTimerSound();
+
+ TimerCompleted?.Invoke(this, EventArgs.Empty);
+ HandleTimerCompletion();
+ }
+ }
+ else
+ {
+ TimeSpan overtimeSpan = timeSpan - totalTimeSpan;
+ int totalHours = (int)overtimeSpan.TotalHours;
+ int displayHours = totalHours;
+
+ if (displayHours > 99) displayHours = 99;
+
+ bool shouldShowRed = MainWindow.Settings.RandSettings?.EnableOvertimeRedText == true;
+
+ SetDigitDisplay("Digit1Display", displayHours / 10, shouldShowRed);
+ SetDigitDisplay("Digit2Display", displayHours % 10, shouldShowRed);
+ SetDigitDisplay("Digit3Display", overtimeSpan.Minutes / 10, shouldShowRed);
+ SetDigitDisplay("Digit4Display", overtimeSpan.Minutes % 10, shouldShowRed);
+ SetDigitDisplay("Digit5Display", overtimeSpan.Seconds / 10, shouldShowRed);
+ SetDigitDisplay("Digit6Display", overtimeSpan.Seconds % 10, shouldShowRed);
+
+ SetColonDisplay(shouldShowRed);
+ }
+ });
+ }
+
+ SoundPlayer player = new SoundPlayer();
+ MediaPlayer mediaPlayer = new MediaPlayer();
+
+ int hour = 0;
+ int minute = 5;
+ int second = 0;
+
+ DateTime startTime = DateTime.Now;
+ DateTime pauseTime = DateTime.Now;
+
+ bool isTimerRunning = false;
+ bool isPaused = false;
+ bool isOvertimeMode = false;
+ TimeSpan remainingTime = TimeSpan.Zero;
+ bool hasPlayedProgressiveReminder = false;
+
+ Timer timer = new Timer();
+ private Timer hideTimer;
+ private MinimizedTimerWindow minimizedWindow;
+ private DateTime lastActivityTime;
+ private bool isFullscreenMode = false;
+ private FullscreenTimerWindow fullscreenWindow;
+ public event EventHandler TimerCompleted;
+ public TimeSpan? GetTotalTimeSpan()
+ {
+ return new TimeSpan(hour, minute, second);
+ }
+
+ public TimeSpan? GetElapsedTime()
+ {
+ if (isPaused) return null;
+
+ return DateTime.Now - startTime;
+ }
+
+ // 最近计时记录
+ private string recentTimer1 = "--:--";
+ private string recentTimer2 = "--:--";
+ private string recentTimer3 = "--:--";
+ private string recentTimer4 = "--:--";
+ private string recentTimer5 = "--:--";
+ private string recentTimer6 = "--:--";
+
+ // JSON文件路径
+ private static readonly string ConfigsFolder = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
+ private static readonly string RecentTimersJsonPath = System.IO.Path.Combine(ConfigsFolder, "RecentTimers.json");
+
+ private void InitializeUI()
+ {
+ UpdateDigitDisplays();
+ LoadRecentTimers();
+ UpdateRecentTimerDisplays();
+ InitializeTabState();
+ }
+
+ private void InitializeTabState()
+ {
+ // 设置默认选中CommonTab
+ CommonTimersGrid.Visibility = Visibility.Visible;
+ RecentTimersGrid.Visibility = Visibility.Collapsed;
+
+ // 设置tab文字颜色和样式
+ var commonText = this.FindName("CommonTabText") as TextBlock;
+ var recentText = this.FindName("RecentTabText") as TextBlock;
+ if (commonText != null)
+ {
+ commonText.FontWeight = FontWeights.Bold;
+ commonText.Opacity = 1.0;
+ commonText.Foreground = new SolidColorBrush(Colors.White);
+ }
+ if (recentText != null)
+ {
+ recentText.FontWeight = FontWeights.Normal;
+ recentText.Opacity = 0.8;
+ recentText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
+ }
+
+ // 设置指示器位置
+ var indicator = this.FindName("SegmentedIndicator") as Border;
+ if (indicator != null)
+ {
+ indicator.CornerRadius = new CornerRadius(7.5, 0, 0, 7.5);
+ indicator.Margin = new Thickness(0, 0, 0, 0);
+ }
+ }
+
+ private void ApplyTheme()
+ {
+ try
+ {
+ // 应用主题设置
+ if (MainWindow.Settings != null)
+ {
+ ApplyTheme(MainWindow.Settings);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用新计时器UI倒计时窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private void ApplyTheme(Settings settings)
+ {
+ try
+ {
+ if (settings.Appearance.Theme == 0) // 浅色主题
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
+ }
+ else if (settings.Appearance.Theme == 1) // 深色主题
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
+ SetDarkThemeBorder();
+ }
+ else // 跟随系统主题
+ {
+ bool isSystemLight = IsSystemThemeLight();
+ if (isSystemLight)
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
+ }
+ else
+ {
+ iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
+ SetDarkThemeBorder();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"应用新计时器UI倒计时窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+ }
+
+ private bool IsSystemThemeLight()
+ {
+ var light = false;
+ try
+ {
+ var registryKey = Microsoft.Win32.Registry.CurrentUser;
+ var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
+ if (themeKey != null)
+ {
+ var value = themeKey.GetValue("AppsUseLightTheme");
+ if (value != null)
+ {
+ light = (int)value == 1;
+ }
+ themeKey.Close();
+ }
+ }
+ catch
+ {
+ // 如果读取注册表失败,默认为浅色主题
+ light = true;
+ }
+ return light;
+ }
+
+ private void UpdateDigitDisplays()
+ {
+ SetDigitDisplay("Digit1Display", hour / 10);
+ SetDigitDisplay("Digit2Display", hour % 10);
+ SetDigitDisplay("Digit3Display", minute / 10);
+ SetDigitDisplay("Digit4Display", minute % 10);
+ SetDigitDisplay("Digit5Display", second / 10);
+ SetDigitDisplay("Digit6Display", second % 10);
+
+ SetColonDisplay(false);
+ }
+
+ private void HideTimer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ // 只有在计时器运行时且不在全屏模式下才检查自动隐藏
+ if (isTimerRunning && !isPaused && !isFullscreenMode)
+ {
+ var timeSinceLastActivity = DateTime.Now - lastActivityTime;
+ if (timeSinceLastActivity.TotalSeconds >= 5)
+ {
+ ShowMinimizedWindow();
+ }
+ }
+ });
+ }
+
+ private void ShowMinimizedWindow()
+ {
+ if (minimizedWindow == null || !minimizedWindow.IsVisible)
+ {
+ minimizedWindow = new MinimizedTimerWindow(this);
+ minimizedWindow.Show();
+
+ // 隐藏主窗口
+ this.Hide();
+ }
+ }
+
+ public void UpdateActivityTime()
+ {
+ lastActivityTime = DateTime.Now;
+ }
+
+ public void SetFullscreenMode(bool isFullscreen)
+ {
+ isFullscreenMode = isFullscreen;
+ }
+
+ // 更新剩余时间
+ private void UpdateRemainingTime()
+ {
+ if (isTimerRunning && !isPaused)
+ {
+ // 获取当前剩余时间
+ TimeSpan? currentRemaining = GetRemainingTime();
+ if (currentRemaining.HasValue)
+ {
+ // 计算已经过去的时间
+ TimeSpan elapsedTime = DateTime.Now - startTime;
+
+ // 计算新的总时间
+ TimeSpan newTotalTime = new TimeSpan(hour, minute, second);
+
+ // 如果新设置的时间小于已经过去的时间,则设置为0
+ if (newTotalTime <= elapsedTime)
+ {
+ remainingTime = TimeSpan.Zero;
+ }
+ else
+ {
+ // 否则,剩余时间 = 新总时间 - 已经过去的时间
+ remainingTime = newTotalTime - elapsedTime;
+ }
+ }
+ else
+ {
+ // 如果没有剩余时间信息,直接设置新的剩余时间
+ remainingTime = new TimeSpan(hour, minute, second);
+ }
+ }
+ }
+
+ // 更新特定时间单位的剩余时间
+ private void UpdateSpecificTimeUnit(int newHour, int newMinute, int newSecond)
+ {
+ if (isTimerRunning && !isPaused)
+ {
+ // 获取当前剩余时间
+ TimeSpan? currentRemaining = GetRemainingTime();
+ if (currentRemaining.HasValue)
+ {
+ // 计算已经过去的时间
+ TimeSpan elapsedTime = DateTime.Now - startTime;
+
+ // 计算新的总时间
+ TimeSpan newTotalTime = new TimeSpan(newHour, newMinute, newSecond);
+
+ // 如果新设置的时间小于已经过去的时间,则设置为0
+ if (newTotalTime <= elapsedTime)
+ {
+ remainingTime = TimeSpan.Zero;
+ }
+ else
+ {
+ // 否则,剩余时间 = 新总时间 - 已经过去的时间
+ remainingTime = newTotalTime - elapsedTime;
+ }
+ }
+ else
+ {
+ // 如果没有剩余时间信息,直接设置新的剩余时间
+ remainingTime = new TimeSpan(newHour, newMinute, newSecond);
+ }
+ }
+ }
+
+ public bool IsTimerRunning => isTimerRunning;
+
+ public TimeSpan? GetRemainingTime()
+ {
+ if (isPaused) return null;
+
+ var elapsed = DateTime.Now - startTime;
+ var totalSeconds = hour * 3600 + minute * 60 + second;
+ var remaining = totalSeconds - elapsed.TotalSeconds;
+
+ return TimeSpan.FromSeconds(remaining);
+ }
+
+ public void StopTimer()
+ {
+ timer.Stop();
+ isTimerRunning = false;
+ StartPauseIcon.Data = Geometry.Parse(PlayIconData);
+ }
+
+ private void Window_MouseMove(object sender, MouseEventArgs e)
+ {
+ UpdateActivityTime();
+ }
+
+ private void Window_MouseEnter(object sender, MouseEventArgs e)
+ {
+ UpdateActivityTime();
+ }
+
+ ///
+ /// 根据数字值设置SVG数字显示
+ ///
+ /// Path控件的名称
+ /// 要显示的数字(0-9)
+ /// 是否显示为红色
+ private void SetDigitDisplay(string pathName, int digit, bool isRed = false)
+ {
+ var path = this.FindName(pathName) as System.Windows.Shapes.Path;
+ if (path != null)
+ {
+ string resourceKey = $"Digit{digit}";
+ var geometry = this.FindResource(resourceKey) as Geometry;
+ if (geometry != null)
+ {
+ path.Data = geometry;
+ }
+
+ if (isRed)
+ {
+ path.Fill = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ path.Fill = defaultBrush;
+ }
+ else
+ {
+ path.Fill = Brushes.White;
+ }
+ }
+ }
+ }
+
+ ///
+ /// 设置冒号显示颜色
+ ///
+ /// 是否显示为红色
+ private void SetColonDisplay(bool isRed = false)
+ {
+ var colon1 = this.FindName("Colon1Display") as TextBlock;
+ var colon2 = this.FindName("Colon2Display") as TextBlock;
+
+ if (colon1 != null)
+ {
+ if (isRed)
+ {
+ colon1.Foreground = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ colon1.Foreground = defaultBrush;
+ }
+ else
+ {
+ colon1.Foreground = Brushes.White;
+ }
+ }
+ }
+
+ if (colon2 != null)
+ {
+ if (isRed)
+ {
+ colon2.Foreground = Brushes.Red;
+ }
+ else
+ {
+ var defaultBrush = this.FindResource("NewTimerWindowDigitForeground") as Brush;
+ if (defaultBrush != null)
+ {
+ colon2.Foreground = defaultBrush;
+ }
+ else
+ {
+ colon2.Foreground = Brushes.White;
+ }
+ }
+ }
+ }
+
+ // 第1位数字(小时十位)
+ private void Digit1Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentHour = hour;
+ int hourTens = currentHour / 10;
+ int hourOnes = currentHour % 10;
+
+ hourTens++;
+ if (hourTens >= 10) hourTens = 0;
+
+ hour = hourTens * 10 + hourOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit1Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentHour = hour;
+ int hourTens = currentHour / 10;
+ int hourOnes = currentHour % 10;
+
+ hourTens--;
+ if (hourTens < 0) hourTens = 9;
+
+ hour = hourTens * 10 + hourOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 第2位数字(小时个位)
+ private void Digit2Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentHour = hour;
+ int hourTens = currentHour / 10;
+ int hourOnes = currentHour % 10;
+
+ hourOnes++;
+ if (hourOnes >= 10)
+ {
+ hourOnes = 0;
+ hourTens++;
+ if (hourTens >= 10) hourTens = 0;
+ }
+
+ hour = hourTens * 10 + hourOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit2Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentHour = hour;
+ int hourTens = currentHour / 10;
+ int hourOnes = currentHour % 10;
+
+ hourOnes--;
+ if (hourOnes < 0)
+ {
+ hourOnes = 9;
+ hourTens--;
+ if (hourTens < 0) hourTens = 9;
+ }
+
+ hour = hourTens * 10 + hourOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 第3位数字(分钟十位)
+ private void Digit3Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentMinute = minute;
+ int minuteTens = currentMinute / 10;
+ int minuteOnes = currentMinute % 10;
+
+ minuteTens++;
+ if (minuteTens >= 6) minuteTens = 0;
+
+ minute = minuteTens * 10 + minuteOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit3Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentMinute = minute;
+ int minuteTens = currentMinute / 10;
+ int minuteOnes = currentMinute % 10;
+
+ minuteTens--;
+ if (minuteTens < 0) minuteTens = 5;
+
+ minute = minuteTens * 10 + minuteOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 第4位数字(分钟个位)
+ private void Digit4Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentMinute = minute;
+ int minuteTens = currentMinute / 10;
+ int minuteOnes = currentMinute % 10;
+
+ minuteOnes++;
+ if (minuteOnes >= 10)
+ {
+ minuteOnes = 0;
+ minuteTens++;
+ if (minuteTens >= 6) minuteTens = 0;
+ }
+
+ minute = minuteTens * 10 + minuteOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit4Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentMinute = minute;
+ int minuteTens = currentMinute / 10;
+ int minuteOnes = currentMinute % 10;
+
+ minuteOnes--;
+ if (minuteOnes < 0)
+ {
+ minuteOnes = 9;
+ minuteTens--;
+ if (minuteTens < 0) minuteTens = 5;
+ }
+
+ minute = minuteTens * 10 + minuteOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 第5位数字(秒十位)
+ private void Digit5Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentSecond = second;
+ int secondTens = currentSecond / 10;
+ int secondOnes = currentSecond % 10;
+
+ secondTens++;
+ if (secondTens >= 6) secondTens = 0;
+
+ second = secondTens * 10 + secondOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit5Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentSecond = second;
+ int secondTens = currentSecond / 10;
+ int secondOnes = currentSecond % 10;
+
+ secondTens--;
+ if (secondTens < 0) secondTens = 5;
+
+ second = secondTens * 10 + secondOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 第6位数字(秒个位)
+ private void Digit6Plus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentSecond = second;
+ int secondTens = currentSecond / 10;
+ int secondOnes = currentSecond % 10;
+
+ secondOnes++;
+ if (secondOnes >= 10)
+ {
+ secondOnes = 0;
+ secondTens++;
+ if (secondTens >= 6) secondTens = 0;
+ }
+
+ second = secondTens * 10 + secondOnes;
+ UpdateDigitDisplays();
+ }
+
+ private void Digit6Minus_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning) return;
+ int currentSecond = second;
+ int secondTens = currentSecond / 10;
+ int secondOnes = currentSecond % 10;
+
+ secondOnes--;
+ if (secondOnes < 0)
+ {
+ secondOnes = 9;
+ secondTens--;
+ if (secondTens < 0) secondTens = 5;
+ }
+
+ second = secondTens * 10 + secondOnes;
+ UpdateDigitDisplays();
+ }
+
+ // 图标数据常量
+ private const string PlayIconData = "M6.5 4.00004V20C6.49995 20.178 6.54737 20.3527 6.63738 20.5062C6.72739 20.6597 6.85672 20.7864 7.01202 20.8732C7.16733 20.96 7.34299 21.0038 7.52088 21.0001C7.69878 20.9964 7.87245 20.9453 8.024 20.852L21.024 12.852C21.1696 12.7626 21.2898 12.6373 21.3733 12.4881C21.4567 12.339 21.5005 12.1709 21.5005 12C21.5005 11.8291 21.4567 11.6611 21.3733 11.512C21.2898 11.3628 21.1696 11.2375 21.024 11.148L8.024 3.14804C7.87245 3.0548 7.69878 3.00369 7.52088 2.99997C7.34299 2.99626 7.16733 3.04007 7.01202 3.1269C6.85672 3.21372 6.72739 3.34042 6.63738 3.4939C6.54737 3.64739 6.49995 3.82211 6.5 4.00004Z";
+ private const string PauseIconData = "M9.5 4H7.5C6.96957 4 6.46086 4.21071 6.08579 4.58579C5.71071 4.96086 5.5 5.46957 5.5 6V18C5.5 18.5304 5.71071 19.0391 6.08579 19.4142C6.46086 19.7893 6.96957 20 7.5 20H9.5C10.0304 20 10.5391 19.7893 10.9142 19.4142C11.2893 19.0391 11.5 18.5304 11.5 18V6C11.5 5.46957 11.2893 4.96086 10.9142 4.58579C10.5391 4.21071 10.0304 4 9.5 4Z M17.5 4H15.5C14.9696 4 14.4609 4.21071 14.0858 4.58579C13.7107 4.96086 13.5 5.46957 13.5 6V18C13.5 18.5304 13.7107 19.0391 14.0858 19.4142C14.4609 19.7893 14.9696 20 15.5 20H17.5C18.0304 20 18.5391 19.7893 18.9142 19.4142C19.2893 19.0391 19.5 18.5304 19.5 18V6C19.5 5.46957 19.2893 4.96086 18.9142 4.58579C18.5391 4.21071 18.0304 4 17.5 4Z";
+
+ private void StartPause_Click(object sender, RoutedEventArgs e)
+ {
+ if (isPaused && isTimerRunning)
+ {
+ // 继续计时
+ startTime += DateTime.Now - pauseTime;
+ StartPauseIcon.Data = Geometry.Parse(PauseIconData);
+ isPaused = false;
+ timer.Start();
+ }
+ else if (isTimerRunning)
+ {
+ // 暂停计时
+ pauseTime = DateTime.Now;
+ StartPauseIcon.Data = Geometry.Parse(PlayIconData);
+ isPaused = true;
+ timer.Stop();
+ }
+ else
+ {
+ // 开始计时
+ if (hour == 0 && minute == 0 && second == 0)
+ {
+ second = 1;
+ UpdateDigitDisplays();
+ }
+
+ startTime = DateTime.Now;
+ StartPauseIcon.Data = Geometry.Parse(PauseIconData);
+ isPaused = false;
+ isTimerRunning = true;
+ isOvertimeMode = false;
+ hasPlayedProgressiveReminder = false;
+ timer.Start();
+
+ // 启动隐藏定时器
+ hideTimer.Start();
+
+ // 保存到最近计时记录
+ SaveRecentTimer();
+ }
+ }
+
+ private void Reset_Click(object sender, RoutedEventArgs e)
+ {
+ if (!isTimerRunning)
+ {
+ UpdateDigitDisplays();
+ isOvertimeMode = false;
+ }
+ else if (isTimerRunning && isPaused)
+ {
+ UpdateDigitDisplays();
+ StartPauseIcon.Data = Geometry.Parse(PlayIconData);
+ isTimerRunning = false;
+ timer.Stop();
+ isPaused = false;
+ isOvertimeMode = false;
+ }
+ else
+ {
+ startTime = DateTime.Now;
+ Timer_Elapsed(timer, null);
+ }
+ }
+
+ private void PlayTimerSound()
+ {
+ try
+ {
+ double volume = MainWindow.Settings.RandSettings?.TimerVolume ?? 1.0;
+ mediaPlayer.Volume = volume;
+
+ if (!string.IsNullOrEmpty(MainWindow.Settings.RandSettings?.CustomTimerSoundPath) &&
+ System.IO.File.Exists(MainWindow.Settings.RandSettings.CustomTimerSoundPath))
+ {
+ mediaPlayer.Open(new Uri(MainWindow.Settings.RandSettings.CustomTimerSoundPath));
+ }
+ else
+ {
+ string tempPath = System.IO.Path.GetTempFileName() + ".wav";
+ using (var stream = Properties.Resources.TimerDownNotice)
+ {
+ using (var fileStream = new System.IO.FileStream(tempPath, System.IO.FileMode.Create))
+ {
+ stream.CopyTo(fileStream);
+ }
+ }
+ mediaPlayer.Open(new Uri(tempPath));
+ }
+
+ mediaPlayer.Play();
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"播放计时器铃声失败: {ex.Message}");
+ }
+ }
+
+ private void PlayProgressiveReminderSound()
+ {
+ try
+ {
+ double volume = MainWindow.Settings.RandSettings?.ProgressiveReminderVolume ?? 1.0;
+ mediaPlayer.Volume = volume;
+
+ if (!string.IsNullOrEmpty(MainWindow.Settings.RandSettings?.ProgressiveReminderSoundPath) &&
+ System.IO.File.Exists(MainWindow.Settings.RandSettings.ProgressiveReminderSoundPath))
+ {
+ mediaPlayer.Open(new Uri(MainWindow.Settings.RandSettings.ProgressiveReminderSoundPath));
+ }
+ else
+ {
+ string tempPath = System.IO.Path.GetTempFileName() + ".wav";
+ using (var stream = Properties.Resources.ProgressiveAudio)
+ {
+ using (var fileStream = new System.IO.FileStream(tempPath, System.IO.FileMode.Create))
+ {
+ stream.CopyTo(fileStream);
+ }
+ }
+ mediaPlayer.Open(new Uri(tempPath));
+ }
+
+ mediaPlayer.Play();
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"播放渐进提醒音频失败: {ex.Message}");
+ }
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ // 窗口加载时的初始化
+ }
+
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ isTimerRunning = false;
+
+ if (MainWindow.Settings != null)
+ {
+ var mainWindow = Application.Current.MainWindow as MainWindow;
+ if (mainWindow != null)
+ {
+ try
+ {
+ var currentModeField = mainWindow.GetType().GetField("currentMode",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ if (currentModeField != null)
+ {
+ int currentMode = (int)currentModeField.GetValue(mainWindow);
+ if (currentMode == 1) // 白板模式
+ {
+ mainWindow.Topmost = false; // 保持白板模式下的非置顶状态
+ }
+ else
+ {
+ mainWindow.Topmost = true; // 其他模式恢复置顶
+ }
+ }
+ }
+ catch
+ {
+ // 如果反射失败,使用默认行为
+ mainWindow.Topmost = true;
+ }
+ }
+ }
+ }
+
+ private void CloseButton_Click(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+
+ private void WindowDragMove(object sender, MouseEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ DragMove();
+ }
+
+ private void CommonTab_Click(object sender, RoutedEventArgs e)
+ {
+ CommonTimersGrid.Visibility = Visibility.Visible;
+ RecentTimersGrid.Visibility = Visibility.Collapsed;
+
+ // 更新字体粗细、透明度和颜色
+ var commonText = this.FindName("CommonTabText") as TextBlock;
+ var recentText = this.FindName("RecentTabText") as TextBlock;
+ if (commonText != null)
+ {
+ commonText.FontWeight = FontWeights.Bold;
+ commonText.Opacity = 1.0;
+ commonText.Foreground = new SolidColorBrush(Colors.White);
+ }
+ if (recentText != null)
+ {
+ recentText.FontWeight = FontWeights.Normal;
+ recentText.Opacity = 0.8;
+ recentText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
+ }
+
+ // 移动指示器到左侧
+ var indicator = this.FindName("SegmentedIndicator") as Border;
+ if (indicator != null)
+ {
+ // 设置左侧圆角
+ indicator.CornerRadius = new CornerRadius(7.5, 0, 0, 7.5);
+ var animation = new System.Windows.Media.Animation.ThicknessAnimation(
+ new Thickness(0, 0, 0, 0),
+ TimeSpan.FromMilliseconds(200));
+ indicator.BeginAnimation(Border.MarginProperty, animation);
+ }
+ }
+
+ private void RecentTab_Click(object sender, RoutedEventArgs e)
+ {
+ CommonTimersGrid.Visibility = Visibility.Collapsed;
+ RecentTimersGrid.Visibility = Visibility.Visible;
+
+ // 更新字体粗细、透明度和颜色
+ var commonText = this.FindName("CommonTabText") as TextBlock;
+ var recentText = this.FindName("RecentTabText") as TextBlock;
+ if (commonText != null)
+ {
+ commonText.FontWeight = FontWeights.Normal;
+ commonText.Opacity = 0.8;
+ commonText.Foreground = new SolidColorBrush(Color.FromRgb(102, 102, 102));
+ }
+ if (recentText != null)
+ {
+ recentText.FontWeight = FontWeights.Bold;
+ recentText.Opacity = 1.0;
+ recentText.Foreground = new SolidColorBrush(Colors.White);
+ }
+
+ // 移动指示器到右侧
+ var indicator = this.FindName("SegmentedIndicator") as Border;
+ if (indicator != null)
+ {
+ // 设置右侧圆角
+ indicator.CornerRadius = new CornerRadius(0, 7.5, 7.5, 0);
+ var animation = new System.Windows.Media.Animation.ThicknessAnimation(
+ new Thickness(118, 0, 0, 0),
+ TimeSpan.FromMilliseconds(200));
+ indicator.BeginAnimation(Border.MarginProperty, animation);
+ }
+ }
+
+ // 常用计时事件处理
+ private void Common5Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(0, 5, 0);
+ }
+
+ private void Common10Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(0, 10, 0);
+ }
+
+ private void Common15Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(0, 15, 0);
+ }
+
+ private void Common30Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(0, 30, 0);
+ }
+
+ private void Common45Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(0, 45, 0);
+ }
+
+ private void Common60Min_Click(object sender, RoutedEventArgs e)
+ {
+ if (isTimerRunning && !isPaused) return;
+ SetQuickTime(1, 0, 0);
+ }
+
+ // 最近计时事件处理
+ private void RecentTimer1_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer1 == "--:--") return;
+ ApplyRecentTimer(recentTimer1);
+ }
+
+ private void RecentTimer2_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer2 == "--:--") return;
+ ApplyRecentTimer(recentTimer2);
+ }
+
+ private void RecentTimer3_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer3 == "--:--") return;
+ ApplyRecentTimer(recentTimer3);
+ }
+
+ private void RecentTimer4_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer4 == "--:--") return;
+ ApplyRecentTimer(recentTimer4);
+ }
+
+ private void RecentTimer5_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer5 == "--:--") return;
+ ApplyRecentTimer(recentTimer5);
+ }
+
+ private void RecentTimer6_Click(object sender, RoutedEventArgs e)
+ {
+ if ((isTimerRunning && !isPaused) || recentTimer6 == "--:--") return;
+ ApplyRecentTimer(recentTimer6);
+ }
+
+ // 设置快捷时间
+ private void SetQuickTime(int h, int m, int s)
+ {
+ hour = h;
+ minute = m;
+ second = s;
+ UpdateDigitDisplays();
+ }
+
+ // 应用最近计时
+ private void ApplyRecentTimer(string timeString)
+ {
+ if (timeString == "--:--") return;
+
+ try
+ {
+ var parts = timeString.Split(':');
+ if (parts.Length == 2)
+ {
+ int minutes = int.Parse(parts[0]);
+ int seconds = int.Parse(parts[1]);
+ SetQuickTime(0, minutes, seconds);
+ }
+ }
+ catch
+ {
+ // 如果解析失败,忽略
+ }
+ }
+
+ // 保存最近计时记录
+ private void SaveRecentTimer()
+ {
+ if (hour == 0 && minute == 0 && second == 0) return;
+
+ string currentTime = $"{minute:D2}:{second:D2}";
+
+ // 检查是否已存在相同的时间
+ var existingIndex = -1;
+ if (recentTimer1 == currentTime) existingIndex = 0;
+ else if (recentTimer2 == currentTime) existingIndex = 1;
+ else if (recentTimer3 == currentTime) existingIndex = 2;
+ else if (recentTimer4 == currentTime) existingIndex = 3;
+ else if (recentTimer5 == currentTime) existingIndex = 4;
+ else if (recentTimer6 == currentTime) existingIndex = 5;
+
+ if (existingIndex >= 0)
+ {
+ // 如果存在重复,将其移到最前面
+ string duplicateTimer = GetRecentTimerByIndex(existingIndex);
+
+ // 移除重复项
+ RemoveRecentTimerByIndex(existingIndex);
+
+ // 将重复项添加到最前面
+ recentTimer6 = recentTimer5;
+ recentTimer5 = recentTimer4;
+ recentTimer4 = recentTimer3;
+ recentTimer3 = recentTimer2;
+ recentTimer2 = recentTimer1;
+ recentTimer1 = duplicateTimer;
+ }
+ else
+ {
+ // 如果不存在重复,正常添加新记录
+ recentTimer6 = recentTimer5;
+ recentTimer5 = recentTimer4;
+ recentTimer4 = recentTimer3;
+ recentTimer3 = recentTimer2;
+ recentTimer2 = recentTimer1;
+ recentTimer1 = currentTime;
+ }
+
+ UpdateRecentTimerDisplays();
+ SaveRecentTimersToRegistry();
+ }
+
+ private string GetRecentTimerByIndex(int index)
+ {
+ switch (index)
+ {
+ case 0: return recentTimer1;
+ case 1: return recentTimer2;
+ case 2: return recentTimer3;
+ case 3: return recentTimer4;
+ case 4: return recentTimer5;
+ case 5: return recentTimer6;
+ default: return "";
+ }
+ }
+
+ private void RemoveRecentTimerByIndex(int index)
+ {
+ switch (index)
+ {
+ case 0:
+ recentTimer1 = recentTimer2;
+ recentTimer2 = recentTimer3;
+ recentTimer3 = recentTimer4;
+ recentTimer4 = recentTimer5;
+ recentTimer5 = recentTimer6;
+ recentTimer6 = "--:--";
+ break;
+ case 1:
+ recentTimer2 = recentTimer3;
+ recentTimer3 = recentTimer4;
+ recentTimer4 = recentTimer5;
+ recentTimer5 = recentTimer6;
+ recentTimer6 = "--:--";
+ break;
+ case 2:
+ recentTimer3 = recentTimer4;
+ recentTimer4 = recentTimer5;
+ recentTimer5 = recentTimer6;
+ recentTimer6 = "--:--";
+ break;
+ case 3:
+ recentTimer4 = recentTimer5;
+ recentTimer5 = recentTimer6;
+ recentTimer6 = "--:--";
+ break;
+ case 4:
+ recentTimer5 = recentTimer6;
+ recentTimer6 = "--:--";
+ break;
+ case 5:
+ recentTimer6 = "--:--";
+ break;
+ }
+ }
+
+ // 更新最近计时显示
+ private void UpdateRecentTimerDisplays()
+ {
+ try
+ {
+ RecentTimer1Text.Text = recentTimer1;
+ RecentTimer2Text.Text = recentTimer2;
+ RecentTimer3Text.Text = recentTimer3;
+ RecentTimer4Text.Text = recentTimer4;
+ RecentTimer5Text.Text = recentTimer5;
+ RecentTimer6Text.Text = recentTimer6;
+ }
+ catch
+ {
+ // 如果UI元素还未初始化,忽略错误
+ }
+ }
+
+ // 从JSON文件加载最近计时记录
+ private void LoadRecentTimers()
+ {
+ try
+ {
+ // 确保Configs文件夹存在
+ if (!Directory.Exists(ConfigsFolder))
+ {
+ Directory.CreateDirectory(ConfigsFolder);
+ }
+
+ if (!File.Exists(RecentTimersJsonPath))
+ {
+ recentTimer1 = "--:--";
+ recentTimer2 = "--:--";
+ recentTimer3 = "--:--";
+ recentTimer4 = "--:--";
+ recentTimer5 = "--:--";
+ recentTimer6 = "--:--";
+ return;
+ }
+
+ // 读取JSON文件
+ string jsonContent = File.ReadAllText(RecentTimersJsonPath);
+ var data = JsonConvert.DeserializeObject(jsonContent);
+
+ if (data != null)
+ {
+ recentTimer1 = data.RecentTimer1 ?? "--:--";
+ recentTimer2 = data.RecentTimer2 ?? "--:--";
+ recentTimer3 = data.RecentTimer3 ?? "--:--";
+ recentTimer4 = data.RecentTimer4 ?? "--:--";
+ recentTimer5 = data.RecentTimer5 ?? "--:--";
+ recentTimer6 = data.RecentTimer6 ?? "--:--";
+ }
+ else
+ {
+ recentTimer1 = "--:--";
+ recentTimer2 = "--:--";
+ recentTimer3 = "--:--";
+ recentTimer4 = "--:--";
+ recentTimer5 = "--:--";
+ recentTimer6 = "--:--";
+ }
+ }
+ catch (Exception ex)
+ {
+ recentTimer1 = "--:--";
+ recentTimer2 = "--:--";
+ recentTimer3 = "--:--";
+ recentTimer4 = "--:--";
+ recentTimer5 = "--:--";
+ recentTimer6 = "--:--";
+ }
+ }
+
+ // 保存最近计时记录到JSON文件
+ private void SaveRecentTimersToRegistry()
+ {
+ try
+ {
+ // 确保Configs文件夹存在
+ if (!Directory.Exists(ConfigsFolder))
+ {
+ Directory.CreateDirectory(ConfigsFolder);
+ }
+
+ // 创建数据对象
+ var data = new RecentTimersData
+ {
+ RecentTimer1 = recentTimer1,
+ RecentTimer2 = recentTimer2,
+ RecentTimer3 = recentTimer3,
+ RecentTimer4 = recentTimer4,
+ RecentTimer5 = recentTimer5,
+ RecentTimer6 = recentTimer6
+ };
+
+ // 序列化为JSON并保存到文件
+ string jsonContent = JsonConvert.SerializeObject(data, Formatting.Indented);
+ File.WriteAllText(RecentTimersJsonPath, jsonContent);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ // 设置深色主题下的灰色边框
+ private void SetDarkThemeBorder()
+ {
+ try
+ {
+ if (MainBorder != null)
+ {
+ MainBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(64, 64, 64));
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ private void Fullscreen_Click(object sender, RoutedEventArgs e)
+ {
+ ShowFullscreenTimer();
+ }
+
+ private void ShowFullscreenTimer()
+ {
+ // 设置全屏模式标志
+ isFullscreenMode = true;
+
+ // 创建全屏计时器窗口
+ fullscreenWindow = new FullscreenTimerWindow(this);
+ fullscreenWindow.Show();
+
+ // 隐藏主窗口
+ this.Hide();
+ }
+
+ private void HandleTimerCompletion()
+ {
+ if (minimizedWindow != null)
+ {
+ minimizedWindow.Close();
+ minimizedWindow = null;
+ this.Show();
+ this.Activate();
+ this.WindowState = WindowState.Normal;
+ }
+ else if (fullscreenWindow != null)
+ {
+ fullscreenWindow.Close();
+ fullscreenWindow = null;
+ isFullscreenMode = false;
+ this.Show();
+ this.Activate();
+ this.WindowState = WindowState.Normal;
+ }
+ }
+ }
+}
diff --git a/Ink Canvas/Windows/NewStyleTimerWindow.xaml b/Ink Canvas/Windows/NewStyleTimerWindow.xaml
new file mode 100644
index 00000000..06b76a1a
--- /dev/null
+++ b/Ink Canvas/Windows/NewStyleTimerWindow.xaml
@@ -0,0 +1,783 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Ink Canvas/Windows/OperatingGuideWindow.xaml.cs b/Ink Canvas/Windows/OperatingGuideWindow.xaml.cs
index 39ded587..28e83faa 100644
--- a/Ink Canvas/Windows/OperatingGuideWindow.xaml.cs
+++ b/Ink Canvas/Windows/OperatingGuideWindow.xaml.cs
@@ -1,8 +1,8 @@
using Ink_Canvas.Helpers;
-using System.Windows;
-using System.Windows.Input;
using iNKORE.UI.WPF.Modern;
using System;
+using System.Windows;
+using System.Windows.Input;
namespace Ink_Canvas
{
@@ -54,9 +54,9 @@ namespace Ink_Canvas
try
{
// 根据当前主题设置窗口主题
- bool isDarkTheme = MainWindow.Settings.Appearance.Theme == 1 ||
+ bool isDarkTheme = MainWindow.Settings.Appearance.Theme == 1 ||
(MainWindow.Settings.Appearance.Theme == 2 && !IsSystemThemeLight());
-
+
if (isDarkTheme)
{
ThemeManager.SetRequestedTheme(this, ElementTheme.Dark);
diff --git a/Ink Canvas/Windows/RandWindow.xaml.cs b/Ink Canvas/Windows/RandWindow.xaml.cs
index fc16446c..c3052eef 100644
--- a/Ink Canvas/Windows/RandWindow.xaml.cs
+++ b/Ink Canvas/Windows/RandWindow.xaml.cs
@@ -487,7 +487,7 @@ namespace Ink_Canvas
{
// 重新应用主题
ApplyTheme(MainWindow.Settings);
-
+
// 强制刷新UI
InvalidateVisual();
}
diff --git a/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml b/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml
deleted file mode 100644
index fd4a0243..00000000
--- a/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml
+++ /dev/null
@@ -1,322 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml.cs b/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml.cs
deleted file mode 100644
index e719f753..00000000
--- a/Ink Canvas/Windows/SeewoStyleTimerWindow.xaml.cs
+++ /dev/null
@@ -1,434 +0,0 @@
-using Ink_Canvas.Helpers;
-using System;
-using System.Media;
-using System.Timers;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Interop;
-using System.Windows.Media;
-
-namespace Ink_Canvas
-{
- ///
- /// 仿希沃风格的倒计时器窗口
- ///
- public partial class SeewoStyleTimerWindow : Window
- {
- public SeewoStyleTimerWindow()
- {
- InitializeComponent();
- AnimationsHelper.ShowWithSlideFromBottomAndFade(this, 0.25);
-
- timer.Elapsed += Timer_Elapsed;
- timer.Interval = 50;
- InitializeUI();
-
- // 应用主题
- ApplyTheme();
- }
-
-
- private void Timer_Elapsed(object sender, ElapsedEventArgs e)
- {
- if (!isTimerRunning || isPaused)
- {
- timer.Stop();
- return;
- }
-
- TimeSpan timeSpan = DateTime.Now - startTime;
- TimeSpan totalTimeSpan = new TimeSpan(hour, minute, second);
- TimeSpan leftTimeSpan = totalTimeSpan - timeSpan;
- if (leftTimeSpan.Milliseconds > 0) leftTimeSpan += new TimeSpan(0, 0, 1);
-
- Application.Current.Dispatcher.Invoke(() =>
- {
- Digit1Display.Text = (leftTimeSpan.Hours / 10).ToString();
- Digit2Display.Text = (leftTimeSpan.Hours % 10).ToString();
- Digit3Display.Text = (leftTimeSpan.Minutes / 10).ToString();
- Digit4Display.Text = (leftTimeSpan.Minutes % 10).ToString();
- Digit5Display.Text = (leftTimeSpan.Seconds / 10).ToString();
- Digit6Display.Text = (leftTimeSpan.Seconds % 10).ToString();
-
- if (leftTimeSpan.TotalSeconds <= 0)
- {
- Digit1Display.Text = "0";
- Digit2Display.Text = "0";
- Digit3Display.Text = "0";
- Digit4Display.Text = "0";
- Digit5Display.Text = "0";
- Digit6Display.Text = "0";
- timer.Stop();
- isTimerRunning = false;
- StartPauseIcon.Data = Geometry.Parse(PlayIconData);
- PlayTimerSound();
- }
- });
- }
-
- SoundPlayer player = new SoundPlayer();
- MediaPlayer mediaPlayer = new MediaPlayer();
-
- int hour = 0;
- int minute = 5;
- int second = 0;
-
- DateTime startTime = DateTime.Now;
- DateTime pauseTime = DateTime.Now;
-
- bool isTimerRunning = false;
- bool isPaused = false;
-
- Timer timer = new Timer();
-
- private void InitializeUI()
- {
- UpdateDigitDisplays();
- }
-
- private void ApplyTheme()
- {
- try
- {
- // 应用主题设置
- if (MainWindow.Settings != null)
- {
- ApplyTheme(MainWindow.Settings);
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"应用仿希沃倒计时窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- private void ApplyTheme(Settings settings)
- {
- try
- {
- if (settings.Appearance.Theme == 0) // 浅色主题
- {
- iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
- }
- else if (settings.Appearance.Theme == 1) // 深色主题
- {
- iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
- }
- else // 跟随系统主题
- {
- bool isSystemLight = IsSystemThemeLight();
- if (isSystemLight)
- {
- iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Light);
- }
- else
- {
- iNKORE.UI.WPF.Modern.ThemeManager.SetRequestedTheme(this, iNKORE.UI.WPF.Modern.ElementTheme.Dark);
- }
- }
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogToFile($"应用仿希沃倒计时窗口主题出错: {ex.Message}", LogHelper.LogType.Error);
- }
- }
-
- private bool IsSystemThemeLight()
- {
- var light = false;
- try
- {
- var registryKey = Microsoft.Win32.Registry.CurrentUser;
- var themeKey = registryKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
- if (themeKey != null)
- {
- var value = themeKey.GetValue("AppsUseLightTheme");
- if (value != null)
- {
- light = (int)value == 1;
- }
- themeKey.Close();
- }
- }
- catch
- {
- // 如果读取注册表失败,默认为浅色主题
- light = true;
- }
- return light;
- }
-
- private void UpdateDigitDisplays()
- {
- Digit1Display.Text = (hour / 10).ToString();
- Digit2Display.Text = (hour % 10).ToString();
- Digit3Display.Text = (minute / 10).ToString();
- Digit4Display.Text = (minute % 10).ToString();
- Digit5Display.Text = (second / 10).ToString();
- Digit6Display.Text = (second % 10).ToString();
- }
-
- // 第1位数字(小时十位)
- private void Digit1Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- hour += 10;
- if (hour >= 100) hour = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit1Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- hour -= 10;
- if (hour < 0) hour = 90;
- UpdateDigitDisplays();
- }
-
- // 第2位数字(小时个位)
- private void Digit2Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- hour++;
- if (hour >= 100) hour = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit2Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- hour--;
- if (hour < 0) hour = 99;
- UpdateDigitDisplays();
- }
-
- // 第3位数字(分钟十位)
- private void Digit3Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- minute += 10;
- if (minute >= 60) minute = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit3Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- minute -= 10;
- if (minute < 0) minute = 50;
- UpdateDigitDisplays();
- }
-
- // 第4位数字(分钟个位)
- private void Digit4Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- minute++;
- if (minute >= 60) minute = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit4Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- minute--;
- if (minute < 0) minute = 59;
- UpdateDigitDisplays();
- }
-
- // 第5位数字(秒十位)
- private void Digit5Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- second += 10;
- if (second >= 60) second = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit5Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- second -= 10;
- if (second < 0) second = 50;
- UpdateDigitDisplays();
- }
-
- // 第6位数字(秒个位)
- private void Digit6Plus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- second++;
- if (second >= 60) second = 0;
- UpdateDigitDisplays();
- }
-
- private void Digit6Minus_Click(object sender, RoutedEventArgs e)
- {
- if (isTimerRunning) return;
- second--;
- if (second < 0) second = 59;
- UpdateDigitDisplays();
- }
-
- // 图标数据常量
- private const string PlayIconData = "M6.5 4.00004V20C6.49995 20.178 6.54737 20.3527 6.63738 20.5062C6.72739 20.6597 6.85672 20.7864 7.01202 20.8732C7.16733 20.96 7.34299 21.0038 7.52088 21.0001C7.69878 20.9964 7.87245 20.9453 8.024 20.852L21.024 12.852C21.1696 12.7626 21.2898 12.6373 21.3733 12.4881C21.4567 12.339 21.5005 12.1709 21.5005 12C21.5005 11.8291 21.4567 11.6611 21.3733 11.512C21.2898 11.3628 21.1696 11.2375 21.024 11.148L8.024 3.14804C7.87245 3.0548 7.69878 3.00369 7.52088 2.99997C7.34299 2.99626 7.16733 3.04007 7.01202 3.1269C6.85672 3.21372 6.72739 3.34042 6.63738 3.4939C6.54737 3.64739 6.49995 3.82211 6.5 4.00004Z";
- private const string PauseIconData = "M9.5 4H7.5C6.96957 4 6.46086 4.21071 6.08579 4.58579C5.71071 4.96086 5.5 5.46957 5.5 6V18C5.5 18.5304 5.71071 19.0391 6.08579 19.4142C6.46086 19.7893 6.96957 20 7.5 20H9.5C10.0304 20 10.5391 19.7893 10.9142 19.4142C11.2893 19.0391 11.5 18.5304 11.5 18V6C11.5 5.46957 11.2893 4.96086 10.9142 4.58579C10.5391 4.21071 10.0304 4 9.5 4Z M17.5 4H15.5C14.9696 4 14.4609 4.21071 14.0858 4.58579C13.7107 4.96086 13.5 5.46957 13.5 6V18C13.5 18.5304 13.7107 19.0391 14.0858 19.4142C14.4609 19.7893 14.9696 20 15.5 20H17.5C18.0304 20 18.5391 19.7893 18.9142 19.4142C19.2893 19.0391 19.5 18.5304 19.5 18V6C19.5 5.46957 19.2893 4.96086 18.9142 4.58579C18.5391 4.21071 18.0304 4 17.5 4Z";
-
- private void StartPause_Click(object sender, RoutedEventArgs e)
- {
- if (isPaused && isTimerRunning)
- {
- // 继续计时
- startTime += DateTime.Now - pauseTime;
- StartPauseIcon.Data = Geometry.Parse(PauseIconData);
- isPaused = false;
- timer.Start();
- }
- else if (isTimerRunning)
- {
- // 暂停计时
- pauseTime = DateTime.Now;
- StartPauseIcon.Data = Geometry.Parse(PlayIconData);
- isPaused = true;
- timer.Stop();
- }
- else
- {
- // 开始计时
- if (hour == 0 && minute == 0 && second == 0)
- {
- second = 1;
- UpdateDigitDisplays();
- }
-
- startTime = DateTime.Now;
- StartPauseIcon.Data = Geometry.Parse(PauseIconData);
- isPaused = false;
- isTimerRunning = true;
- timer.Start();
- }
- }
-
- private void Reset_Click(object sender, RoutedEventArgs e)
- {
- if (!isTimerRunning)
- {
- UpdateDigitDisplays();
- }
- else if (isTimerRunning && isPaused)
- {
- UpdateDigitDisplays();
- StartPauseIcon.Data = Geometry.Parse(PlayIconData);
- isTimerRunning = false;
- timer.Stop();
- isPaused = false;
- }
- else
- {
- startTime = DateTime.Now;
- Timer_Elapsed(timer, null);
- }
- }
-
- private void Fullscreen_Click(object sender, RoutedEventArgs e)
- {
- if (WindowState == WindowState.Normal)
- {
- WindowState = WindowState.Maximized;
- }
- else
- {
- WindowState = WindowState.Normal;
- }
- }
-
-
- private void PlayTimerSound()
- {
- try
- {
- double volume = MainWindow.Settings.RandSettings?.TimerVolume ?? 1.0;
- mediaPlayer.Volume = volume;
-
- if (!string.IsNullOrEmpty(MainWindow.Settings.RandSettings?.CustomTimerSoundPath) &&
- System.IO.File.Exists(MainWindow.Settings.RandSettings.CustomTimerSoundPath))
- {
- // 播放自定义铃声
- mediaPlayer.Open(new Uri(MainWindow.Settings.RandSettings.CustomTimerSoundPath));
- }
- else
- {
- // 播放默认铃声
- string tempPath = System.IO.Path.GetTempFileName() + ".wav";
- using (var stream = Properties.Resources.TimerDownNotice)
- {
- using (var fileStream = new System.IO.FileStream(tempPath, System.IO.FileMode.Create))
- {
- stream.CopyTo(fileStream);
- }
- }
- mediaPlayer.Open(new Uri(tempPath));
- }
-
- mediaPlayer.Play();
- }
- catch (Exception ex)
- {
- // 如果播放失败,静默处理
- System.Diagnostics.Debug.WriteLine($"播放计时器铃声失败: {ex.Message}");
- }
- }
-
- private void Window_Loaded(object sender, RoutedEventArgs e)
- {
- // 窗口加载时的初始化
- }
-
- private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
- {
- isTimerRunning = false;
-
- if (MainWindow.Settings != null)
- {
- var mainWindow = Application.Current.MainWindow as MainWindow;
- if (mainWindow != null)
- {
- try
- {
- var currentModeField = mainWindow.GetType().GetField("currentMode",
- System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
- if (currentModeField != null)
- {
- int currentMode = (int)currentModeField.GetValue(mainWindow);
- if (currentMode == 1) // 白板模式
- {
- mainWindow.Topmost = false; // 保持白板模式下的非置顶状态
- }
- else
- {
- mainWindow.Topmost = true; // 其他模式恢复置顶
- }
- }
- }
- catch
- {
- // 如果反射失败,使用默认行为
- mainWindow.Topmost = true;
- }
- }
- }
- }
-
- private void BtnClose_MouseUp(object sender, MouseButtonEventArgs e)
- {
- Close();
- }
-
- private void WindowDragMove(object sender, MouseEventArgs e)
- {
- if (e.LeftButton == MouseButtonState.Pressed)
- DragMove();
- }
- }
-}
diff --git a/Ink Canvas/obj/Debug/net472/InkCanvasForClass.csproj.AssemblyReference.cache b/Ink Canvas/obj/Debug/net472/InkCanvasForClass.csproj.AssemblyReference.cache
deleted file mode 100644
index c8c2dfbd..00000000
Binary files a/Ink Canvas/obj/Debug/net472/InkCanvasForClass.csproj.AssemblyReference.cache and /dev/null differ
diff --git a/README.md b/README.md
index a10bd6da..71ab7b17 100644
--- a/README.md
+++ b/README.md
@@ -114,3 +114,4 @@ GPLv3
## 项目引用
[Alan-CRL/DesktopDrawpadBlocker](https://github.com/Alan-CRL/DesktopDrawpadBlocker)
+[Awesome-Iwb/iwbicons-gallery](https://github.com/awesome-iwb/awesome-iwb/wiki/iwbicons-gallery)「本项目部分图标来自 Awesome Iwb 的 IwbIcons 图标库,由 Douxiba 制作。」
diff --git a/UpdateLog.md b/UpdateLog.md
index cf2fa362..af861394 100644
--- a/UpdateLog.md
+++ b/UpdateLog.md
@@ -98,4 +98,5 @@ ICC CE 1.7.X.X更新日志
97. 改进橡皮系统
98. 新增启动动画
99. 修复仅调色盘状态下浮动栏不居中
-100. 修复希沃白板查杀与思锐希沃启动器导致的重复启动
\ No newline at end of file
+100. 修复希沃白板查杀与思锐希沃启动器导致的重复启动
+101. 新增UIA窗口置顶