@@ -1 +1 @@
|
||||
1.7.11.0
|
||||
1.7.12.0
|
||||
|
||||
+198
-592
File diff suppressed because it is too large
Load Diff
@@ -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.11.4")]
|
||||
[assembly: AssemblyFileVersion("1.7.11.4")]
|
||||
[assembly: AssemblyVersion("1.7.12.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.12.0")]
|
||||
|
||||
@@ -18,6 +18,11 @@ namespace Ink_Canvas.Helpers
|
||||
private readonly object _frameLock = new object();
|
||||
private Dispatcher _dispatcher;
|
||||
|
||||
// 新增属性
|
||||
private int _rotationAngle = 0; // 0=0度,1=90度,2=180度,3=270度
|
||||
private int _resolutionWidth = 640;
|
||||
private int _resolutionHeight = 480;
|
||||
|
||||
public event EventHandler<Bitmap> FrameReceived;
|
||||
public event EventHandler<string> ErrorOccurred;
|
||||
|
||||
@@ -25,6 +30,25 @@ namespace Ink_Canvas.Helpers
|
||||
public List<FilterInfo> AvailableCameras { get; private set; }
|
||||
public FilterInfo CurrentCamera { get; private set; }
|
||||
|
||||
// 新增属性
|
||||
public int RotationAngle
|
||||
{
|
||||
get => _rotationAngle;
|
||||
set => _rotationAngle = Math.Max(0, Math.Min(3, value));
|
||||
}
|
||||
|
||||
public int ResolutionWidth
|
||||
{
|
||||
get => _resolutionWidth;
|
||||
set => _resolutionWidth = Math.Max(320, Math.Min(1920, value));
|
||||
}
|
||||
|
||||
public int ResolutionHeight
|
||||
{
|
||||
get => _resolutionHeight;
|
||||
set => _resolutionHeight = Math.Max(240, Math.Min(1080, value));
|
||||
}
|
||||
|
||||
public CameraService()
|
||||
{
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
@@ -32,6 +56,16 @@ namespace Ink_Canvas.Helpers
|
||||
RefreshCameraList();
|
||||
}
|
||||
|
||||
public CameraService(int rotationAngle, int resolutionWidth, int resolutionHeight)
|
||||
{
|
||||
_dispatcher = Dispatcher.CurrentDispatcher;
|
||||
AvailableCameras = new List<FilterInfo>();
|
||||
_rotationAngle = rotationAngle;
|
||||
_resolutionWidth = resolutionWidth;
|
||||
_resolutionHeight = resolutionHeight;
|
||||
RefreshCameraList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新可用摄像头列表
|
||||
/// </summary>
|
||||
@@ -242,14 +276,16 @@ namespace Ink_Canvas.Helpers
|
||||
var width = sourceFrame.Width;
|
||||
var height = sourceFrame.Height;
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
_currentFrame = new Bitmap(width, height, PixelFormat.Format24bppRgb);
|
||||
using (var graphics = Graphics.FromImage(_currentFrame))
|
||||
{
|
||||
graphics.DrawImage(sourceFrame, 0, 0);
|
||||
}
|
||||
}
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
// 应用旋转
|
||||
Bitmap rotatedFrame = ApplyRotation(sourceFrame);
|
||||
|
||||
// 应用分辨率调整
|
||||
_currentFrame = ResizeImage(rotatedFrame, _resolutionWidth, _resolutionHeight);
|
||||
|
||||
rotatedFrame?.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentFrame = null;
|
||||
@@ -300,6 +336,46 @@ namespace Ink_Canvas.Helpers
|
||||
return AvailableCameras.Count > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用旋转到图像
|
||||
/// </summary>
|
||||
private Bitmap ApplyRotation(Bitmap source)
|
||||
{
|
||||
if (_rotationAngle == 0)
|
||||
return new Bitmap(source);
|
||||
|
||||
var rotationType = RotateFlipType.RotateNoneFlipNone;
|
||||
switch (_rotationAngle)
|
||||
{
|
||||
case 1: rotationType = RotateFlipType.Rotate90FlipNone; break;
|
||||
case 2: rotationType = RotateFlipType.Rotate180FlipNone; break;
|
||||
case 3: rotationType = RotateFlipType.Rotate270FlipNone; break;
|
||||
}
|
||||
|
||||
var rotated = new Bitmap(source);
|
||||
rotated.RotateFlip(rotationType);
|
||||
return rotated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调整图像大小
|
||||
/// </summary>
|
||||
private Bitmap ResizeImage(Bitmap source, int width, int height)
|
||||
{
|
||||
if (source.Width == width && source.Height == height)
|
||||
return new Bitmap(source);
|
||||
|
||||
var resized = new Bitmap(width, height, PixelFormat.Format24bppRgb);
|
||||
using (var graphics = Graphics.FromImage(resized))
|
||||
{
|
||||
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
|
||||
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
|
||||
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
|
||||
graphics.DrawImage(source, 0, 0, width, height);
|
||||
}
|
||||
return resized;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopPreview();
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Ink_Canvas.Helpers
|
||||
private const string IpcEventName = "InkCanvasFileAssociationEvent";
|
||||
private const string IpcFilePrefix = "InkCanvasFileAssociation_";
|
||||
private const string IpcBoardModePrefix = "InkCanvasBoardMode_";
|
||||
private const string IpcShowModePrefix = "InkCanvasShowMode_";
|
||||
private const int IpcTimeout = 5000; // 5秒超时
|
||||
|
||||
/// <summary>
|
||||
@@ -310,6 +311,56 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过IPC将展开浮动栏命令发送给已运行的实例
|
||||
/// </summary>
|
||||
/// <returns>是否成功发送</returns>
|
||||
public static bool TrySendShowModeCommandToExistingInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogHelper.WriteLogToFile("尝试通过IPC发送展开浮动栏命令给已运行实例", LogHelper.LogType.Event);
|
||||
|
||||
// 创建IPC文件
|
||||
string tempDir = Path.GetTempPath();
|
||||
string ipcFileName = IpcShowModePrefix + Guid.NewGuid().ToString("N") + ".tmp";
|
||||
string ipcFilePath = Path.Combine(tempDir, ipcFileName);
|
||||
|
||||
// 写入展开浮动栏命令到IPC文件
|
||||
File.WriteAllText(ipcFilePath, "SHOW_MODE", Encoding.UTF8);
|
||||
|
||||
// 创建事件通知已运行实例
|
||||
using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName))
|
||||
{
|
||||
ipcEvent.Set();
|
||||
}
|
||||
|
||||
// 等待一段时间让已运行实例处理命令
|
||||
Thread.Sleep(1000);
|
||||
|
||||
// 清理IPC文件
|
||||
try
|
||||
{
|
||||
if (File.Exists(ipcFilePath))
|
||||
{
|
||||
File.Delete(ipcFilePath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("IPC展开浮动栏命令发送完成", LogHelper.LogType.Event);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"通过IPC发送展开浮动栏命令失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动IPC监听器,等待其他实例发送文件路径
|
||||
/// </summary>
|
||||
@@ -470,6 +521,61 @@ namespace Ink_Canvas.Helpers
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// 处理展开浮动栏命令IPC文件
|
||||
string[] showModeFiles = Directory.GetFiles(tempDir, IpcShowModePrefix + "*.tmp");
|
||||
foreach (string ipcFile in showModeFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取命令内容
|
||||
string command = File.ReadAllText(ipcFile, Encoding.UTF8);
|
||||
|
||||
if (command == "SHOW_MODE")
|
||||
{
|
||||
LogHelper.WriteLogToFile("IPC接收到展开浮动栏命令", LogHelper.LogType.Event);
|
||||
|
||||
// 在UI线程中处理展开浮动栏
|
||||
Application.Current.Dispatcher.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取主窗口并展开浮动栏
|
||||
if (Application.Current.MainWindow is MainWindow mainWindow)
|
||||
{
|
||||
// 如果当前处于收纳模式,则展开浮动栏
|
||||
if (mainWindow.isFloatingBarFolded)
|
||||
{
|
||||
await mainWindow.UnFoldFloatingBar(new object());
|
||||
}
|
||||
mainWindow.ShowNotification("已退出收纳模式并恢复浮动栏");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"IPC处理展开浮动栏失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// 删除IPC文件
|
||||
File.Delete(ipcFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理展开浮动栏IPC文件失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
|
||||
// 尝试删除损坏的IPC文件
|
||||
try
|
||||
{
|
||||
if (File.Exists(ipcFile))
|
||||
{
|
||||
File.Delete(ipcFile);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -55,6 +55,27 @@ namespace Ink_Canvas.Helpers
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetWindowRect(IntPtr hWnd, out ForegroundWindowInfo.RECT lpRect);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("dwmapi.dll")]
|
||||
private static extern int DwmGetWindowAttribute(IntPtr hWnd, int dwAttribute, out ForegroundWindowInfo.RECT pvAttribute, int cbAttribute);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetDC(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool BringWindowToTop(IntPtr hWnd);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
|
||||
|
||||
@@ -68,9 +89,11 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
private const int SW_HIDE = 0;
|
||||
private const int SW_SHOW = 1;
|
||||
private const int SW_SHOWNORMAL = 1;
|
||||
private const int SW_MINIMIZE = 6;
|
||||
private const int SW_RESTORE = 9;
|
||||
|
||||
private const int GWL_STYLE = -16;
|
||||
private const int GWL_EXSTYLE = -20;
|
||||
private const uint WS_EX_TOOLWINDOW = 0x00000080;
|
||||
private const uint WS_EX_APPWINDOW = 0x00040000;
|
||||
@@ -79,10 +102,17 @@ namespace Ink_Canvas.Helpers
|
||||
private const uint SWP_NOSIZE = 0x0001;
|
||||
private const uint SWP_NOZORDER = 0x0004;
|
||||
private const uint SWP_HIDEWINDOW = 0x0080;
|
||||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||||
|
||||
private const uint PROCESS_QUERY_INFORMATION = 0x0400;
|
||||
private const uint PROCESS_VM_READ = 0x0010;
|
||||
|
||||
private const uint WM_CLOSE = 0x0010;
|
||||
private const int DWMWA_EXTENDED_FRAME_BOUNDS = 9;
|
||||
private const int LOGPIXELSX = 88;
|
||||
private const int LOGPIXELSY = 90;
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 拦截规则定义
|
||||
@@ -212,6 +242,19 @@ namespace Ink_Canvas.Helpers
|
||||
public string Description { get; set; }
|
||||
public InterceptType? ParentType { get; set; }
|
||||
public List<InterceptType> ChildTypes { get; set; } = new List<InterceptType>();
|
||||
|
||||
// 新增的精确匹配字段
|
||||
public bool HasWindowStyle { get; set; }
|
||||
public uint WindowStyle { get; set; }
|
||||
public bool HasWindowSize { get; set; }
|
||||
public int WindowWidth { get; set; }
|
||||
public int WindowHeight { get; set; }
|
||||
public bool ExactTitleMatch { get; set; } = false;
|
||||
public bool ExactClassNameMatch { get; set; } = false;
|
||||
|
||||
// 运行时状态字段
|
||||
public bool foundHwnd { get; set; } = false;
|
||||
public IntPtr outHwnd { get; set; } = IntPtr.Zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -225,13 +268,9 @@ namespace Ink_Canvas.Helpers
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
|
||||
// 性能优化字段
|
||||
private readonly Dictionary<IntPtr, DateTime> _lastScanTime = new Dictionary<IntPtr, DateTime>();
|
||||
private readonly HashSet<IntPtr> _knownWindows = new HashSet<IntPtr>();
|
||||
private readonly Dictionary<string, DateTime> _processLastScanTime = new Dictionary<string, DateTime>();
|
||||
// 简化的性能统计
|
||||
private int _consecutiveEmptyScans = 0;
|
||||
private DateTime _lastSuccessfulScan = DateTime.Now;
|
||||
private readonly object _scanLock = new object();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -275,7 +314,14 @@ namespace Ink_Canvas.Helpers
|
||||
ClassNamePattern = "HwndWrapper[EasiNote.exe;;",
|
||||
IsEnabled = true,
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃白板3 桌面悬浮窗"
|
||||
Description = "希沃白板3 桌面悬浮窗",
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 370081792,
|
||||
HasWindowSize = true,
|
||||
WindowWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
|
||||
WindowHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
|
||||
ExactTitleMatch = true,
|
||||
ExactClassNameMatch = false
|
||||
};
|
||||
|
||||
// 希沃白板5 桌面悬浮窗
|
||||
@@ -287,7 +333,14 @@ namespace Ink_Canvas.Helpers
|
||||
ClassNamePattern = "HwndWrapper[EasiNote;;",
|
||||
IsEnabled = true,
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃白板5 桌面悬浮窗"
|
||||
Description = "希沃白板5 桌面悬浮窗",
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 369623040,
|
||||
HasWindowSize = true,
|
||||
WindowWidth = 550,
|
||||
WindowHeight = 200,
|
||||
ExactTitleMatch = false,
|
||||
ExactClassNameMatch = false
|
||||
};
|
||||
|
||||
// 希沃白板5C 桌面悬浮窗
|
||||
@@ -299,7 +352,14 @@ namespace Ink_Canvas.Helpers
|
||||
ClassNamePattern = "HwndWrapper[EasiNote5C;;",
|
||||
IsEnabled = true,
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃白板5C 桌面悬浮窗"
|
||||
Description = "希沃白板5C 桌面悬浮窗",
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 369623040,
|
||||
HasWindowSize = true,
|
||||
WindowWidth = 550,
|
||||
WindowHeight = 200,
|
||||
ExactTitleMatch = false,
|
||||
ExactClassNameMatch = false
|
||||
};
|
||||
|
||||
// 希沃品课教师端 桌面悬浮窗(父规则)
|
||||
@@ -313,7 +373,11 @@ namespace Ink_Canvas.Helpers
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃品课教师端 桌面悬浮窗",
|
||||
ParentType = null,
|
||||
ChildTypes = new List<InterceptType> { InterceptType.SeewoPincoDrawingFloating, InterceptType.SeewoPincoBoardService }
|
||||
ChildTypes = new List<InterceptType> { InterceptType.SeewoPincoDrawingFloating, InterceptType.SeewoPincoBoardService },
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 0x16CF0000,
|
||||
ExactTitleMatch = true,
|
||||
ExactClassNameMatch = true
|
||||
};
|
||||
|
||||
// 希沃品课教师端 画笔悬浮窗(子规则)
|
||||
@@ -327,7 +391,11 @@ namespace Ink_Canvas.Helpers
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃品课教师端 画笔悬浮窗(包括PPT控件)",
|
||||
ParentType = InterceptType.SeewoPincoSideBarFloating,
|
||||
ChildTypes = new List<InterceptType>()
|
||||
ChildTypes = new List<InterceptType>(),
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 335675392,
|
||||
ExactTitleMatch = true,
|
||||
ExactClassNameMatch = true
|
||||
};
|
||||
|
||||
// 希沃品课教师端 桌面画板(子规则)
|
||||
@@ -341,7 +409,14 @@ namespace Ink_Canvas.Helpers
|
||||
RequiresAdmin = false,
|
||||
Description = "希沃品课教师端 桌面画板",
|
||||
ParentType = InterceptType.SeewoPincoSideBarFloating,
|
||||
ChildTypes = new List<InterceptType>()
|
||||
ChildTypes = new List<InterceptType>(),
|
||||
HasWindowStyle = true,
|
||||
WindowStyle = 369623040,
|
||||
HasWindowSize = true,
|
||||
WindowWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
|
||||
WindowHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height,
|
||||
ExactTitleMatch = false,
|
||||
ExactClassNameMatch = false
|
||||
};
|
||||
|
||||
// 希沃PPT小工具
|
||||
@@ -665,6 +740,12 @@ namespace Ink_Canvas.Helpers
|
||||
var rule = _interceptRules[type];
|
||||
rule.IsEnabled = enabled;
|
||||
|
||||
// 如果规则被禁用,恢复相关的被拦截窗口
|
||||
if (!enabled)
|
||||
{
|
||||
RestoreWindowsByType(type);
|
||||
}
|
||||
|
||||
// 如果是父规则被禁用,则禁用所有子规则
|
||||
if (!enabled && rule.ChildTypes.Count > 0)
|
||||
{
|
||||
@@ -673,6 +754,7 @@ namespace Ink_Canvas.Helpers
|
||||
if (_interceptRules.ContainsKey(childType))
|
||||
{
|
||||
_interceptRules[childType].IsEnabled = false;
|
||||
RestoreWindowsByType(childType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -726,6 +808,14 @@ namespace Ink_Canvas.Helpers
|
||||
return new Dictionary<InterceptType, InterceptRule>(_interceptRules);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前被拦截的窗口数量
|
||||
/// </summary>
|
||||
public int GetInterceptedWindowsCount()
|
||||
{
|
||||
return _interceptedWindows.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动扫描一次
|
||||
/// </summary>
|
||||
@@ -740,10 +830,41 @@ namespace Ink_Canvas.Helpers
|
||||
public void RestoreAllWindows()
|
||||
{
|
||||
var windowsToRestore = new List<IntPtr>(_interceptedWindows.Keys);
|
||||
var restoredCount = 0;
|
||||
|
||||
foreach (var hWnd in windowsToRestore)
|
||||
{
|
||||
RestoreWindow(hWnd);
|
||||
if (RestoreWindow(hWnd))
|
||||
{
|
||||
restoredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复指定类型的被拦截窗口
|
||||
/// </summary>
|
||||
public void RestoreWindowsByType(InterceptType type)
|
||||
{
|
||||
var windowsToRestore = new List<IntPtr>();
|
||||
foreach (var kvp in _interceptedWindows)
|
||||
{
|
||||
if (kvp.Value == type)
|
||||
{
|
||||
windowsToRestore.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
var restoredCount = 0;
|
||||
foreach (var hWnd in windowsToRestore)
|
||||
{
|
||||
if (RestoreWindow(hWnd))
|
||||
{
|
||||
restoredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -753,15 +874,29 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
if (!_interceptedWindows.ContainsKey(hWnd)) return false;
|
||||
|
||||
var interceptType = _interceptedWindows[hWnd];
|
||||
|
||||
if (IsWindow(hWnd))
|
||||
{
|
||||
// 使用多种方法确保窗口恢复显示
|
||||
ShowWindow(hWnd, SW_RESTORE);
|
||||
ShowWindow(hWnd, SW_SHOW);
|
||||
ShowWindow(hWnd, SW_SHOWNORMAL);
|
||||
|
||||
// 将窗口置于前台并显示
|
||||
SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
|
||||
|
||||
// 强制将窗口带到前台
|
||||
BringWindowToTop(hWnd);
|
||||
SetForegroundWindow(hWnd);
|
||||
|
||||
_interceptedWindows.Remove(hWnd);
|
||||
|
||||
WindowRestored?.Invoke(this, new WindowRestoredEventArgs
|
||||
{
|
||||
WindowHandle = hWnd,
|
||||
InterceptType = _interceptedWindows[hWnd]
|
||||
InterceptType = interceptType
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -775,187 +910,103 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
#region 私有方法
|
||||
|
||||
/// <summary>
|
||||
/// 清理无效的窗口句柄
|
||||
/// </summary>
|
||||
private void CleanupInvalidWindows()
|
||||
{
|
||||
var invalidWindows = new List<IntPtr>();
|
||||
|
||||
foreach (var kvp in _interceptedWindows)
|
||||
{
|
||||
var hWnd = kvp.Key;
|
||||
if (!IsWindow(hWnd))
|
||||
{
|
||||
invalidWindows.Add(hWnd);
|
||||
}
|
||||
}
|
||||
foreach (var hWnd in invalidWindows)
|
||||
{
|
||||
_interceptedWindows.Remove(hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScanForWindows(object state)
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
|
||||
lock (_scanLock)
|
||||
try
|
||||
{
|
||||
try
|
||||
// 简化的扫描逻辑
|
||||
var interceptedCount = 0;
|
||||
CleanupInvalidWindows();
|
||||
|
||||
// 重置所有规则的发现状态
|
||||
foreach (var rule in _interceptRules.Values)
|
||||
{
|
||||
var scanStartTime = DateTime.Now;
|
||||
var windowsFound = 0;
|
||||
var windowsIntercepted = 0;
|
||||
|
||||
// 清理过期的缓存
|
||||
CleanupExpiredCache();
|
||||
|
||||
// 使用优化的扫描策略
|
||||
if (_consecutiveEmptyScans > 3)
|
||||
if (rule.IsEnabled)
|
||||
{
|
||||
// 如果连续多次扫描没有发现新窗口,使用快速扫描模式
|
||||
PerformQuickScan(ref windowsFound, ref windowsIntercepted);
|
||||
rule.foundHwnd = false;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
// 枚举所有窗口
|
||||
EnumWindows(EnumWindowsCallback, IntPtr.Zero);
|
||||
|
||||
// 处理找到的窗口
|
||||
foreach (var rule in _interceptRules.Values)
|
||||
{
|
||||
if (rule.IsEnabled && rule.foundHwnd && rule.outHwnd != IntPtr.Zero)
|
||||
{
|
||||
// 正常扫描模式
|
||||
PerformFullScan(ref windowsFound, ref windowsIntercepted);
|
||||
}
|
||||
|
||||
// 更新扫描统计
|
||||
UpdateScanStatistics(windowsFound, windowsIntercepted, scanStartTime);
|
||||
|
||||
// 动态调整扫描间隔
|
||||
AdjustScanInterval();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误但不中断扫描
|
||||
LogHelper.WriteLogToFile($"扫描窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
_consecutiveEmptyScans++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行快速扫描 - 只检查已知进程
|
||||
/// </summary>
|
||||
private void PerformQuickScan(ref int windowsFound, ref int windowsIntercepted)
|
||||
{
|
||||
var targetProcesses = new HashSet<string>();
|
||||
var scanData = new ScanData { WindowsFound = 0, WindowsIntercepted = 0 };
|
||||
|
||||
// 收集所有启用的规则对应的进程名
|
||||
foreach (var rule in _interceptRules.Values)
|
||||
{
|
||||
if (rule.IsEnabled && !string.IsNullOrEmpty(rule.ProcessName))
|
||||
{
|
||||
targetProcesses.Add(rule.ProcessName.ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
// 只扫描目标进程的窗口
|
||||
foreach (var processName in targetProcesses)
|
||||
{
|
||||
try
|
||||
{
|
||||
var processes = Process.GetProcessesByName(processName);
|
||||
foreach (var process in processes)
|
||||
{
|
||||
if (process.MainWindowHandle != IntPtr.Zero)
|
||||
bool shouldIntercept = !_interceptedWindows.ContainsKey(rule.outHwnd) ||
|
||||
(_interceptedWindows.ContainsKey(rule.outHwnd) && IsWindowVisible(rule.outHwnd));
|
||||
|
||||
if (shouldIntercept)
|
||||
{
|
||||
ProcessWindow(process.MainWindowHandle, scanData);
|
||||
InterceptWindow(rule.outHwnd, rule);
|
||||
interceptedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
// 更新统计
|
||||
if (interceptedCount == 0)
|
||||
{
|
||||
// 忽略进程访问错误
|
||||
_consecutiveEmptyScans++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_consecutiveEmptyScans = 0;
|
||||
_lastSuccessfulScan = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
windowsFound = scanData.WindowsFound;
|
||||
windowsIntercepted = scanData.WindowsIntercepted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行完整扫描
|
||||
/// </summary>
|
||||
private void PerformFullScan(ref int windowsFound, ref int windowsIntercepted)
|
||||
{
|
||||
var scanData = new ScanData { WindowsFound = 0, WindowsIntercepted = 0 };
|
||||
|
||||
EnumWindows((hWnd, lParam) =>
|
||||
catch (Exception ex)
|
||||
{
|
||||
ProcessWindow(hWnd, scanData);
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
|
||||
windowsFound = scanData.WindowsFound;
|
||||
windowsIntercepted = scanData.WindowsIntercepted;
|
||||
LogHelper.WriteLogToFile($"扫描窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
_consecutiveEmptyScans++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理单个窗口
|
||||
/// </summary>
|
||||
private bool ProcessWindow(IntPtr hWnd, ScanData scanData)
|
||||
|
||||
private bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 递归枚举子窗口
|
||||
EnumChildWindows(hWnd, EnumWindowsCallback, lParam);
|
||||
|
||||
// 基本检查
|
||||
if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) return true;
|
||||
|
||||
// 检查是否已经被拦截
|
||||
if (_interceptedWindows.ContainsKey(hWnd)) return true;
|
||||
|
||||
// 检查缓存,避免重复处理
|
||||
if (_knownWindows.Contains(hWnd))
|
||||
{
|
||||
var lastScan = _lastScanTime.ContainsKey(hWnd) ? _lastScanTime[hWnd] : DateTime.MinValue;
|
||||
if (DateTime.Now - lastScan < TimeSpan.FromSeconds(30)) // 30秒内不重复处理
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
scanData.WindowsFound++;
|
||||
_knownWindows.Add(hWnd);
|
||||
_lastScanTime[hWnd] = DateTime.Now;
|
||||
|
||||
// 获取窗口信息
|
||||
var windowInfo = GetWindowInfo(hWnd);
|
||||
if (windowInfo == null) return true;
|
||||
|
||||
// 检查进程缓存
|
||||
if (_processLastScanTime.ContainsKey(windowInfo.ProcessName))
|
||||
{
|
||||
var lastProcessScan = _processLastScanTime[windowInfo.ProcessName];
|
||||
if (DateTime.Now - lastProcessScan < TimeSpan.FromSeconds(10)) // 10秒内不重复扫描同一进程
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_processLastScanTime[windowInfo.ProcessName] = DateTime.Now;
|
||||
|
||||
// 检查窗口样式,过滤掉系统窗口和主窗口
|
||||
var exStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
|
||||
var style = GetWindowLong(hWnd, -16); // GWL_STYLE
|
||||
|
||||
// 跳过工具窗口
|
||||
if ((exStyle & WS_EX_TOOLWINDOW) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 跳过主窗口(有标题栏和系统菜单的窗口)
|
||||
const uint WS_CAPTION = 0x00C00000;
|
||||
const uint WS_SYSMENU = 0x00080000;
|
||||
if ((style & WS_CAPTION) != 0 && (style & WS_SYSMENU) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查窗口大小,跳过过大的窗口
|
||||
var rect = new ForegroundWindowInfo.RECT();
|
||||
GetWindowRect(hWnd, out rect);
|
||||
var width = rect.Right - rect.Left;
|
||||
var height = rect.Bottom - rect.Top;
|
||||
|
||||
if (width > 600 || height > 400)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查是否匹配拦截规则
|
||||
// 检查每个启用的规则
|
||||
foreach (var rule in _interceptRules.Values)
|
||||
{
|
||||
if (!rule.IsEnabled) continue;
|
||||
if (!rule.IsEnabled || rule.foundHwnd) continue;
|
||||
|
||||
if (MatchesRule(windowInfo, rule))
|
||||
if (MatchesRulePrecise(hWnd, rule))
|
||||
{
|
||||
InterceptWindow(hWnd, rule);
|
||||
scanData.WindowsIntercepted++;
|
||||
break;
|
||||
rule.outHwnd = hWnd;
|
||||
rule.foundHwnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,107 +1014,11 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"处理窗口时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile($"枚举窗口回调错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理过期缓存
|
||||
/// </summary>
|
||||
private void CleanupExpiredCache()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var expiredWindows = new List<IntPtr>();
|
||||
var expiredProcesses = new List<string>();
|
||||
|
||||
// 清理窗口缓存
|
||||
foreach (var kvp in _lastScanTime)
|
||||
{
|
||||
if (now - kvp.Value > TimeSpan.FromMinutes(5))
|
||||
{
|
||||
expiredWindows.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var hWnd in expiredWindows)
|
||||
{
|
||||
_lastScanTime.Remove(hWnd);
|
||||
_knownWindows.Remove(hWnd);
|
||||
}
|
||||
|
||||
// 清理进程缓存
|
||||
foreach (var kvp in _processLastScanTime)
|
||||
{
|
||||
if (now - kvp.Value > TimeSpan.FromMinutes(2))
|
||||
{
|
||||
expiredProcesses.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var processName in expiredProcesses)
|
||||
{
|
||||
_processLastScanTime.Remove(processName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新扫描统计
|
||||
/// </summary>
|
||||
private void UpdateScanStatistics(int windowsFound, int windowsIntercepted, DateTime scanStartTime)
|
||||
{
|
||||
var scanDuration = DateTime.Now - scanStartTime;
|
||||
|
||||
if (windowsFound == 0)
|
||||
{
|
||||
_consecutiveEmptyScans++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_consecutiveEmptyScans = 0;
|
||||
_lastSuccessfulScan = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动态调整扫描间隔
|
||||
/// </summary>
|
||||
private void AdjustScanInterval()
|
||||
{
|
||||
if (!_isRunning) return;
|
||||
|
||||
int newInterval;
|
||||
if (_consecutiveEmptyScans > 5)
|
||||
{
|
||||
// 连续多次空扫描,增加间隔到30秒
|
||||
newInterval = 30000;
|
||||
}
|
||||
else if (_consecutiveEmptyScans > 3)
|
||||
{
|
||||
// 连续多次空扫描,增加间隔到15秒
|
||||
newInterval = 15000;
|
||||
}
|
||||
else if (_consecutiveEmptyScans > 1)
|
||||
{
|
||||
// 连续空扫描,增加间隔到10秒
|
||||
newInterval = 10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 正常扫描,使用5秒间隔
|
||||
newInterval = 5000;
|
||||
}
|
||||
|
||||
// 更新定时器间隔
|
||||
_scanTimer.Change(newInterval, newInterval);
|
||||
}
|
||||
|
||||
private bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
|
||||
{
|
||||
// 这个方法现在由ProcessWindow替代,保留用于兼容性
|
||||
return true;
|
||||
}
|
||||
|
||||
private WindowInfo GetWindowInfo(IntPtr hWnd)
|
||||
{
|
||||
try
|
||||
@@ -1100,62 +1055,119 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private bool MatchesRule(WindowInfo windowInfo, InterceptRule rule)
|
||||
/// <summary>
|
||||
/// 精确匹配规则
|
||||
/// </summary>
|
||||
private bool MatchesRulePrecise(IntPtr hWnd, InterceptRule rule)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查进程名(如果指定了进程名)
|
||||
if (!string.IsNullOrEmpty(rule.ProcessName))
|
||||
{
|
||||
if (!windowInfo.ProcessName.ToLower().Contains(rule.ProcessName.ToLower()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查窗口标题(如果指定了模式)
|
||||
if (!string.IsNullOrEmpty(rule.WindowTitlePattern))
|
||||
{
|
||||
if (!windowInfo.WindowTitle.ToLower().Contains(rule.WindowTitlePattern.ToLower()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查类名(如果指定了模式)
|
||||
// 检查类名
|
||||
if (!string.IsNullOrEmpty(rule.ClassNamePattern))
|
||||
{
|
||||
if (!windowInfo.ClassName.ToLower().Contains(rule.ClassNamePattern.ToLower()))
|
||||
var className = new StringBuilder(256);
|
||||
GetClassName(hWnd, className, className.Capacity);
|
||||
var classNameStr = className.ToString();
|
||||
|
||||
if (rule.ExactClassNameMatch)
|
||||
{
|
||||
if (!classNameStr.Equals(rule.ClassNamePattern, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!classNameStr.Contains(rule.ClassNamePattern))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查窗口标题
|
||||
if (!string.IsNullOrEmpty(rule.WindowTitlePattern))
|
||||
{
|
||||
var windowTitle = new StringBuilder(256);
|
||||
GetWindowText(hWnd, windowTitle, windowTitle.Capacity);
|
||||
var titleStr = windowTitle.ToString();
|
||||
|
||||
if (rule.ExactTitleMatch)
|
||||
{
|
||||
if (!titleStr.Equals(rule.WindowTitlePattern, StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!titleStr.Contains(rule.WindowTitlePattern))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查窗口样式
|
||||
if (rule.HasWindowStyle)
|
||||
{
|
||||
var style = GetWindowLong(hWnd, GWL_STYLE);
|
||||
if (style != rule.WindowStyle)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查窗口尺寸
|
||||
if (rule.HasWindowSize)
|
||||
{
|
||||
var rect = new ForegroundWindowInfo.RECT();
|
||||
if (DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(rect)) == 0)
|
||||
{
|
||||
var width = rect.Right - rect.Left;
|
||||
var height = rect.Bottom - rect.Top;
|
||||
|
||||
// 检查精确匹配
|
||||
if (rule.WindowWidth == width && rule.WindowHeight == height)
|
||||
return true;
|
||||
|
||||
// 检查缩放匹配
|
||||
var hdc = GetDC(IntPtr.Zero);
|
||||
var horizontalDPI = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||
var verticalDPI = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||
ReleaseDC(IntPtr.Zero, hdc);
|
||||
|
||||
var scale = (horizontalDPI + verticalDPI) / 2.0f / 96.0f;
|
||||
var scaledWidth = (int)(rule.WindowWidth * scale);
|
||||
var scaledHeight = (int)(rule.WindowHeight * scale);
|
||||
|
||||
if (Math.Abs(scaledWidth - width) <= 1 && Math.Abs(scaledHeight - height) <= 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有检查都通过,就认为是目标窗口
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"匹配规则时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
LogHelper.WriteLogToFile($"精确匹配规则时发生错误: {ex.Message}", LogHelper.LogType.Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool MatchesRule(WindowInfo windowInfo, InterceptRule rule)
|
||||
{
|
||||
return MatchesRulePrecise(windowInfo.Handle, rule);
|
||||
}
|
||||
|
||||
private void InterceptWindow(IntPtr hWnd, InterceptRule rule)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用多种方法隐藏窗口
|
||||
// 方法1:移动到屏幕外
|
||||
SetWindowPos(hWnd, IntPtr.Zero, -2000, -2000, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW);
|
||||
|
||||
// 方法2:最小化窗口
|
||||
ShowWindow(hWnd, SW_MINIMIZE);
|
||||
|
||||
// 方法3:隐藏窗口
|
||||
if (!IsWindow(hWnd) || !IsWindowVisible(hWnd))
|
||||
{
|
||||
if (_interceptedWindows.ContainsKey(hWnd))
|
||||
{
|
||||
_interceptedWindows.Remove(hWnd);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接隐藏窗口,不发送关闭消息
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
|
||||
|
||||
// 记录拦截的窗口
|
||||
_interceptedWindows[hWnd] = rule.Type;
|
||||
|
||||
@@ -1167,6 +1179,7 @@ namespace Ink_Canvas.Helpers
|
||||
Rule = rule,
|
||||
WindowTitle = GetWindowTitle(hWnd)
|
||||
});
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1243,12 +1256,6 @@ namespace Ink_Canvas.Helpers
|
||||
public Process Process { get; set; }
|
||||
}
|
||||
|
||||
private class ScanData
|
||||
{
|
||||
public int WindowsFound { get; set; }
|
||||
public int WindowsIntercepted { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件参数类
|
||||
|
||||
@@ -575,6 +575,11 @@
|
||||
<Resource Include="Resources\PresentationExample\sidebar-dark.png" />
|
||||
<Resource Include="Resources\PresentationExample\sidebar-white.png" />
|
||||
<Resource Include="Resources\PresentationExample\toolbar.png" />
|
||||
<Resource Include="Resources\Startup-animation\ICC Spring.png" />
|
||||
<Resource Include="Resources\Startup-animation\ICC Summer.png" />
|
||||
<Resource Include="Resources\Startup-animation\ICC Autumn.png" />
|
||||
<Resource Include="Resources\Startup-animation\ICC Winter.png" />
|
||||
<Resource Include="Resources\Startup-animation\ICC Horse.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
|
||||
@@ -1040,6 +1040,28 @@
|
||||
<ComboBoxItem Content="跟随系统" FontFamily="Microsoft YaHei UI" />
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启用启动动画" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ui:ToggleSwitch OnContent="" OffContent="" Name="ToggleSwitchEnableSplashScreen"
|
||||
IsOn="False" FontFamily="Microsoft YaHei UI" FontWeight="Bold"
|
||||
Toggled="ToggleSwitchEnableSplashScreen_Toggled" />
|
||||
</ui:SimpleStackPanel>
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
<TextBlock Foreground="#fafafa" Text="启动动画样式" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
<ComboBox Name="ComboBoxSplashScreenStyle" FontFamily="Microsoft YaHei UI"
|
||||
SelectedIndex="1" Width="150"
|
||||
SelectionChanged="ComboBoxSplashScreenStyle_SelectionChanged">
|
||||
<ComboBoxItem Content="随机" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="跟随四季" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="春季" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="夏季" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="秋季" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="冬季" FontFamily="Microsoft YaHei UI" />
|
||||
<ComboBoxItem Content="马年限定" FontFamily="Microsoft YaHei UI" />
|
||||
</ComboBox>
|
||||
</ui:SimpleStackPanel>
|
||||
<Line HorizontalAlignment="Center" X1="0" Y1="0" X2="400" Y2="0"
|
||||
Stroke="#3f3f46" StrokeThickness="1" Margin="0,4,0,4" />
|
||||
<ui:SimpleStackPanel Orientation="Horizontal" HorizontalAlignment="Left">
|
||||
@@ -3666,9 +3688,6 @@
|
||||
<TextBlock
|
||||
Text="© 2025 CJK_mkp 版权所有" FontWeight="Bold"
|
||||
TextWrapping="Wrap" Foreground="White" />
|
||||
<TextBlock
|
||||
Text="© 2022-2025 HarkoTek Studio 提供技术支持"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
<TextBlock
|
||||
Text="We love Open-Source forever!" FontWeight="Bold"
|
||||
TextWrapping="Wrap" Foreground="#a1a1aa" />
|
||||
|
||||
@@ -511,8 +511,8 @@ namespace Ink_Canvas
|
||||
}
|
||||
|
||||
SystemEvents.DisplaySettingsChanged += SystemEventsOnDisplaySettingsChanged;
|
||||
// 自动收纳到侧边栏(若通过 --board 进入白板模式则跳过收纳)
|
||||
if (Settings.Startup.IsFoldAtStartup && !App.StartWithBoardMode)
|
||||
// 自动收纳到侧边栏(若通过 --board 进入白板模式或 --show 参数则跳过收纳)
|
||||
if (Settings.Startup.IsFoldAtStartup && !App.StartWithBoardMode && !App.StartWithShowMode)
|
||||
{
|
||||
FoldFloatingBar_MouseUp(new object(), null);
|
||||
}
|
||||
@@ -523,8 +523,6 @@ namespace Ink_Canvas
|
||||
else
|
||||
RadioCrashNoAction.IsChecked = true;
|
||||
|
||||
|
||||
|
||||
// 如果当前不是黑板模式,则切换到黑板模式
|
||||
if (currentMode == 0)
|
||||
{
|
||||
@@ -584,6 +582,21 @@ namespace Ink_Canvas
|
||||
SwitchToBoardMode();
|
||||
}), DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
// 检查是否通过--show参数启动,如果是则确保退出收纳模式并恢复浮动栏
|
||||
if (App.StartWithShowMode)
|
||||
{
|
||||
LogHelper.WriteLogToFile("检测到--show参数,退出收纳模式并恢复浮动栏", LogHelper.LogType.Event);
|
||||
// 延迟执行,确保UI已完全加载
|
||||
Dispatcher.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
// 如果当前处于收纳模式,则展开浮动栏
|
||||
if (isFloatingBarFolded)
|
||||
{
|
||||
await UnFoldFloatingBar(new object());
|
||||
}
|
||||
}), DispatcherPriority.Loaded);
|
||||
}
|
||||
}
|
||||
|
||||
private void SystemEventsOnDisplaySettingsChanged(object sender, EventArgs e)
|
||||
@@ -727,6 +740,13 @@ namespace Ink_Canvas
|
||||
_inkFadeManager = null;
|
||||
}
|
||||
|
||||
// 清理悬浮窗拦截管理器
|
||||
if (_floatingWindowInterceptorManager != null)
|
||||
{
|
||||
_floatingWindowInterceptorManager.Dispose();
|
||||
_floatingWindowInterceptorManager = null;
|
||||
}
|
||||
|
||||
// 停止置顶维护定时器
|
||||
StopTopmostMaintenance();
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace Ink_Canvas
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
// 添加当前模式的缓存,避免依赖可能过时的inkCanvas.EditingMode状态
|
||||
private string _currentToolMode = "cursor";
|
||||
|
||||
#region "手勢"按鈕
|
||||
@@ -1537,7 +1536,6 @@ namespace Ink_Canvas
|
||||
// 仅计算Windows任务栏高度,不考虑其他程序对工作区的影响
|
||||
var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY);
|
||||
|
||||
// 计算浮动栏位置,考虑快捷调色盘的显示状态
|
||||
// 使用更可靠的方法获取浮动栏宽度
|
||||
double baseWidth = ViewboxFloatingBar.ActualWidth;
|
||||
|
||||
@@ -1577,7 +1575,7 @@ namespace Ink_Canvas
|
||||
else
|
||||
{
|
||||
// 双行显示模式,宽度较大
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 820 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 68 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1657,7 +1655,7 @@ namespace Ink_Canvas
|
||||
|
||||
public async void PureViewboxFloatingBarMarginAnimationInDesktopMode()
|
||||
{
|
||||
// 新增:在白板模式下不执行浮动栏动画
|
||||
// 在白板模式下不执行浮动栏动画
|
||||
if (currentMode == 1)
|
||||
{
|
||||
return;
|
||||
@@ -1682,8 +1680,6 @@ namespace Ink_Canvas
|
||||
// 仅计算Windows任务栏高度,不考虑其他程序对工作区的影响
|
||||
var toolbarHeight = ForegroundWindowInfo.GetTaskbarHeight(screen, dpiScaleY);
|
||||
|
||||
// 计算浮动栏位置,考虑快捷调色盘的显示状态
|
||||
// 使用更可靠的方法获取浮动栏宽度
|
||||
double baseWidth = ViewboxFloatingBar.ActualWidth;
|
||||
|
||||
// 如果ActualWidth为0,尝试使用DesiredSize
|
||||
@@ -1722,13 +1718,13 @@ namespace Ink_Canvas
|
||||
else
|
||||
{
|
||||
// 双行显示模式,宽度较大
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 850 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 68 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
}
|
||||
}
|
||||
|
||||
pos.X = (screenWidth - floatingBarWidth) / 2;
|
||||
|
||||
// 如果任务栏高度为0(隐藏状态),则使用固定边距
|
||||
// 如果任务栏高度为0,则使用固定边距
|
||||
if (toolbarHeight == 0)
|
||||
{
|
||||
pos.Y = screenHeight - ViewboxFloatingBar.ActualHeight * ViewboxFloatingBarScaleTransform.ScaleY -
|
||||
@@ -1828,7 +1824,7 @@ namespace Ink_Canvas
|
||||
else
|
||||
{
|
||||
// 双行显示模式,宽度较大
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 820 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
floatingBarWidth = Math.Max(floatingBarWidth, 68 * ViewboxFloatingBarScaleTransform.ScaleX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,20 @@ namespace Ink_Canvas
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchEnableSplashScreen_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Appearance.EnableSplashScreen = ToggleSwitchEnableSplashScreen.IsOn;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ComboBoxSplashScreenStyle_SelectionChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Appearance.SplashScreenStyle = ComboBoxSplashScreenStyle.SelectedIndex;
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ViewboxFloatingBarScaleTransformValueSlider_ValueChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
@@ -332,6 +332,10 @@ namespace Ink_Canvas
|
||||
|
||||
ToggleSwitchEnableQuickPanel.IsOn = Settings.Appearance.IsShowQuickPanel;
|
||||
|
||||
ToggleSwitchEnableSplashScreen.IsOn = Settings.Appearance.EnableSplashScreen;
|
||||
|
||||
ComboBoxSplashScreenStyle.SelectedIndex = Settings.Appearance.SplashScreenStyle;
|
||||
|
||||
ToggleSwitchEnableTrayIcon.IsOn = Settings.Appearance.EnableTrayIcon;
|
||||
ICCTrayIconExampleImage.Visibility =
|
||||
Settings.Appearance.EnableTrayIcon ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
@@ -275,6 +275,8 @@ namespace Ink_Canvas
|
||||
{
|
||||
var processes = Process.GetProcessesByName("EasiNote");
|
||||
if (processes.Length > 0) arg += " /IM EasiNote.exe";
|
||||
var seewoStartProcesses = Process.GetProcessesByName("SeewoStart");
|
||||
if (seewoStartProcesses.Length > 0) arg += " /IM SeewoStart.exe";
|
||||
}
|
||||
|
||||
if (Settings.Automation.IsAutoKillHiteAnnotation)
|
||||
@@ -471,10 +473,22 @@ namespace Ink_Canvas
|
||||
Trace.WriteLine(ForegroundWindowInfo.ProcessPath());
|
||||
Trace.WriteLine(version);
|
||||
Trace.WriteLine(prodName);
|
||||
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote && (!(windowTitle.Length == 0 && ForegroundWindowInfo.WindowRect().Height < 500) ||
|
||||
!Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno))
|
||||
if (version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote)
|
||||
{ // EasiNote5
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
// 检查是否是桌面批注窗口
|
||||
bool isAnnotationWindow = windowTitle.Length == 0 && ForegroundWindowInfo.WindowRect().Height < 500;
|
||||
|
||||
// 如果启用了忽略桌面批注窗口功能,且当前是批注窗口
|
||||
if (Settings.Automation.IsAutoFoldInEasiNoteIgnoreDesktopAnno && isAnnotationWindow)
|
||||
{
|
||||
// 强制保持收纳状态
|
||||
if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
else if (!isAnnotationWindow)
|
||||
{
|
||||
// 非批注窗口时正常收纳
|
||||
if (!unfoldFloatingBarByUser && !isFloatingBarFolded) FoldFloatingBar_MouseUp(null, null);
|
||||
}
|
||||
}
|
||||
else if (version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3)
|
||||
{ // EasiNote3
|
||||
|
||||
@@ -696,6 +696,11 @@ namespace Ink_Canvas
|
||||
touchPoint = e.GetTouchPoint(inkCanvas);
|
||||
EraserOverlay_PointerDown(sender);
|
||||
EraserOverlay_PointerMove(sender, touchPoint.Position);
|
||||
if (Settings.Canvas.IsShowCursor)
|
||||
{
|
||||
inkCanvas.ForceCursor = false;
|
||||
inkCanvas.UseCustomCursor = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -840,6 +845,11 @@ namespace Ink_Canvas
|
||||
|
||||
// 禁用橡皮擦覆盖层
|
||||
DisableEraserOverlay();
|
||||
if (Settings.Canvas.IsShowCursor)
|
||||
{
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("Palm eraser state reset completed");
|
||||
}
|
||||
@@ -948,6 +958,12 @@ namespace Ink_Canvas
|
||||
ViewboxFloatingBar.IsHitTestVisible = true;
|
||||
BlackboardUIGridForInkReplay.IsHitTestVisible = true;
|
||||
|
||||
DisableEraserOverlay();
|
||||
if (Settings.Canvas.IsShowCursor)
|
||||
{
|
||||
inkCanvas.ForceCursor = true;
|
||||
inkCanvas.UseCustomCursor = true;
|
||||
}
|
||||
|
||||
LogHelper.WriteLogToFile("Palm eraser force recovery completed");
|
||||
}
|
||||
|
||||
@@ -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.11.4")]
|
||||
[assembly: AssemblyFileVersion("1.7.11.4")]
|
||||
[assembly: AssemblyVersion("1.7.12.0")]
|
||||
[assembly: AssemblyFileVersion("1.7.12.0")]
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Ink_Canvas
|
||||
public RandSettings RandSettings { get; set; } = new RandSettings();
|
||||
[JsonProperty("modeSettings")]
|
||||
public ModeSettings ModeSettings { get; set; } = new ModeSettings();
|
||||
[JsonProperty("camera")]
|
||||
public CameraSettings Camera { get; set; } = new CameraSettings();
|
||||
}
|
||||
|
||||
public class Canvas
|
||||
@@ -199,6 +201,10 @@ namespace Ink_Canvas
|
||||
public int UnFoldButtonImageType { get; set; }
|
||||
[JsonProperty("isShowLRSwitchButton")]
|
||||
public bool IsShowLRSwitchButton { get; set; }
|
||||
[JsonProperty("enableSplashScreen")]
|
||||
public bool EnableSplashScreen { get; set; } = false;
|
||||
[JsonProperty("splashScreenStyle")]
|
||||
public int SplashScreenStyle { get; set; } = 1; // 0-随机, 1-跟随四季, 2-春季, 3-夏季, 4-秋季, 5-冬季, 6-马年限定
|
||||
[JsonProperty("isShowQuickPanel")]
|
||||
public bool IsShowQuickPanel { get; set; } = true;
|
||||
[JsonProperty("chickenSoupSource")]
|
||||
@@ -678,4 +684,19 @@ namespace Ink_Canvas
|
||||
[JsonProperty("isPPTOnlyMode")]
|
||||
public bool IsPPTOnlyMode { get; set; } = false; // 是否为仅PPT模式,默认为false(正常模式)
|
||||
}
|
||||
|
||||
public class CameraSettings
|
||||
{
|
||||
[JsonProperty("rotationAngle")]
|
||||
public int RotationAngle { get; set; } = 0;
|
||||
|
||||
[JsonProperty("resolutionWidth")]
|
||||
public int ResolutionWidth { get; set; } = 1920;
|
||||
|
||||
[JsonProperty("resolutionHeight")]
|
||||
public int ResolutionHeight { get; set; } = 1080;
|
||||
|
||||
[JsonProperty("selectedCameraIndex")]
|
||||
public int SelectedCameraIndex { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 920 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 923 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 926 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 924 KiB |
@@ -181,29 +181,65 @@
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<!-- 摄像头控制面板 -->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
<StackPanel Orientation="Vertical"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0,0,0,8">
|
||||
<!-- 摄像头选择下拉框 -->
|
||||
<ComboBox Name="CameraSelectionComboBox"
|
||||
Width="200"
|
||||
Margin="4,0"
|
||||
Background="#2d2d2d"
|
||||
Foreground="White"
|
||||
BorderBrush="#404040"
|
||||
SelectionChanged="CameraSelectionComboBox_SelectionChanged" />
|
||||
<!-- 第一行:摄像头选择和切换 -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<!-- 摄像头选择下拉框 -->
|
||||
<ComboBox Name="CameraSelectionComboBox"
|
||||
Width="200"
|
||||
Margin="4,0"
|
||||
Background="#2d2d2d"
|
||||
Foreground="White"
|
||||
BorderBrush="#404040"
|
||||
SelectionChanged="CameraSelectionComboBox_SelectionChanged" />
|
||||
|
||||
<!-- 切换摄像头按钮 -->
|
||||
<Button Name="SwitchCameraButton"
|
||||
Content="切换摄像头"
|
||||
Margin="4,0"
|
||||
Padding="8,4"
|
||||
Background="#3b82f6"
|
||||
Foreground="White"
|
||||
BorderThickness="0"
|
||||
FontWeight="Medium"
|
||||
Click="SwitchCameraButton_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- 切换摄像头按钮 -->
|
||||
<Button Name="SwitchCameraButton"
|
||||
Content="切换摄像头"
|
||||
Margin="4,0"
|
||||
Padding="8,4"
|
||||
Background="#3b82f6"
|
||||
Foreground="White"
|
||||
BorderThickness="0"
|
||||
FontWeight="Medium"
|
||||
Click="SwitchCameraButton_Click" />
|
||||
<!-- 第二行:旋转和分辨率控制 -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,4,0,0">
|
||||
<!-- 旋转控制 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="4,0">
|
||||
<TextBlock Text="旋转:" Foreground="White" VerticalAlignment="Center" Margin="0,0,4,0" FontSize="12"/>
|
||||
<Button Name="RotateLeftButton" Content="⟲" Width="30" Height="30" Margin="2,0"
|
||||
Background="#4b5563" Foreground="White" BorderThickness="0"
|
||||
Click="RotateLeftButton_Click" FontSize="10" FontWeight="10"/>
|
||||
<TextBlock Name="RotationAngleText" Text="0°" Foreground="White" VerticalAlignment="Center"
|
||||
Margin="4,0" FontSize="12" MinWidth="30" TextAlignment="Center"/>
|
||||
<Button Name="RotateRightButton" Content="⟳" Width="30" Height="30" Margin="2,0"
|
||||
Background="#4b5563" Foreground="White" BorderThickness="0"
|
||||
Click="RotateRightButton_Click" FontSize="10" FontWeight="10"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<Rectangle Width="1" Height="20" Fill="#404040" Margin="8,0"/>
|
||||
|
||||
<!-- 分辨率控制 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="4,0">
|
||||
<TextBlock Text="分辨率:" Foreground="White" VerticalAlignment="Center" Margin="0,0,4,0" FontSize="12"/>
|
||||
<ComboBox Name="ResolutionComboBox" Width="120" Margin="2,0"
|
||||
Background="#2d2d2d" Foreground="White" BorderBrush="#404040"
|
||||
SelectionChanged="ResolutionComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="640x480" Tag="640,480"/>
|
||||
<ComboBoxItem Content="800x600" Tag="800,600"/>
|
||||
<ComboBoxItem Content="1024x768" Tag="1024,768"/>
|
||||
<ComboBoxItem Content="1280x720" Tag="1280,720"/>
|
||||
<ComboBoxItem Content="1920x1080" Tag="1920,1080"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 摄像头状态指示 -->
|
||||
|
||||
@@ -104,12 +104,20 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
_cameraService = new CameraService();
|
||||
// 从设置中加载摄像头配置
|
||||
var cameraSettings = MainWindow.Settings.Camera;
|
||||
_cameraService = new CameraService(
|
||||
cameraSettings.RotationAngle,
|
||||
cameraSettings.ResolutionWidth,
|
||||
cameraSettings.ResolutionHeight);
|
||||
_cameraService.FrameReceived += CameraService_FrameReceived;
|
||||
_cameraService.ErrorOccurred += CameraService_ErrorOccurred;
|
||||
|
||||
// 初始化摄像头选择下拉框
|
||||
RefreshCameraComboBox();
|
||||
|
||||
// 初始化旋转和分辨率显示
|
||||
InitializeCameraControls();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -117,6 +125,26 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCameraControls()
|
||||
{
|
||||
if (_cameraService != null)
|
||||
{
|
||||
// 更新旋转角度显示
|
||||
UpdateRotationDisplay();
|
||||
|
||||
// 设置分辨率下拉框
|
||||
var currentResolution = $"{_cameraService.ResolutionWidth}x{_cameraService.ResolutionHeight}";
|
||||
foreach (ComboBoxItem item in ResolutionComboBox.Items)
|
||||
{
|
||||
if (item.Tag?.ToString() == $"{_cameraService.ResolutionWidth},{_cameraService.ResolutionHeight}")
|
||||
{
|
||||
ResolutionComboBox.SelectedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshCameraComboBox()
|
||||
{
|
||||
try
|
||||
@@ -1213,6 +1241,70 @@ namespace Ink_Canvas
|
||||
CameraModeButton.Background = new SolidColorBrush(Color.FromRgb(107, 114, 128)); // 灰色
|
||||
}
|
||||
|
||||
#region 摄像头旋转和分辨率控制
|
||||
|
||||
private void RotateLeftButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_cameraService != null)
|
||||
{
|
||||
_cameraService.RotationAngle = (_cameraService.RotationAngle - 1 + 4) % 4;
|
||||
UpdateRotationDisplay();
|
||||
SaveCameraSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void RotateRightButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_cameraService != null)
|
||||
{
|
||||
_cameraService.RotationAngle = (_cameraService.RotationAngle + 1) % 4;
|
||||
UpdateRotationDisplay();
|
||||
SaveCameraSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolutionComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_cameraService != null && ResolutionComboBox.SelectedItem is ComboBoxItem selectedItem)
|
||||
{
|
||||
var resolution = selectedItem.Tag?.ToString();
|
||||
if (!string.IsNullOrEmpty(resolution))
|
||||
{
|
||||
var parts = resolution.Split(',');
|
||||
if (parts.Length == 2 &&
|
||||
int.TryParse(parts[0], out int width) &&
|
||||
int.TryParse(parts[1], out int height))
|
||||
{
|
||||
_cameraService.ResolutionWidth = width;
|
||||
_cameraService.ResolutionHeight = height;
|
||||
SaveCameraSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRotationDisplay()
|
||||
{
|
||||
if (_cameraService != null)
|
||||
{
|
||||
var angle = _cameraService.RotationAngle * 90;
|
||||
RotationAngleText.Text = $"{angle}°";
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveCameraSettings()
|
||||
{
|
||||
if (_cameraService != null)
|
||||
{
|
||||
MainWindow.Settings.Camera.RotationAngle = _cameraService.RotationAngle;
|
||||
MainWindow.Settings.Camera.ResolutionWidth = _cameraService.ResolutionWidth;
|
||||
MainWindow.Settings.Camera.ResolutionHeight = _cameraService.ResolutionHeight;
|
||||
MainWindow.SaveSettingsToFile();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<Window x:Class="Ink_Canvas.Windows.SplashScreen"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Ink Canvas"
|
||||
WindowStyle="None"
|
||||
AllowsTransparency="True"
|
||||
Background="Transparent"
|
||||
WindowState="Maximized"
|
||||
Topmost="True"
|
||||
ShowInTaskbar="False"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<Grid>
|
||||
<!-- 背景遮罩 -->
|
||||
<Rectangle Fill="#80000000" />
|
||||
|
||||
<!-- 启动图片容器 -->
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<!-- 启动图片 -->
|
||||
<Image x:Name="StartupImage"
|
||||
Width="600"
|
||||
Height="450"
|
||||
Stretch="Uniform" />
|
||||
|
||||
<!-- 版本号显示 - 右上角 -->
|
||||
<TextBlock x:Name="VersionTextBlock"
|
||||
FontSize="12"
|
||||
Foreground="FloralWhite"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top"
|
||||
Margin="0,10,50,0"
|
||||
Opacity="0.8"
|
||||
Panel.ZIndex="1000" />
|
||||
</Grid>
|
||||
|
||||
<!-- 加载文本 -->
|
||||
<TextBlock x:Name="LoadingText"
|
||||
Text="正在启动 Ink Canvas..."
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="White"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,200,0,0" />
|
||||
|
||||
<!-- 进度条 -->
|
||||
<Grid HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,276.5,0,0"
|
||||
Width="532"
|
||||
Height="10">
|
||||
<!-- 进度条填充 -->
|
||||
<Border x:Name="ProgressBarFill"
|
||||
HorizontalAlignment="Left"
|
||||
Width="0">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="CornerRadius" Value="0,0,7,7"/>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
</Border>
|
||||
<!-- 进度条背景 -->
|
||||
<Border x:Name="ProgressBarBackground"
|
||||
CornerRadius="7"
|
||||
BorderThickness="1"
|
||||
ClipToBounds="True"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,530 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ink_Canvas.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// SplashScreen.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class SplashScreen : Window
|
||||
{
|
||||
private DispatcherTimer _timer;
|
||||
private int _loadingStep = 0;
|
||||
private int _actualSplashStyle = 1;
|
||||
private readonly string[] _loadingMessages = {
|
||||
"正在启动 Ink Canvas...",
|
||||
"正在初始化组件...",
|
||||
"正在加载配置...",
|
||||
"正在准备界面...",
|
||||
"启动完成!"
|
||||
};
|
||||
|
||||
public SplashScreen()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeSplashScreen();
|
||||
}
|
||||
|
||||
private void InitializeSplashScreen()
|
||||
{
|
||||
// 设置窗口居中
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
// 设置版本号
|
||||
SetVersionText();
|
||||
|
||||
// 加载启动图片并获取实际样式
|
||||
_actualSplashStyle = LoadSplashImageWithStyle();
|
||||
|
||||
// 启动加载动画
|
||||
StartLoadingAnimation();
|
||||
}
|
||||
|
||||
private void StartLoadingAnimation()
|
||||
{
|
||||
_timer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(1200)
|
||||
};
|
||||
_timer.Tick += Timer_Tick;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (_loadingStep < _loadingMessages.Length)
|
||||
{
|
||||
LoadingText.Text = _loadingMessages[_loadingStep];
|
||||
_loadingStep++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timer.Stop();
|
||||
// 不要自动关闭启动画面,等待外部调用CloseSplashScreen
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseSplashScreen()
|
||||
{
|
||||
// 添加淡出动画
|
||||
var fadeOutAnimation = new DoubleAnimation
|
||||
{
|
||||
From = 1,
|
||||
To = 0,
|
||||
Duration = TimeSpan.FromMilliseconds(300),
|
||||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseIn }
|
||||
};
|
||||
|
||||
fadeOutAnimation.Completed += (s, e) =>
|
||||
{
|
||||
this.Close();
|
||||
};
|
||||
|
||||
this.BeginAnimation(OpacityProperty, fadeOutAnimation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置加载进度(0-100)
|
||||
/// </summary>
|
||||
/// <param name="progress">进度百分比</param>
|
||||
public void SetProgress(int progress)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
// 设置进度条颜色
|
||||
SetProgressBarColor();
|
||||
|
||||
// 获取进度条容器的实际宽度
|
||||
double containerWidth = ProgressBarBackground.ActualWidth;
|
||||
if (containerWidth <= 0)
|
||||
{
|
||||
// 如果ActualWidth为0,使用设计时宽度
|
||||
containerWidth = 530;
|
||||
}
|
||||
|
||||
// 计算目标宽度
|
||||
double targetWidth = containerWidth * (progress / 100.0);
|
||||
|
||||
// 创建Storyboard动画
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
// 创建宽度动画
|
||||
var widthAnimation = new DoubleAnimation
|
||||
{
|
||||
From = ProgressBarFill.Width,
|
||||
To = targetWidth,
|
||||
Duration = TimeSpan.FromMilliseconds(300),
|
||||
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
|
||||
};
|
||||
|
||||
// 设置动画目标
|
||||
Storyboard.SetTarget(widthAnimation, ProgressBarFill);
|
||||
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Border.WidthProperty));
|
||||
|
||||
// 添加动画到Storyboard
|
||||
storyboard.Children.Add(widthAnimation);
|
||||
|
||||
// 添加动画完成事件
|
||||
storyboard.Completed += (s, e) =>
|
||||
{
|
||||
// 确保最终值正确设置
|
||||
ProgressBarFill.Width = targetWidth;
|
||||
|
||||
// 根据进度调整圆角
|
||||
if (progress >= 100)
|
||||
{
|
||||
// 进度100%时,底部角都是圆角
|
||||
ProgressBarFill.CornerRadius = new CornerRadius(0, 0, 7, 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 进度未满时,只有左侧是圆角
|
||||
ProgressBarFill.CornerRadius = new CornerRadius(0, 0, 0, 7);
|
||||
}
|
||||
};
|
||||
|
||||
// 开始动画
|
||||
storyboard.Begin();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置加载消息
|
||||
/// </summary>
|
||||
/// <param name="message">加载消息</param>
|
||||
public void SetLoadingMessage(string message)
|
||||
{
|
||||
// 使用实际选择的样式
|
||||
SetLoadingMessage(message, _actualSplashStyle);
|
||||
}
|
||||
|
||||
public void SetLoadingMessage(string message, int actualSplashStyle)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
LoadingText.Text = message;
|
||||
|
||||
// 根据实际启动动画样式调整加载文本样式
|
||||
if (actualSplashStyle == 6) // 马年限定
|
||||
{
|
||||
// 马年限定样式
|
||||
LoadingText.FontSize = 12;
|
||||
LoadingText.FontWeight = FontWeights.SemiBold;
|
||||
LoadingText.Foreground = Brushes.White;
|
||||
LoadingText.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
LoadingText.Margin = new Thickness(0,200,140,4);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 默认样式
|
||||
LoadingText.FontSize = 18;
|
||||
LoadingText.FontWeight = FontWeights.SemiBold;
|
||||
LoadingText.Foreground = Brushes.White;
|
||||
LoadingText.HorizontalAlignment = HorizontalAlignment.Center;
|
||||
LoadingText.Margin = new Thickness(0,200,0,0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前启动动画样式
|
||||
/// </summary>
|
||||
/// <returns>启动动画样式索引</returns>
|
||||
private int GetCurrentSplashStyle()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "Settings.json");
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
var json = File.ReadAllText(settingsPath);
|
||||
dynamic obj = JsonConvert.DeserializeObject(json);
|
||||
if (obj?["appearance"]?["splashScreenStyle"] != null)
|
||||
{
|
||||
return (int)obj["appearance"]["splashScreenStyle"];
|
||||
}
|
||||
}
|
||||
return 1; // 默认跟随四季
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 1; // 默认跟随四季
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置版本号文本
|
||||
/// </summary>
|
||||
private void SetVersionText()
|
||||
{
|
||||
try
|
||||
{
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
if (version != null)
|
||||
{
|
||||
VersionTextBlock.Text = $"v{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
|
||||
}
|
||||
else
|
||||
{
|
||||
VersionTextBlock.Text = "v5.0.4.0";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
VersionTextBlock.Text = "v5.0.4.0";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载启动图片
|
||||
/// </summary>
|
||||
private void LoadSplashImage()
|
||||
{
|
||||
try
|
||||
{
|
||||
string imagePath = GetSplashImagePath();
|
||||
if (!string.IsNullOrEmpty(imagePath))
|
||||
{
|
||||
StartupImage.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(imagePath));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果加载失败,使用默认图片
|
||||
System.Diagnostics.Debug.WriteLine($"加载启动图片失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载启动图片并返回实际样式
|
||||
/// </summary>
|
||||
/// <returns>实际选择的样式</returns>
|
||||
public int LoadSplashImageWithStyle()
|
||||
{
|
||||
try
|
||||
{
|
||||
int actualStyle;
|
||||
string imagePath = GetSplashImagePath(out actualStyle);
|
||||
if (!string.IsNullOrEmpty(imagePath))
|
||||
{
|
||||
StartupImage.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(imagePath));
|
||||
}
|
||||
return actualStyle;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"加载启动图片失败: {ex.Message}");
|
||||
return GetActualStyle(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据设置获取启动图片路径
|
||||
/// </summary>
|
||||
private string GetSplashImagePath()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取设置
|
||||
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "Settings.json");
|
||||
int splashStyle = 1; // 默认跟随四季
|
||||
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
var json = File.ReadAllText(settingsPath);
|
||||
dynamic obj = JsonConvert.DeserializeObject(json);
|
||||
if (obj?["appearance"]?["splashScreenStyle"] != null)
|
||||
{
|
||||
splashStyle = (int)obj["appearance"]["splashScreenStyle"];
|
||||
}
|
||||
}
|
||||
|
||||
// 根据样式选择图片
|
||||
string imageName = GetImageNameByStyle(splashStyle);
|
||||
return $"pack://application:,,,/Resources/Startup-animation/{imageName}";
|
||||
}
|
||||
catch
|
||||
{
|
||||
string imageName = GetImageNameByStyle(1);
|
||||
return $"pack://application:,,,/Resources/Startup-animation/{imageName}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据设置获取启动图片路径和实际样式
|
||||
/// </summary>
|
||||
/// <param name="actualStyle">返回实际选择的样式</param>
|
||||
/// <returns>图片路径</returns>
|
||||
private string GetSplashImagePath(out int actualStyle)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取设置
|
||||
var settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "Settings.json");
|
||||
int splashStyle = 1; // 默认跟随四季
|
||||
|
||||
if (File.Exists(settingsPath))
|
||||
{
|
||||
var json = File.ReadAllText(settingsPath);
|
||||
dynamic obj = JsonConvert.DeserializeObject(json);
|
||||
if (obj?["appearance"]?["splashScreenStyle"] != null)
|
||||
{
|
||||
splashStyle = (int)obj["appearance"]["splashScreenStyle"];
|
||||
}
|
||||
}
|
||||
|
||||
// 根据样式选择图片,并获取实际样式
|
||||
actualStyle = GetActualStyle(splashStyle);
|
||||
string imageName = GetImageNameByStyle(splashStyle);
|
||||
return $"pack://application:,,,/Resources/Startup-animation/{imageName}";
|
||||
}
|
||||
catch
|
||||
{
|
||||
actualStyle = GetActualStyle(1);
|
||||
string imageName = GetImageNameByStyle(1);
|
||||
return $"pack://application:,,,/Resources/Startup-animation/{imageName}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取实际样式
|
||||
/// </summary>
|
||||
/// <param name="style">设置中的样式</param>
|
||||
/// <returns>实际选择的样式</returns>
|
||||
private int GetActualStyle(int style)
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case 0: // 随机
|
||||
var random = new Random();
|
||||
var randomStyles = new[] { 2, 3, 4, 5, 6 }; // 春季、夏季、秋季、冬季、马年限定
|
||||
return randomStyles[random.Next(randomStyles.Length)];
|
||||
|
||||
case 1: // 跟随四季
|
||||
var month = DateTime.Now.Month;
|
||||
if (month >= 3 && month <= 5) return 2; // 春季
|
||||
if (month >= 6 && month <= 8) return 3; // 夏季
|
||||
if (month >= 9 && month <= 11) return 4; // 秋季
|
||||
return 5; // 冬季
|
||||
|
||||
default:
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据样式获取图片名称
|
||||
/// </summary>
|
||||
private string GetImageNameByStyle(int style)
|
||||
{
|
||||
switch (style)
|
||||
{
|
||||
case 0: // 随机
|
||||
var random = new Random();
|
||||
var randomStyles = new[] { 2, 3, 4, 5, 6 }; // 春季、夏季、秋季、冬季、马年限定
|
||||
return GetImageNameByStyle(randomStyles[random.Next(randomStyles.Length)]);
|
||||
|
||||
case 1: // 跟随四季
|
||||
var month = DateTime.Now.Month;
|
||||
if (month >= 3 && month <= 5) return GetImageNameByStyle(2); // 春季
|
||||
if (month >= 6 && month <= 8) return GetImageNameByStyle(3); // 夏季
|
||||
if (month >= 9 && month <= 11) return GetImageNameByStyle(4); // 秋季
|
||||
return GetImageNameByStyle(5); // 冬季
|
||||
|
||||
case 2: // 春季
|
||||
return "ICC Spring.png";
|
||||
case 3: // 夏季
|
||||
return "ICC Summer.png";
|
||||
case 4: // 秋季
|
||||
return "ICC Autumn.png";
|
||||
case 5: // 冬季
|
||||
return "ICC Winter.png";
|
||||
case 6: // 马年限定
|
||||
return "ICC Horse.png";
|
||||
default:// 默认返回
|
||||
return "ICC Horse.png";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据实际样式设置进度条颜色
|
||||
/// </summary>
|
||||
private void SetProgressBarColor()
|
||||
{
|
||||
Color progressColor;
|
||||
|
||||
switch (_actualSplashStyle)
|
||||
{
|
||||
case 2: // 春季 - H=136, S=15, L=22
|
||||
progressColor = HslToRgb(136, 15, 22);
|
||||
break;
|
||||
case 3: // 夏季 - H=6, S=15, L=22
|
||||
progressColor = HslToRgb(6, 15, 22);
|
||||
break;
|
||||
case 4: // 秋季 - H=39, S=15, L=22
|
||||
progressColor = HslToRgb(39, 15, 22);
|
||||
break;
|
||||
case 5: // 冬季 - H=204, S=15, L=22
|
||||
progressColor = HslToRgb(204, 15, 22);
|
||||
break;
|
||||
case 6: // 马年限定 - 白色
|
||||
progressColor = Colors.White;
|
||||
break;
|
||||
default: // 默认使用
|
||||
progressColor = Colors.White;
|
||||
break;
|
||||
}
|
||||
|
||||
// 创建渐变画刷
|
||||
var gradientBrush = new LinearGradientBrush
|
||||
{
|
||||
StartPoint = new System.Windows.Point(0, 0),
|
||||
EndPoint = new System.Windows.Point(1, 0)
|
||||
};
|
||||
|
||||
// 根据颜色类型设置渐变
|
||||
if (_actualSplashStyle == 6) // 马年限定使用白色渐变
|
||||
{
|
||||
gradientBrush.GradientStops.Add(new GradientStop(Colors.White, 0));
|
||||
gradientBrush.GradientStops.Add(new GradientStop(Color.FromArgb(200, 255, 255, 255), 0.5));
|
||||
gradientBrush.GradientStops.Add(new GradientStop(Color.FromArgb(150, 255, 255, 255), 1));
|
||||
}
|
||||
else // 其他样式使用HSL颜色的渐变
|
||||
{
|
||||
var lighterColor = Color.FromArgb(255,
|
||||
(byte)Math.Min(255, progressColor.R + 30),
|
||||
(byte)Math.Min(255, progressColor.G + 30),
|
||||
(byte)Math.Min(255, progressColor.B + 30));
|
||||
var darkerColor = Color.FromArgb(255,
|
||||
(byte)Math.Max(0, progressColor.R - 30),
|
||||
(byte)Math.Max(0, progressColor.G - 30),
|
||||
(byte)Math.Max(0, progressColor.B - 30));
|
||||
|
||||
gradientBrush.GradientStops.Add(new GradientStop(lighterColor, 0));
|
||||
gradientBrush.GradientStops.Add(new GradientStop(progressColor, 0.5));
|
||||
gradientBrush.GradientStops.Add(new GradientStop(darkerColor, 1));
|
||||
}
|
||||
|
||||
ProgressBarFill.Background = gradientBrush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将HSL颜色转换为RGB颜色
|
||||
/// </summary>
|
||||
/// <param name="h">色相 (0-360)</param>
|
||||
/// <param name="s">饱和度 (0-100)</param>
|
||||
/// <param name="l">亮度 (0-100)</param>
|
||||
/// <returns>RGB颜色</returns>
|
||||
private Color HslToRgb(double h, double s, double l)
|
||||
{
|
||||
// 将HSL值转换为0-1范围
|
||||
h = h / 360.0;
|
||||
s = s / 100.0;
|
||||
l = l / 100.0;
|
||||
|
||||
double r, g, b;
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
// 无饱和度,为灰度
|
||||
r = g = b = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
double p = 2 * l - q;
|
||||
|
||||
r = HueToRgb(p, q, h + 1.0 / 3.0);
|
||||
g = HueToRgb(p, q, h);
|
||||
b = HueToRgb(p, q, h - 1.0 / 3.0);
|
||||
}
|
||||
|
||||
return Color.FromRgb(
|
||||
(byte)Math.Round(r * 255),
|
||||
(byte)Math.Round(g * 255),
|
||||
(byte)Math.Round(b * 255)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HSL颜色转换辅助方法
|
||||
/// </summary>
|
||||
private double HueToRgb(double p, double q, double t)
|
||||
{
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1.0 / 6.0) return p + (q - p) * 6 * t;
|
||||
if (t < 1.0 / 2.0) return q;
|
||||
if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,3 +110,6 @@
|
||||
|
||||
## License
|
||||
GPLv3
|
||||
|
||||
## 项目引用
|
||||
[Alan-CRL/DesktopDrawpadBlocker](https://github.com/Alan-CRL/DesktopDrawpadBlocker)
|
||||
|
||||
+18
-1
@@ -81,4 +81,21 @@ ICC CE 1.7.X.X更新日志
|
||||
80. 修复切换模式后无法持续生效
|
||||
81. 优化UI
|
||||
82. 修复插入图片后使用多指书写导致图片消失
|
||||
83. 优化PowerPoint联动增强
|
||||
83. 优化PowerPoint联动增强
|
||||
84. 修复墨迹克隆
|
||||
85. 修复白板手势
|
||||
86. 新增退出白板自动收纳
|
||||
87. 修复进入PPT自动收纳
|
||||
88. 更新计时器铃声及按钮样式
|
||||
89. 更新更新弹窗样式
|
||||
90. 完善悬浮窗拦截功能
|
||||
91. 新增主题切换
|
||||
92. 更新历史版本回滚页面样式
|
||||
93. 修复悬浮窗拦截
|
||||
94. 改进计时器
|
||||
95. 新增配置文件损坏自动恢复
|
||||
96. 修复手掌擦
|
||||
97. 改进橡皮系统
|
||||
98. 新增启动动画
|
||||
99. 修复仅调色盘状态下浮动栏不居中
|
||||
100. 修复希沃白板查杀与思锐希沃启动器导致的重复启动
|
||||
Reference in New Issue
Block a user