From 9b5ee56a091b9a19f9314b3924573c3fb9b4de98 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sun, 28 Dec 2025 14:30:02 +0800 Subject: [PATCH] improve:issue #175 --- Ink Canvas/Helpers/WindowOverviewModel.cs | 479 ++++++++++++++++++++++ Ink Canvas/MainWindow.xaml.cs | 20 + Ink Canvas/MainWindow_cs/MW_Timer.cs | 245 ++++++++++- 3 files changed, 736 insertions(+), 8 deletions(-) create mode 100644 Ink Canvas/Helpers/WindowOverviewModel.cs diff --git a/Ink Canvas/Helpers/WindowOverviewModel.cs b/Ink Canvas/Helpers/WindowOverviewModel.cs new file mode 100644 index 00000000..c9af2f2b --- /dev/null +++ b/Ink Canvas/Helpers/WindowOverviewModel.cs @@ -0,0 +1,479 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; + +namespace Ink_Canvas.Helpers +{ + /// + /// 矩形结构体(用于窗口位置和大小) + /// + [StructLayout(LayoutKind.Sequential)] + public struct WindowRect + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int Width => Right - Left; + public int Height => Bottom - Top; + } + + /// + /// 窗口信息结构 + /// + public class WindowInfo + { + public IntPtr Handle { get; set; } + public string Title { get; set; } + public string ClassName { get; set; } + public string ProcessName { get; set; } + public string ProcessPath { get; set; } + public WindowRect Rect { get; set; } + public bool IsVisible { get; set; } + public bool IsMinimized { get; set; } + public bool IsMaximized { get; set; } + public int ZOrder { get; set; } + public uint ProcessId { get; set; } + public bool IsFullScreen { get; set; } + + /// + /// 计算窗口是否覆盖指定区域 + /// + public bool CoversArea(WindowRect area) + { + if (!IsVisible || IsMinimized) return false; + + // 计算交集 + int left = Math.Max(Rect.Left, area.Left); + int top = Math.Max(Rect.Top, area.Top); + int right = Math.Min(Rect.Right, area.Right); + int bottom = Math.Min(Rect.Bottom, area.Bottom); + + // 如果有交集,说明窗口覆盖了该区域 + return left < right && top < bottom; + } + + /// + /// 计算窗口覆盖指定区域的比例 + /// + public double GetCoverageRatio(WindowRect area) + { + if (!IsVisible || IsMinimized) return 0.0; + + // 计算交集 + int left = Math.Max(Rect.Left, area.Left); + int top = Math.Max(Rect.Top, area.Top); + int right = Math.Min(Rect.Right, area.Right); + int bottom = Math.Min(Rect.Bottom, area.Bottom); + + if (left >= right || top >= bottom) return 0.0; + + // 计算交集面积 + double intersectionArea = (right - left) * (bottom - top); + // 计算目标区域面积 + double targetArea = area.Width * area.Height; + + if (targetArea == 0) return 0.0; + + return intersectionArea / targetArea; + } + } + + /// + /// 窗口概览模型 - 实时监控桌面所有可见窗口并计算遮挡情况 + /// + public class WindowOverviewModel : IDisposable + { + #region Win32 API Declarations + [DllImport("user32.dll")] + private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetWindowRect(IntPtr hWnd, out WindowRect lpRect); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool IsZoomed(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + private const uint GW_HWNDNEXT = 2; + private const uint GW_HWNDPREV = 3; + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + #endregion + + private readonly object _lockObject = new object(); + private List _windows = new List(); + private Timer _updateTimer; + private bool _isDisposed = false; + private readonly int _updateInterval = 200; // 更新间隔(毫秒) + + /// + /// 窗口列表更新事件 + /// + public event EventHandler> WindowsUpdated; + + /// + /// 当前窗口列表(按Z顺序排序,最上层在前) + /// + public List Windows + { + get + { + lock (_lockObject) + { + return new List(_windows); + } + } + } + + /// + /// 构造函数 + /// + public WindowOverviewModel() + { + // 立即执行一次更新 + UpdateWindows(); + + // 启动定时器,定期更新窗口列表 + _updateTimer = new Timer(OnUpdateTimer, null, _updateInterval, _updateInterval); + } + + /// + /// 定时器回调 + /// + private void OnUpdateTimer(object state) + { + if (_isDisposed) return; + + try + { + UpdateWindows(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"窗口概览模型更新失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 更新窗口列表 + /// + public void UpdateWindows() + { + var windows = new List(); + var zOrder = 0; + + EnumWindows((hWnd, lParam) => + { + try + { + // 检查窗口是否可见 + if (!IsWindowVisible(hWnd)) return true; + + // 检查窗口是否最小化 + bool isMinimized = IsIconic(hWnd); + if (isMinimized) return true; + + // 获取窗口矩形 + if (!GetWindowRect(hWnd, out WindowRect rect)) return true; + + // 过滤掉无效的窗口(太小或位置异常的窗口) + if (rect.Width <= 0 || rect.Height <= 0) return true; + if (rect.Right < rect.Left || rect.Bottom < rect.Top) return true; + + // 获取窗口标题 + const int nChars = 256; + StringBuilder windowTitle = new StringBuilder(nChars); + GetWindowText(hWnd, windowTitle, nChars); + string title = windowTitle.ToString(); + + // 获取窗口类名 + StringBuilder className = new StringBuilder(nChars); + GetClassName(hWnd, className, nChars); + string classNameStr = className.ToString(); + + // 获取进程信息 + GetWindowThreadProcessId(hWnd, out uint processId); + string processName = "Unknown"; + string processPath = "Unknown"; + + try + { + Process process = Process.GetProcessById((int)processId); + processName = process.ProcessName; + try + { + processPath = process.MainModule?.FileName ?? "Unknown"; + } + catch + { + processPath = "Unknown"; + } + } + catch + { + // 进程可能已退出 + } + + // 检查是否最大化 + bool isMaximized = IsZoomed(hWnd); + + // 检查是否全屏(窗口大小接近屏幕大小) + bool isFullScreen = false; + try + { + var screen = System.Windows.Forms.Screen.FromHandle(hWnd); + var screenBounds = screen.Bounds; + // 如果窗口大小接近屏幕大小(允许10像素误差),认为是全屏 + isFullScreen = rect.Width >= screenBounds.Width - 10 && + rect.Height >= screenBounds.Height - 10 && + Math.Abs(rect.Left - screenBounds.Left) <= 10 && + Math.Abs(rect.Top - screenBounds.Top) <= 10; + } + catch + { + // 无法获取屏幕信息,使用默认值 + } + + // 跳过当前应用程序的窗口(避免检测到自己) + if (processName == "InkCanvasForClass" || processName == "Ink Canvas") + { + return true; + } + + var windowInfo = new WindowInfo + { + Handle = hWnd, + Title = title, + ClassName = classNameStr, + ProcessName = processName, + ProcessPath = processPath, + Rect = rect, + IsVisible = true, + IsMinimized = false, + IsMaximized = isMaximized, + ZOrder = zOrder++, + ProcessId = processId, + IsFullScreen = isFullScreen + }; + + windows.Add(windowInfo); + } + catch + { + // 忽略单个窗口的错误,继续枚举其他窗口 + } + + return true; // 继续枚举 + }, IntPtr.Zero); + + // 按Z顺序排序(从最上层到最下层) + // 注意:EnumWindows返回的顺序可能不是严格的Z顺序,但我们可以通过GetWindow来获取更准确的顺序 + windows = windows.OrderByDescending(w => w.ZOrder).ToList(); + + lock (_lockObject) + { + _windows = windows; + } + + // 触发更新事件 + WindowsUpdated?.Invoke(this, windows); + } + + /// + /// 检查指定区域是否被其他窗口覆盖 + /// + /// 要检查的区域 + /// 要排除的进程名列表(例如当前应用程序) + /// 覆盖阈值(0.0-1.0),超过此阈值认为被覆盖 + /// 如果被覆盖返回true + public bool IsAreaCovered(WindowRect area, List excludeProcessNames = null, double coverageThreshold = 0.5) + { + lock (_lockObject) + { + excludeProcessNames = excludeProcessNames ?? new List(); + + // 从最上层窗口开始检查 + foreach (var window in _windows) + { + // 跳过排除的进程 + if (excludeProcessNames.Contains(window.ProcessName)) continue; + + // 计算覆盖比例 + double coverage = window.GetCoverageRatio(area); + if (coverage >= coverageThreshold) + { + return true; + } + } + + return false; + } + } + + /// + /// 检查指定区域是否被全屏窗口覆盖 + /// + /// 要检查的区域 + /// 要排除的进程名列表 + /// 如果被全屏窗口覆盖返回true + public bool IsAreaCoveredByFullScreenWindow(WindowRect area, List excludeProcessNames = null) + { + lock (_lockObject) + { + excludeProcessNames = excludeProcessNames ?? new List(); + + // 查找全屏窗口 + foreach (var window in _windows) + { + // 跳过排除的进程 + if (excludeProcessNames.Contains(window.ProcessName)) continue; + + // 只检查全屏窗口 + if (window.IsFullScreen && window.CoversArea(area)) + { + return true; + } + } + + return false; + } + } + + /// + /// 获取覆盖指定区域的所有窗口 + /// + /// 要检查的区域 + /// 要排除的进程名列表 + /// 覆盖阈值 + /// 覆盖该区域的窗口列表(按Z顺序,最上层在前) + public List GetCoveringWindows(WindowRect area, List excludeProcessNames = null, double coverageThreshold = 0.1) + { + lock (_lockObject) + { + excludeProcessNames = excludeProcessNames ?? new List(); + var coveringWindows = new List(); + + foreach (var window in _windows) + { + // 跳过排除的进程 + if (excludeProcessNames.Contains(window.ProcessName)) continue; + + // 计算覆盖比例 + double coverage = window.GetCoverageRatio(area); + if (coverage >= coverageThreshold) + { + coveringWindows.Add(window); + } + } + + return coveringWindows; + } + } + + /// + /// 检查是否有全屏窗口 + /// + /// 要排除的进程名列表 + /// 如果有全屏窗口返回true + public bool HasFullScreenWindow(List excludeProcessNames = null) + { + lock (_lockObject) + { + excludeProcessNames = excludeProcessNames ?? new List(); + + return _windows.Any(w => !excludeProcessNames.Contains(w.ProcessName) && w.IsFullScreen); + } + } + + /// + /// 获取所有全屏窗口 + /// + /// 要排除的进程名列表 + /// 全屏窗口列表 + public List GetFullScreenWindows(List excludeProcessNames = null) + { + lock (_lockObject) + { + excludeProcessNames = excludeProcessNames ?? new List(); + + return _windows.Where(w => !excludeProcessNames.Contains(w.ProcessName) && w.IsFullScreen).ToList(); + } + } + + /// + /// 根据进程名查找窗口 + /// + /// 进程名 + /// 匹配的窗口列表 + public List FindWindowsByProcessName(string processName) + { + lock (_lockObject) + { + return _windows.Where(w => w.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase)).ToList(); + } + } + + /// + /// 根据窗口标题查找窗口 + /// + /// 窗口标题(支持部分匹配) + /// 匹配的窗口列表 + public List FindWindowsByTitle(string title) + { + lock (_lockObject) + { + return _windows.Where(w => w.Title.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); + } + } + + /// + /// 释放资源 + /// + public void Dispose() + { + if (_isDisposed) return; + + _isDisposed = true; + _updateTimer?.Dispose(); + _updateTimer = null; + + lock (_lockObject) + { + _windows.Clear(); + } + } + } +} + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index c477b173..0f0e9182 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -62,6 +62,8 @@ namespace Ink_Canvas // 悬浮窗拦截管理器 private FloatingWindowInterceptorManager _floatingWindowInterceptorManager; + // 窗口概览模型 + private WindowOverviewModel _windowOverviewModel; // 设置面板相关状态 private bool userChangedNoFocusModeInSettings; @@ -591,6 +593,17 @@ namespace Ink_Canvas StartPPTMonitoring(); } + // 初始化窗口概览模型 + try + { + _windowOverviewModel = new WindowOverviewModel(); + LogHelper.WriteLogToFile("窗口概览模型已初始化", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"初始化窗口概览模型失败: {ex.Message}", LogHelper.LogType.Error); + } + // 如果启用PowerPoint联动增强功能,启动进程守护 if (Settings.PowerPointSettings.EnablePowerPointEnhancement) { @@ -995,6 +1008,13 @@ namespace Ink_Canvas _floatingWindowInterceptorManager = null; } + // 清理窗口概览模型 + if (_windowOverviewModel != null) + { + _windowOverviewModel.Dispose(); + _windowOverviewModel = null; + } + // 停止置顶维护定时器 StopTopmostMaintenance(); diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs index 3de74f33..305378aa 100644 --- a/Ink Canvas/MainWindow_cs/MW_Timer.cs +++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs @@ -1,5 +1,6 @@ using Ink_Canvas.Helpers; using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -13,6 +14,7 @@ using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using WinForms = System.Windows.Forms; namespace Ink_Canvas { @@ -57,18 +59,18 @@ namespace Ink_Canvas public partial class MainWindow : Window { - private Timer timerCheckPPT = new Timer(); - private Timer timerKillProcess = new Timer(); - private Timer timerCheckAutoFold = new Timer(); + private System.Timers.Timer timerCheckPPT = new System.Timers.Timer(); + private System.Timers.Timer timerKillProcess = new System.Timers.Timer(); + private System.Timers.Timer timerCheckAutoFold = new System.Timers.Timer(); private string AvailableLatestVersion; - private Timer timerCheckAutoUpdateWithSilence = new Timer(); - private Timer timerCheckAutoUpdateRetry = new Timer(); + private System.Timers.Timer timerCheckAutoUpdateWithSilence = new System.Timers.Timer(); + private System.Timers.Timer timerCheckAutoUpdateRetry = new System.Timers.Timer(); private bool isHidingSubPanelsWhenInking; // 避免书写时触发二次关闭二级菜单导致动画不连续 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(); + private System.Timers.Timer timerDisplayTime = new System.Timers.Timer(); + private System.Timers.Timer timerDisplayDate = new System.Timers.Timer(); + private System.Timers.Timer timerNtpSync = new System.Timers.Timer(); private TimeViewModel nowTimeVM = new TimeViewModel(); private DateTime cachedNetworkTime = DateTime.Now; @@ -512,6 +514,14 @@ namespace Ink_Canvas if (isFloatingBarChangingHideMode) return; try { + // 优先使用窗口概览模型进行检测 + if (_windowOverviewModel != null) + { + CheckAutoFoldWithWindowOverviewModel(); + return; + } + + // 如果窗口概览模型未初始化,回退到传统的进程检测方式 var windowProcessName = ForegroundWindowInfo.ProcessName(); var windowTitle = ForegroundWindowInfo.WindowTitle(); //LogHelper.WriteLogToFile("windowTitle | " + windowTitle + " | windowProcessName | " + windowProcessName); @@ -732,6 +742,225 @@ namespace Ink_Canvas catch { } } + /// + /// 检查进程是否在用户的自动收纳设置中启用 + /// + private bool IsProcessInAutoFoldSettings(string processName, WindowInfo windowInfo = null) + { + // 根据进程名和窗口信息检查是否在自动收纳设置中 + switch (processName) + { + case "EasiNote": + // EasiNote需要检查版本 + if (windowInfo != null && !string.IsNullOrEmpty(windowInfo.ProcessPath) && windowInfo.ProcessPath != "Unknown") + { + try + { + var versionInfo = FileVersionInfo.GetVersionInfo(windowInfo.ProcessPath); + string version = versionInfo.FileVersion; + string prodName = versionInfo.ProductName; + + if (version != null && version.StartsWith("5.") && Settings.Automation.IsAutoFoldInEasiNote) + return true; + if (version != null && version.StartsWith("3.") && Settings.Automation.IsAutoFoldInEasiNote3) + return true; + if (prodName != null && prodName.Contains("3C") && Settings.Automation.IsAutoFoldInEasiNote3C) + return true; + } + catch { } + } + return false; + + case "EasiCamera": + return Settings.Automation.IsAutoFoldInEasiCamera; + + case "EasiNote5C": + return Settings.Automation.IsAutoFoldInEasiNote5C; + + case "BoardService": + case "seewoPincoTeacher": + return Settings.Automation.IsAutoFoldInSeewoPincoTeacher; + + case "HiteCamera": + return Settings.Automation.IsAutoFoldInHiteCamera; + + case "HiteTouchPro": + return Settings.Automation.IsAutoFoldInHiteTouchPro; + + case "HiteLightBoard": + return Settings.Automation.IsAutoFoldInHiteLightBoard; + + case "WxBoardMain": + return Settings.Automation.IsAutoFoldInWxBoardMain; + + case "MicrosoftWhiteboard": + case "msedgewebview2": + return Settings.Automation.IsAutoFoldInMSWhiteboard; + + case "Amdox.WhiteBoard": + return Settings.Automation.IsAutoFoldInAdmoxWhiteboard; + + case "Amdox.Booth": + return Settings.Automation.IsAutoFoldInAdmoxBooth; + + case "QPoint": + return Settings.Automation.IsAutoFoldInQPoint; + + case "YiYunVisualPresenter": + return Settings.Automation.IsAutoFoldInYiYunVisualPresenter; + + case "WhiteBoard": + // MaxHub需要检查窗口标题 + if (windowInfo != null && !string.IsNullOrEmpty(windowInfo.Title) && + windowInfo.Title.Contains("白板书写") && Settings.Automation.IsAutoFoldInMaxHubWhiteboard) + { + if (!string.IsNullOrEmpty(windowInfo.ProcessPath) && windowInfo.ProcessPath != "Unknown") + { + try + { + var versionInfo = FileVersionInfo.GetVersionInfo(windowInfo.ProcessPath); + if (versionInfo.FileVersion != null && versionInfo.FileVersion.StartsWith("6.") && + versionInfo.ProductName == "WhiteBoard") + return true; + } + catch { } + } + } + return false; + + default: + return false; + } + } + + /// + /// 使用窗口概览模型检测是否需要自动收纳 + /// + private void CheckAutoFoldWithWindowOverviewModel() + { + try + { + if (_windowOverviewModel == null) return; + + // 获取浮动栏的位置和大小 + Application.Current.Dispatcher.Invoke(() => + { + try + { + var floatingBarMargin = ViewboxFloatingBar.Margin; + var floatingBarWidth = ViewboxFloatingBar.ActualWidth; + var floatingBarHeight = ViewboxFloatingBar.ActualHeight; + + // 如果浮动栏未显示或大小为0,跳过检测 + if (floatingBarWidth <= 0 || floatingBarHeight <= 0) return; + + // 计算浮动栏在屏幕上的位置(考虑DPI缩放) + var screen = WinForms.Screen.PrimaryScreen; + var dpiScaleX = PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M11 ?? 1.0; + var dpiScaleY = PresentationSource.FromVisual(this)?.CompositionTarget?.TransformToDevice.M22 ?? 1.0; + + // 将WPF坐标转换为屏幕坐标 + var point = ViewboxFloatingBar.PointToScreen(new System.Windows.Point(0, 0)); + int left = (int)(point.X); + int top = (int)(point.Y); + int right = left + (int)(floatingBarWidth * dpiScaleX); + int bottom = top + (int)(floatingBarHeight * dpiScaleY); + + // 创建检测区域(稍微扩大一点,确保检测到覆盖) + var detectionArea = new WindowRect + { + Left = left - 5, + Top = top - 5, + Right = right + 5, + Bottom = bottom + 5 + }; + + // 排除当前应用程序的进程 + var excludeProcesses = new List { "InkCanvasForClass", "Ink Canvas" }; + + // 检查 OldZyBoard(通过窗口标题检测) + bool isOldZyBoardWindowExisted = Settings.Automation.IsAutoFoldInOldZyBoard && + (WinTabWindowsChecker.IsWindowExisted("WhiteBoard - DrawingWindow") || + WinTabWindowsChecker.IsWindowExisted("InstantAnnotationWindow")); + + if (isOldZyBoardWindowExisted) + { + if (!isFloatingBarFolded) + { + FoldFloatingBar_MouseUp(new object(), null); + } + } + else if (!isOldZyBoardWindowExisted && isFloatingBarFolded && !foldFloatingBarByUser) + { + // OldZyBoard窗口退出时,如果未开启保持收纳模式,则展开 + if (!Settings.Automation.KeepFoldAfterSoftwareExit) + { + UnFoldFloatingBar_MouseUp(new object(), null); + } + return; // OldZyBoard 使用特殊检测方式,处理完后直接返回 + } + + if (isOldZyBoardWindowExisted) + { + return; // OldZyBoard 窗口存在时,直接返回,不继续检测其他窗口 + } + + // 获取覆盖浮动栏的所有窗口 + var coveringWindows = _windowOverviewModel.GetCoveringWindows(detectionArea, excludeProcesses, 0.1); + + // 检查是否有覆盖窗口在用户的自动收纳设置中 + bool shouldFold = false; + + foreach (var window in coveringWindows) + { + // 检查窗口是否全屏(全屏窗口优先) + bool isFullScreen = window.IsFullScreen; + + // 检查窗口大小是否接近全屏(用于检测二级菜单等) + bool isNearFullScreen = false; + try + { + var screenBounds = WinForms.Screen.FromHandle(window.Handle).Bounds; + isNearFullScreen = window.Rect.Width >= screenBounds.Width - 16 && + window.Rect.Height >= screenBounds.Height - 16; + } + catch { } + + // 如果窗口是全屏或接近全屏,且进程在自动收纳设置中,则应该收纳 + if ((isFullScreen || isNearFullScreen) && IsProcessInAutoFoldSettings(window.ProcessName, window)) + { + shouldFold = true; + break; // 找到匹配的窗口就退出 + } + } + + // 如果检测到应该收纳的窗口,且当前未收纳,则收纳 + if (shouldFold && !isFloatingBarFolded) + { + FoldFloatingBar_MouseUp(new object(), null); + } + // 如果未检测到应该收纳的窗口,且当前已收纳(且不是用户手动收纳),则展开 + else if (!shouldFold && isFloatingBarFolded && !foldFloatingBarByUser) + { + // 检查是否启用了软件退出后保持收纳模式 + if (!Settings.Automation.KeepFoldAfterSoftwareExit) + { + UnFoldFloatingBar_MouseUp(new object(), null); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"窗口概览模型检测失败: {ex.Message}", LogHelper.LogType.Error); + } + }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"窗口概览模型自动收纳检测异常: {ex.Message}", LogHelper.LogType.Error); + } + } + private void timerCheckAutoUpdateWithSilence_Elapsed(object sender, ElapsedEventArgs e) { // 停止计时器,避免重复触发