随笔 - 52,  文章 - 0,  评论 - 7,  阅读 - 9836

功能描述

原始的右键菜单可能无法满足我的日常需求,比如简单的一点:复制路径。

所以我使用ahk写了一个脚本,可以打开自定义的菜单(使用热键);

仓库地址

截图如下:

image

其中添加了几项有用的功能,相比右键菜单要方便很多。

实现原理

原理是获取到文件管理器窗口,并获取其选中项属性(选中的文件),见下面代码:

#Include ..\Extend.ahk
#Include ..\Path.ahk
class Explorer {
__New(hwnd) {
window := this._GetWindow(hwnd)
if !window {
throw Error('invalid window')
}
this.hwnd := hwnd
this.isDesktop := window = 'desktop'
this.window := window
}
static IsValidHwnd(hwnd) => WinGetProcessName('ahk_id' hwnd) = 'explorer.exe'
static IsWinExist(hwnd) => WinExist('ahk_id' hwnd)
_GetWindow(hwnd) {
if !Explorer.IsValidHwnd(hwnd) {
return
}
className := WinGetClass('ahk_id ' hwnd)
if className ~= '(Cabinet|Explore)WClass' {
for win in ComObject('Shell.Application').Windows
if win.Hwnd = hwnd {
return win
}
} else if className ~= 'Progman|WorkerW' {
return 'desktop'
}
}
_GetListViewContent(selection, options) {
if !Explorer.IsWinExist(this.hwnd)
return
arr := []
if this.isDesktop {
List := ListViewGetContent(options, "SysListView321", 'ahk_class WorkerW')
Loop Parse, List, "`n" {
loop parse A_LoopField, A_Tab
arr.Push(Path.Join(A_Desktop, A_LoopField))
}
} else {
if selection
collection := this.window.document.Selecteditems
else
collection := this.window.document.Folder.Items
for item in collection {
arr.push(item.path)
}
}
return arr
}
GetListViewSelected() => this._GetListViewContent(true, "Selected Col1")
GetListViewAll() => this._GetListViewContent(false, 'Col1')
GetLocationURL() => (Explorer.IsWinExist(this.hwnd) && !this.isDesktop) ? this.window.LocationURL : ''
GetURL() => SubStr(this.GetLocationURL(), 9)
}

源脚本来自网上

通过此脚本,就可以获取到选中文件、URL等信息了。

实现ui

上面的脚本可以获取到数据,还需要界面来展示,下面的代码使用ListView来实现一个简单菜单:

#Include G:\AHK\git-ahk-lib\Tip.ahk
#Include G:\AHK\git-ahk-lib\Theme.ahk
#Include G:\AHK\git-ahk-lib\Extend.ahk
#Include G:\AHK\git-ahk-lib\util\Explorer.ahk
#Include G:\AHK\git-ahk-lib\util\Animation.ahk
CoordMode 'Mouse', 'Screen'
!#0:: ShowContextMenu()
imgList := IL_Create(10)
, IL_Add(imgList, './resource/icon/vscode.ico')
, IL_Add(imgList, './resource/icon/markdown.ico')
, IL_Add(imgList, './resource/icon/text.png')
ExplorerContextActions := [
['Icon' 0, '复制路径', _copyPath],
['Icon' 0, '复制文本', _copyText],
['Icon' 1, '以vscode打开', _openWithVsCode],
['Icon' 2, '以typora打开', _openWithTypora],
['Icon' 3, '以记事本打开', _openWithNotepad],
]
selected := []
ShowContextMenu() {
try id := WinGetID('A')
catch {
Tip.ShowTip('Error on get id')
return
}
if !Explorer.IsValidHwnd(id)
return
window := Explorer(WinGetID('A'))
global selected := window.GetListViewSelected()
if !selected.Length
return
ExplorerContext.Show(imgList, ExplorerContextActions)
}
_openWithVsCode() {
if selected.Length
Run(A_ComSpec ' /c code ' selected[1])
}
_openWithTypora() {
if selected.Length
Run(A_ComSpec Format(' /c F:\Typore\Typora\Typora.exe "{}"', selected[1]), , 'Hide')
}
_openWithNotepad() {
if selected.Length
Run('notepad ' selected.join(A_Space))
}
_copyPath() {
if selected.Length
A_Clipboard := '', A_Clipboard := selected[1], Tip.ShowTip('Copied!')
}
_copyText() {
if selected.Length {
_p := selected[1]
_t := FileRead(_p, 'utf-8')
A_Clipboard := '', A_Clipboard := _t, Tip.ShowTip('Copied!')
}
}
class ExplorerContext extends Gui {
static Width := 150
cbs := Map()
__New(imageList := '', actions := []) {
super.__New('+AlwaysOnTop +ToolWindow -Caption')
this.SetFont('s14', 'consolas')
lv := this.AddListView('w' ExplorerContext.Width ' Grid -Multi -Hdr Lv0x2000 LV0x8000', ['items'])
lv.OnEvent('Click', (p*) => this.OnLvClick(p*))
this.lv := lv, lv.SetImageList(imageList)
for v in actions
this.LvAdd(v[1], v[2], v[3])
Theme.Light(this)
}
static Show(imgList, actions) {
static ins := ExplorerContext(imgList, actions)
Animation.FadeIn(ins, (*) => ins.Modify(), , 8)
Hotkey '~LButton Up', (*) => ins._Hide(), 'On'
}
Modify() => (lv := this.lv, this.GetPos(&x, &y, &w, &h), lv.Move(3, 3, w - 6, h - 6), lv.ModifyCol(1, w - 10))
LvAdd(opts, col, callback) => (this.lv.Add(opts, col), this.cbs.Set(col, callback))
_Hide() => (Animation.FadeOut(this, true), HotKey('~LButton Up', 'Off'))
OnLvClick(lv, row, *) => this.cbs.Get(lv.GetText(row, 1), Noop)()
}
posted on   落寞的雪  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示