刷抖音刷到一个项目,就是一个可以增加鼠标功能的操作。它为鼠标赋予了以下操作:

Action + Target = Result
Middle click + title bar = close window
Right click + title bar = minimize window
Hold left click + title bar = toggle window always on top
Double press + Esc key = close active window
Right click + taskbar button = move pointer to “Close window”

软件不大,主要使用 AutoHotKey 脚本进行开发。鼠标快捷操作挺好用的,尤其是中键关闭窗口的操作,统一了浏览器关闭标签页的动作。但是我看中了其中的脚本语言 AutoHotKey,感觉它很有用处,遂决定剖析源码,提取其中执行快捷操作的有用脚本进行学习。

以下是原项目的地址:

chaohershi/cclose: A Windows utility that helps you close windows faster or pin windows always on top. (github.com)

原项目的 AutoHotKey 脚本语言版本应该是 V1,下面我将用 V2 版本进行改写并对其进行了简化 (😥 其实是删去我看不懂但实际影响不大的内容) 。读者可以摘取其中的片段作为自己的脚本。改进点:

  • AutoHotKey 语法升级:从 V1 到 V2
  • 启用部分快捷功能有提示

解析开始 🔽

公共函数部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#Requires AutoHotkey v2.0

; 检测鼠标是否在指定的WinTitle窗口中
MouseIsOver(WinTitle)
{
MouseGetPos , , &win
return WinExist(WinTitle " ahk_id " win)
}

; 检测鼠标是否悬停在标题
MouseIsOverTitlebar()
{
CoordMode "Mouse", "Screen"
MouseGetPos &x, &y, &win
; 用于存储目标窗口左上角的 X 和 Y 坐标的变量的引用
WinGetPos &xWin, &yWin, , , "ahk_id " win

if WinExist("ahk_class Shell_TrayWnd ahk_id " win) || WinExist("ahk_class Shell_SecondaryTrayWnd ahk_id " win) ; exclude the taskbar
{
return false
}
else if (y >= yWin && y < yWin + 30 * A_ScreenDPI / 96)
; if within title bar width
{
return true
}
return false
}

; 展示1s钟的标签
ShowToolTip(msg)
{
ToolTip msg,,0
SetTimer () => ToolTip(), -1000
}

第一部分 标题操作

Action + Target = Result
Middle click + title bar = close window
Right click + title bar = minimize window
Hold left click + title bar = toggle window always on top
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#HotIf MouseIsOverTitlebar()
; 功能:鼠标中键点击标题关闭窗口
MButton::
{
; alternative to WinClose, as WinClose is a somewhat forceful method, e.g., if multiple Microsoft Excel instances exist, WinClose will close them all at once.
; 0x112 = WM_SYSCOMMAND, 0xF060 = SC_CLOSE https://autohotkey.com/docs/commands/WinClose.htm#Remarks
; https://learn.microsoft.com/zh-cn/windows/win32/menurc/wm-syscommand
MouseGetPos , , &win ; 我加的,要不然它不起作用
PostMessage 0x112, 0xF060, , win
ShowToolTip("窗口关闭")
}

; 功能:鼠标右键点击标题最小化窗口
RButton::
{
err_code := KeyWait("RButton", "T0.4")
if(err_code = 0) ;超时
{
Send "{Click, Right}" ; n.b., do not use Send {RButton}
}else{
; https://learn.microsoft.com/zh-cn/windows/win32/menurc/wm-syscommand
MouseGetPos , , &win ; 我加的,要不然它不起作用
PostMessage 0x112, 0xF020, ,win ; alternative to WinMinimize
ShowToolTip("窗口最小化")
}
}

; 窗口置顶功能
~LButton::
{
CoordMode "Mouse", "Screen"
MouseGetPos &xOld, &yOld, &win
ExStyle := WinGetExStyle(win)
if (ExStyle & 0x8) ; 0x8 is WS_EX_TOPMOST
{
ExStyle := "窗口已取消置顶"
}
else
{
ExStyle := "窗口已置顶"
}
err_code := KeyWait("LButton", "T1") ; wait for the left mouse button to be released with timeout set to 1 second
MouseGetPos &xNew, &yNew
if (xOld = xNew && yOld = yNew && err_code = 0) ; if the mouse did not move during the timeout period
{
WinSetAlwaysOnTop -1, "A" ; toggle window always on top
ShowToolTip(ExStyle)
}
}

第二部分 双击 Esc 关闭窗口

Action + Target = Result
Double press + Esc key = close active window
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; 功能:双击Esc关闭窗口
#HotIf
~Esc::
{
id_old := WinGetID("A")
process_name := WinGetProcessName("A")
KeyWait "Esc" ; wait for the Esc key to be released
err_code := KeyWait("Esc", "D T0.4")
if (err_code = 1){
id_new := WinGetID("A") ; get the window id after the Esc key has being pressed again
win_class := WinGetClass("A")
if (id_old = id_new && win_class != "Shell_TrayWnd" && win_class != "Shell_SecondaryTrayWnd" && win_class != "Progman" && win_class != "WorkerW")
{
; if the current window is the same one as before and is not taskbar or desktop
Send "!{F4}"
}
}
}

第三部分 右击任务栏

Action + Target = Result
Right click + taskbar button = move pointer to “Close window”

右击任务栏后,如果鼠标自动悬停在关闭窗口上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
; 功能,右击任务栏图标鼠标自动跳转到关闭窗口
#HotIf MouseIsOver("ahk_class Shell_TrayWnd") || MouseIsOver("ahk_class Shell_SecondaryTrayWnd") ; apply the following hotkey only when the mouse is over the taskbar
~RButton:: ; when right clicked
{
CoordMode "Mouse", "Screen"
MouseGetPos &xOld, &yOld
Sleep 500 ; wait for the Jump List to pop up, n.b., this line also helps to provide a uniform waiting experience
MouseGetPos &xNew, &yNew
CoordMode "Mouse", "Window"
if (Abs(xNew - xOld) < 8 && Abs(yNew - yOld) < 8) ; if the mouse did not move much
{
Loop 6
{
if WinActive("ahk_class Windows.UI.Core.CoreWindow") ; if the Jump List pops up (right clicked on the taskbar app buttons)
{
WinGetPos , , &width, &height ; get the size of the last found window (Jump List)
MouseMove (width / 2), (height - 3 * width / 32), 10 ; move the mouse to the bottom of the Jump List ("Close window")
break
}
Sleep 250 ; wait for more time
}
}
}

总结

上面的示例代码可以组成一个.ahk 文件,实现所有 CClose 的基本功能。

开机自启动方法:

  1. Win+R 打开运行窗口,输入 shell:startup
  2. 把脚本的快捷方式丢到里面即可

未来可以改进的地方:

  • 控制部分功能的启用与禁用(好的,又成 CClose 的轮子版了)
  • 进一步,对自己的脚本进行控制和管理
  • 对使用了管理员权限运行的系统无效
  • 状态栏图标改一下
  • 学习制作一个 Windows 软件

目前存在的问题:

  • 在运行《荒野大镖客 2》时部分情况下会触发。估计全屏游戏都会出现这个问题,待修复和改进。