├── docs ├── .vuepress │ ├── styles │ │ ├── config.scss │ │ ├── palette.scss │ │ └── index.scss │ ├── public │ │ └── favicon.ico │ ├── navbar.ts │ ├── config.ts │ ├── sidebar.ts │ └── theme.ts ├── images │ ├── plugin.jpg │ ├── plugin-2.jpg │ ├── setting-1.jpg │ ├── setting-2.jpg │ ├── search-box.jpg │ ├── virustotal.jpg │ ├── edit-startup-mode.jpg │ ├── intelligent-mode.jpg │ └── edit-intelligent-mode.jpg ├── guide │ ├── boot │ │ ├── boot.jpg │ │ └── README.md │ ├── about │ │ ├── about.jpg │ │ └── README.md │ ├── startup │ │ ├── edit.jpg │ │ ├── search.jpg │ │ └── README.md │ ├── intelligent │ │ ├── search.jpg │ │ ├── usage.jpg │ │ ├── README.md │ │ ├── run-with.jpg │ │ ├── usage.md │ │ ├── edit.md │ │ └── concept.md │ ├── user-data │ │ ├── export.jpg │ │ ├── import.jpg │ │ └── README.md │ ├── get-started │ │ ├── README.md │ │ ├── intro.md │ │ └── install-usage.md │ ├── plugin │ │ └── README.md │ ├── README.md │ └── setting │ │ └── README.md ├── plugin │ ├── images │ │ ├── ocr-1.jpg │ │ ├── ocr-2.jpg │ │ ├── file-find.jpg │ │ ├── web-search-1.jpg │ │ ├── web-search-2.jpg │ │ ├── timed-reminder-1.jpg │ │ ├── timed-reminder-2.jpg │ │ ├── window-switcher-1.jpg │ │ ├── window-switcher-2.jpg │ │ ├── window-switcher-3.jpg │ │ ├── win10-taskbar-transparent.jpg │ │ ├── file-selection-dialog-nav-1.jpg │ │ └── file-selection-dialog-nav-2.jpg │ ├── README.md │ ├── win10-taskbar-transparent.md │ ├── timed-reminder.md │ ├── web-search.md │ ├── file-selection-dialog-nav.md │ ├── window-switcher.md │ ├── file-find.md │ └── ocr.md ├── dev │ ├── images │ │ ├── startup-1.jpg │ │ ├── startup-2.jpg │ │ ├── startup-3.jpg │ │ ├── startup-4.jpg │ │ ├── startup-5.jpg │ │ ├── code-prompt.jpg │ │ ├── intelligent-1.jpg │ │ ├── intelligent-2.jpg │ │ ├── intelligent-3.jpg │ │ ├── intelligent-4.jpg │ │ ├── intelligent-5.jpg │ │ ├── intelligent-6.jpg │ │ ├── myplugin-gui.jpg │ │ ├── myplugin-menu.jpg │ │ ├── plugin-mode-1.jpg │ │ ├── plugin-mode-2.jpg │ │ ├── plugin-mode-3.jpg │ │ ├── plugin-mode-4.jpg │ │ ├── plugin-mode-5.jpg │ │ ├── plugin-mode-6.jpg │ │ ├── plugin-mode-7.jpg │ │ ├── plugin-mode-8.jpg │ │ ├── plugin-mode-9.jpg │ │ ├── plugin-mode-10.jpg │ │ ├── plugin-mode-11.jpg │ │ └── myplugin-startup-item.jpg │ ├── others │ │ ├── README.md │ │ ├── ahk-gui.md │ │ └── image-put-doc-1.md │ ├── plugin-mode │ │ ├── README.md │ │ ├── basics.md │ │ └── advanced.md │ ├── intelligent │ │ ├── README.md │ │ ├── basics.md │ │ └── advanced.md │ ├── README.md │ ├── image-put.md │ └── startup.md ├── api │ ├── utils │ │ ├── README.md │ │ ├── other.md │ │ └── ImagePutHelper.md │ ├── README.md │ ├── pluginMenu.md │ ├── searchGuiHwnd.md │ ├── imgDir.md │ ├── placeholder.md │ ├── searchText.md │ ├── getPluginMode.md │ ├── pastedContentType.md │ ├── addEntryFunc.md │ ├── hideSearchGui.md │ ├── workWinInfo.md │ ├── getPluginIconBase64.md │ ├── getPluginHIcon.md │ ├── setSearchTextSel.md │ ├── winInfoMatchFlag.md │ ├── pastedContent.md │ ├── addPluginToStartupMode.md │ ├── addPluginToIntelligentMode.md │ └── showPluginMode.md ├── README.md └── history.md ├── 创建快捷方式.bat ├── resource └── img │ ├── AI.png │ ├── add.ico │ ├── img.png │ ├── run.ico │ ├── txt.png │ ├── copy.ico │ ├── delete.ico │ ├── edit.ico │ ├── error.png │ ├── folder.ico │ ├── folder.png │ ├── help.png │ ├── info.ico │ ├── link.ico │ ├── noImg.png │ ├── plugin.ico │ ├── plugin.png │ ├── reload.ico │ ├── Starter.ico │ ├── Starter.png │ ├── program.png │ ├── setting.ico │ └── autohotkey.png ├── src ├── AutoHotkey64.exe ├── Utils │ ├── 7Zip │ │ └── 7-zip64.dll │ ├── LoadIconFromBase64.ah2 │ ├── IME.ah2 │ ├── QuickSort.ah2 │ ├── MapArrClone.ah2 │ ├── DataHelper.ah2 │ ├── Start.ah2 │ ├── GetSelectedText.ah2 │ ├── UrlEncode.ah2 │ ├── ChineseFirstChar.ah2 │ ├── CopyToClipboard.ah2 │ ├── EditCtrlFunc.ah2 │ ├── Class_Loader.ah2 │ ├── HotkeyHelper.ah2 │ ├── GlobalData.ah2 │ └── JXON.ah2 ├── Plugin │ ├── Everything │ │ ├── Everything32.dll │ │ └── Everything64.dll │ ├── win10任务栏透明.ahk │ ├── 定时提醒.ahk │ ├── 文件选择对话框导航.ahk │ ├── 窗口切换.ahk │ └── demo.ahk ├── Gui │ ├── AboutGui.ah2 │ ├── BootGui.ah2 │ ├── PluginGui.ah2 │ └── TrayMenu.ah2 └── Starter.ah2 ├── .gitignore ├── CreateShortcut.vbs ├── package.json ├── .github └── workflows │ ├── sync2gitee.yml │ └── deploy-docs.yml └── README.md /docs/.vuepress/styles/config.scss: -------------------------------------------------------------------------------- 1 | $code-bg-color: #23272e; 2 | $code-color: #ffffff; -------------------------------------------------------------------------------- /创建快捷方式.bat: -------------------------------------------------------------------------------- 1 | echo Creating... 2 | start CreateShortcut.vbs 3 | echo Finished! 4 | -------------------------------------------------------------------------------- /resource/img/AI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/AI.png -------------------------------------------------------------------------------- /resource/img/add.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/add.ico -------------------------------------------------------------------------------- /resource/img/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/img.png -------------------------------------------------------------------------------- /resource/img/run.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/run.ico -------------------------------------------------------------------------------- /resource/img/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/txt.png -------------------------------------------------------------------------------- /src/AutoHotkey64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/src/AutoHotkey64.exe -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.scss: -------------------------------------------------------------------------------- 1 | // you can change colors here 2 | $theme-color: #4daf7c; 3 | -------------------------------------------------------------------------------- /docs/images/plugin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/plugin.jpg -------------------------------------------------------------------------------- /resource/img/copy.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/copy.ico -------------------------------------------------------------------------------- /resource/img/delete.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/delete.ico -------------------------------------------------------------------------------- /resource/img/edit.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/edit.ico -------------------------------------------------------------------------------- /resource/img/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/error.png -------------------------------------------------------------------------------- /resource/img/folder.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/folder.ico -------------------------------------------------------------------------------- /resource/img/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/folder.png -------------------------------------------------------------------------------- /resource/img/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/help.png -------------------------------------------------------------------------------- /resource/img/info.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/info.ico -------------------------------------------------------------------------------- /resource/img/link.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/link.ico -------------------------------------------------------------------------------- /resource/img/noImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/noImg.png -------------------------------------------------------------------------------- /resource/img/plugin.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/plugin.ico -------------------------------------------------------------------------------- /resource/img/plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/plugin.png -------------------------------------------------------------------------------- /resource/img/reload.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/reload.ico -------------------------------------------------------------------------------- /docs/guide/boot/boot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/boot/boot.jpg -------------------------------------------------------------------------------- /docs/images/plugin-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/plugin-2.jpg -------------------------------------------------------------------------------- /docs/images/setting-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/setting-1.jpg -------------------------------------------------------------------------------- /docs/images/setting-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/setting-2.jpg -------------------------------------------------------------------------------- /resource/img/Starter.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/Starter.ico -------------------------------------------------------------------------------- /resource/img/Starter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/Starter.png -------------------------------------------------------------------------------- /resource/img/program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/program.png -------------------------------------------------------------------------------- /resource/img/setting.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/setting.ico -------------------------------------------------------------------------------- /docs/guide/about/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/about/about.jpg -------------------------------------------------------------------------------- /docs/guide/startup/edit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/startup/edit.jpg -------------------------------------------------------------------------------- /docs/images/search-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/search-box.jpg -------------------------------------------------------------------------------- /docs/images/virustotal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/virustotal.jpg -------------------------------------------------------------------------------- /docs/plugin/images/ocr-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/ocr-1.jpg -------------------------------------------------------------------------------- /docs/plugin/images/ocr-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/ocr-2.jpg -------------------------------------------------------------------------------- /resource/img/autohotkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/resource/img/autohotkey.png -------------------------------------------------------------------------------- /src/Utils/7Zip/7-zip64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/src/Utils/7Zip/7-zip64.dll -------------------------------------------------------------------------------- /docs/dev/images/startup-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/startup-1.jpg -------------------------------------------------------------------------------- /docs/dev/images/startup-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/startup-2.jpg -------------------------------------------------------------------------------- /docs/dev/images/startup-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/startup-3.jpg -------------------------------------------------------------------------------- /docs/dev/images/startup-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/startup-4.jpg -------------------------------------------------------------------------------- /docs/dev/images/startup-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/startup-5.jpg -------------------------------------------------------------------------------- /docs/dev/others/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 其他内容 3 | icon: ellipsis 4 | index: false 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/guide/startup/search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/startup/search.jpg -------------------------------------------------------------------------------- /docs/.vuepress/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/.vuepress/public/favicon.ico -------------------------------------------------------------------------------- /docs/dev/images/code-prompt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/code-prompt.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-1.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-2.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-3.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-4.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-5.jpg -------------------------------------------------------------------------------- /docs/dev/images/intelligent-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/intelligent-6.jpg -------------------------------------------------------------------------------- /docs/dev/images/myplugin-gui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/myplugin-gui.jpg -------------------------------------------------------------------------------- /docs/dev/images/myplugin-menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/myplugin-menu.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-1.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-2.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-3.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-4.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-5.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-6.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-7.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-8.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-9.jpg -------------------------------------------------------------------------------- /docs/dev/plugin-mode/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件模式 3 | icon: list-ul 4 | index: false 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/guide/intelligent/search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/intelligent/search.jpg -------------------------------------------------------------------------------- /docs/guide/intelligent/usage.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/intelligent/usage.jpg -------------------------------------------------------------------------------- /docs/guide/user-data/export.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/user-data/export.jpg -------------------------------------------------------------------------------- /docs/guide/user-data/import.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/user-data/import.jpg -------------------------------------------------------------------------------- /docs/images/edit-startup-mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/edit-startup-mode.jpg -------------------------------------------------------------------------------- /docs/images/intelligent-mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/intelligent-mode.jpg -------------------------------------------------------------------------------- /docs/plugin/images/file-find.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/file-find.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-10.jpg -------------------------------------------------------------------------------- /docs/dev/images/plugin-mode-11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/plugin-mode-11.jpg -------------------------------------------------------------------------------- /docs/dev/intelligent/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件智能项 3 | icon: face-laugh 4 | index: false 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/guide/get-started/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 快速上手 3 | icon: lightbulb 4 | index: false 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/guide/intelligent/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 智能模式 3 | icon: face-laugh 4 | index: false 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /docs/guide/intelligent/run-with.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/guide/intelligent/run-with.jpg -------------------------------------------------------------------------------- /docs/plugin/images/web-search-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/web-search-1.jpg -------------------------------------------------------------------------------- /docs/plugin/images/web-search-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/web-search-2.jpg -------------------------------------------------------------------------------- /docs/images/edit-intelligent-mode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/images/edit-intelligent-mode.jpg -------------------------------------------------------------------------------- /src/Plugin/Everything/Everything32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/src/Plugin/Everything/Everything32.dll -------------------------------------------------------------------------------- /src/Plugin/Everything/Everything64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/src/Plugin/Everything/Everything64.dll -------------------------------------------------------------------------------- /docs/plugin/images/timed-reminder-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/timed-reminder-1.jpg -------------------------------------------------------------------------------- /docs/plugin/images/timed-reminder-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/timed-reminder-2.jpg -------------------------------------------------------------------------------- /docs/plugin/images/window-switcher-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/window-switcher-1.jpg -------------------------------------------------------------------------------- /docs/plugin/images/window-switcher-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/window-switcher-2.jpg -------------------------------------------------------------------------------- /docs/plugin/images/window-switcher-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/window-switcher-3.jpg -------------------------------------------------------------------------------- /docs/dev/images/myplugin-startup-item.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/dev/images/myplugin-startup-item.jpg -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.scss: -------------------------------------------------------------------------------- 1 | .shields { 2 | text-align: center !important; 3 | 4 | img { 5 | margin: 0.1rem; 6 | } 7 | } -------------------------------------------------------------------------------- /docs/plugin/images/win10-taskbar-transparent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/win10-taskbar-transparent.jpg -------------------------------------------------------------------------------- /docs/plugin/images/file-selection-dialog-nav-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/file-selection-dialog-nav-1.jpg -------------------------------------------------------------------------------- /docs/plugin/images/file-selection-dialog-nav-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AkiChase/Starter/HEAD/docs/plugin/images/file-selection-dialog-nav-2.jpg -------------------------------------------------------------------------------- /docs/api/utils/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Utils 3 | author: AkiChase 4 | index: false 5 | --- 6 | 7 | `PluginHelper.Utils` 纯工具类 8 | 9 | -------------------------------------------------------------------------------- /docs/api/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: PluginHelper 3 | author: AkiChase 4 | index: false 5 | --- 6 | 7 | 插件开发工具模块 `PluginHelper` 中的所有API参数及说明 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | resource/data/ 3 | Starter.lnk 4 | src/Plugin/*.txt 5 | 6 | node_modules/ 7 | docs/.vuepress/.cache/ 8 | docs/.vuepress/.temp/ 9 | docs/.vuepress/dist/ 10 | -------------------------------------------------------------------------------- /docs/plugin/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件指南 3 | icon: list-ul 4 | index: false 5 | --- 6 | 7 | 插件指南为用户提供各种插件的**使用帮助**。 8 | 9 | 插件是 **Starter** 最有创造性✨也是最强大的一部分💪 10 | 11 | -------------------------------------------------------------------------------- /docs/api/pluginMenu.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pluginMenu 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-14 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static pluginMenu 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态属性 \{Menu\} 17 | 18 | ## 说明 19 | 20 | **插件菜单**对象,菜单位于:**托盘菜单** > **插件功能** 21 | -------------------------------------------------------------------------------- /docs/dev/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件开发 3 | icon: code 4 | --- 5 | 6 | 开发者,欢迎你。 7 | 8 | 创建一个 `Starter` 插件非常简单,只需要了解一些AHK的基本知识并使用 `Starter` 提供的接口和工具函数。实际上,创建插件与编写普通的AHK程序类似,你也可以把自己常用的脚本转化为 `Starter` 插件,通过 `Starter` 实现更加强大的功能。 9 | 10 | ::: tip 11 | 阅读[快速上手](./get-started/),创建你的第一个专属插件。 12 | ::: -------------------------------------------------------------------------------- /docs/api/searchGuiHwnd.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: searchText 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static searchText { 12 | get => SearchGui.gui.Hwnd 13 | } 14 | ``` 15 | 16 | ## 类型 17 | 18 | 动态属性 \{Int\} 19 | 20 | ## 说明 21 | 22 | **获取**搜索界面hwnd。 -------------------------------------------------------------------------------- /docs/api/imgDir.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: imgDir 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static imgDir => GlobalData.imgDir 12 | ``` 13 | 14 | ## 类型 15 | 16 | 动态属性 \{String\} 17 | 18 | ## 说明 19 | 20 | 返回图片资源的目录路径。 21 | 22 | ## 返回值 {String} 23 | 24 | 返回字符串类型,代表 **Starter** 图片资源的目录路径。 -------------------------------------------------------------------------------- /docs/plugin/win10-taskbar-transparent.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Win10任务栏透明 3 | author: AkiChase 4 | date: 2023-04-11 5 | --- 6 | 7 | 将Win10任务栏透明化,仅此而已 8 | 9 | ::: warning 10 | 打开开始菜单后透明状态会暂时消失,**且仅支持Win10** 11 | ::: 12 | 13 | 右键Starter右下角托盘图标:**Starter菜单** > **插件功能** > **Win10任务栏透明** > **开启** 14 | 15 | ![网页搜索:主界面](./images/win10-taskbar-transparent.jpg) -------------------------------------------------------------------------------- /docs/plugin/timed-reminder.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 定时提醒 3 | author: AkiChase 4 | date: 2023-04-11 5 | --- 6 | 7 | ## 介绍 8 | 9 | 隔一段时间提醒一下坐在电脑前的你,该喝水\起身\休息啦! 10 | 11 | ![定时提醒:主界面](./images/timed-reminder-1.jpg) 12 | 13 | ### 1.2 配置 14 | 15 | 右键Starter右下角托盘图标:**Starter菜单** > **插件功能** > **定时提醒** > **设置 | 开启** 16 | 17 | ![定时提醒:配置界面](./images/timed-reminder-2.jpg) -------------------------------------------------------------------------------- /docs/api/placeholder.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: placeholder 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static placeholder { 12 | get => SearchGui.placeholder 13 | set => SearchGui.placeholder := Value 14 | } 15 | ``` 16 | 17 | ## 类型 18 | 19 | 动态属性 \{String\} 20 | 21 | ## 说明 22 | 23 | **获取**、**设置**搜索框的占位符。占位符是搜索框未输入搜索内容时的显示内容。 -------------------------------------------------------------------------------- /docs/api/searchText.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: searchText 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static searchText { 12 | get => SearchGui.searchText 13 | set => SearchGui.edit.Value := SearchGui.searchText := value 14 | } 15 | ``` 16 | 17 | ## 类型 18 | 19 | 动态属性 \{String\} 20 | 21 | ## 说明 22 | 23 | **获取**、**设置**搜索框当前的文本内容。 -------------------------------------------------------------------------------- /docs/.vuepress/navbar.ts: -------------------------------------------------------------------------------- 1 | import { navbar } from "vuepress-theme-hope"; 2 | 3 | export const zhNavbar = navbar([ 4 | { text: "使用指南", icon: "lightbulb", link: "/guide/" }, 5 | { text: "插件指南", icon: "list-ul", link: "/plugin/" }, 6 | { text: "插件开发", icon: "code", link: "/dev/" }, 7 | { text: "API", icon: "screwdriver-wrench", link: "/api/" }, 8 | { text: "更新日志", icon: "clock", link: "/history" } 9 | ]); 10 | -------------------------------------------------------------------------------- /docs/api/getPluginMode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: getPluginMode 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static getPluginMode(*) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态函数 17 | 18 | ## 说明 19 | 20 | 返回 `PluginMode` 类 21 | 22 | 特定情况下需要提前获取到PluginMode,即所谓的that。 23 | 24 | ## 参数 25 | 26 | - `*`: 具体使用时可以传入任何参数,此参数在该函数中没有任何用途。 27 | 28 | ## 返回值 \{PluginMode\} 29 | 30 | 返回 `PluginMode` 类 -------------------------------------------------------------------------------- /docs/api/pastedContentType.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pastedContentType 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static pastedContentType => SearchGui._pastedContentType 12 | ``` 13 | 14 | ## 类型 15 | 16 | 动态属性 \{String\} 17 | 18 | ## 说明 19 | 20 | 返回**当前搜索框**粘贴的内容的**类型**,或者按下**智能搜索框**时选中的内容的**类型**。可返回 "file", "bitmap", "text"。 21 | 22 | ## 返回值 \{String\} 23 | 24 | 返回字符串类型,代表粘贴的内容类型。可返回 "file", "bitmap", "text"。 -------------------------------------------------------------------------------- /docs/api/addEntryFunc.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: addEntryFunc 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-14 6 | --- 7 | ## 定义 8 | 9 | ```ahk 10 | static PluginHelper.addEntryFunc(f) 11 | ``` 12 | 13 | ## 类型 14 | 15 | 静态方法 16 | 17 | ## 说明 18 | 19 | 添加插件入口函数到执行队列,等待基本功能初始化后,函数将被执行 20 | 21 | ## 参数 22 | 23 | - ### f \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} 24 | 25 | ```ahk 26 | (*) => Any 27 | ``` 28 | 29 | 插件**入口函数**,也可以称为插件的**初始化函数** -------------------------------------------------------------------------------- /docs/api/hideSearchGui.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: hideSearchGui 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static hideSearchGui(recordHideTime:=false) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态方法 17 | 18 | ## 说明 19 | 20 | 隐藏搜索框。 21 | 22 | 如果 recordHideTime 值为 true,则会记录隐藏时间,15s内按**呼出搜索框**快捷键可恢复窗口。 23 | 24 | ## 参数 25 | 26 | - ### recordHideTime \{Bool\} 27 | 28 | 默认为 `false`。如果设置为 `true`,则会记录隐藏时间,15s内按**呼出搜索框**快捷键可恢复搜索框内容。 -------------------------------------------------------------------------------- /docs/api/workWinInfo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: workWinInfo 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static workWinInfo => SearchGui.workWinInfo 12 | ``` 13 | 14 | ## 类型 15 | 16 | 动态属性 \{Object\} 17 | 18 | ## 说明 19 | 20 | 返回当前活动窗口的相关信息。 21 | 22 | ## 返回值 \{Object\} 23 | 24 | 返回一个包含活动窗口信息的对象。对象包含以下属性: 25 | 26 | - `hwnd`: 窗口句柄 27 | - `title`: 窗口标题 28 | - `class`: 窗口类名 29 | - `processPath`: 程序路径 30 | - `processName`: 程序名称 -------------------------------------------------------------------------------- /src/Utils/LoadIconFromBase64.ah2: -------------------------------------------------------------------------------- 1 | LoadIconFromBase64(base64, W := 0, H := 0) 2 | { 3 | nBytes := Floor((B64Len := StrLen(base64 := RTrim(base64, "="))) * 3 / 4) 4 | buf := Buffer(nBytes) 5 | 6 | DllCall("Crypt32.dll\CryptStringToBinary", "str", base64, "int", B64Len, "int", 1, "ptr", buf, "uintp", nBytes, "Int", 0, "Int", 0) 7 | Return DllCall("User32.dll\CreateIconFromResourceEx", "ptr", buf, "int", nBytes, "int", 1, "int", 0x30000, "Int", W, "Int", H, "Int", 0, "ptr") 8 | } -------------------------------------------------------------------------------- /docs/guide/plugin/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件界面 3 | icon: list-ul 4 | author: AkiChase 5 | date: 2023-04-11 6 | --- 7 | 8 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**插件界面**。 9 | 10 | ![插件界面](../../images/plugin-2.jpg) 11 | 12 | - **双击**列表项可以启用/禁用插件 13 | - 点击**保存重载**,可以保存当前启用/禁用状态,然后重启软件以加载启用的插件 14 | - 点击**刷新列表**,可以重新读取插件目录内的插件信息,刷新插件界面列表 15 | - 插件使用帮助,请移步[插件指南](../../plugin/) 16 | - 自定义插件开发,请移步[插件开发](../../dev/) 17 | 18 | ::: tip 19 | 请不要过分贪图 `ALL IN ONE`, 加载到`Starter`内的插件过多必然会影响程序的运行速度。 20 | ::: -------------------------------------------------------------------------------- /docs/plugin/web-search.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 网页搜索 3 | author: AkiChase 4 | date: 2023-04-11 5 | order: 1 6 | --- 7 | 8 | ## 介绍 9 | 10 | 进行百度、必应、谷歌网页搜索,输入搜索内容时显示搜索联想词。 11 | 12 | ![网页搜索:主界面](./images/web-search-1.jpg) 13 | 14 | ## 配置 15 | 16 | 右键 **Starter** 右下角托盘图标 > **Starter菜单** > **插件功能** > **网页搜索** > **设置** 17 | 18 | **谷歌搜索**国内需要使用代理,通过本地socket代理的方式连通网络。 19 | 20 | 如果有需要可以考虑[蓝色海洋](https://abcloud365.xyz/index.php#/register?code=lyUxxqMu),配合Clash使用 21 | 22 | ![网页搜索:配置](./images/web-search-2.jpg) -------------------------------------------------------------------------------- /docs/api/getPluginIconBase64.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: getPluginIconBase64 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static getPluginIconBase64(pluginName) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态方法 17 | 18 | ## 说明 19 | 20 | 获取指定插件的图标的无头部 `Base64` 21 | 22 | 如果插件存在图标则返回无头部 `Base64`,如果插件不存在图标则返回 0。 23 | 24 | ## 参数 25 | 26 | - ### pluginName \{String\} 27 | 28 | 插件名称,即插件文件名。 29 | 30 | ## 返回值 \{(String|Int)\} 31 | 32 | 如果插件存在图标则返回无头部 `Base64`,如果插件不存在图标则返回 0。 -------------------------------------------------------------------------------- /docs/guide/about/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 关于界面 3 | icon: circle-info 4 | author: AkiChase 5 | date: 2023-04-11 6 | --- 7 | 8 | ## 关于界面 9 | 10 | ![自启界面](./about.jpg) 11 | 12 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**关于界面**。 13 | 14 | - 显示当前版本号 15 | - 显示项目主页 16 | - 检查更新按钮 17 | 18 | ## 检查更新 19 | 20 | 软件启动时会进行一次检查更新,也可以通过自启界面的检查更新按钮来手动检查。 21 | 22 | 更新需要前往项目主页,或者其他版本发布地址手动下载最新版本。 23 | 24 | 更新版本前记得导出旧版的数据文件,以导入新版本中。 25 | 26 | ::: tip 27 | 考虑到很多人无法访问GitHub,所以检查更新基于同步的Gitee仓库的Tags 28 | 29 | 效果一般,仅仅能检测版本号 30 | ::: -------------------------------------------------------------------------------- /docs/api/getPluginHIcon.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: getPluginHIcon 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static PluginHelper.getPluginHIcon(pluginName) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态方法 17 | 18 | ## 说明 19 | 20 | 获取指定插件的图标 `hIcon` 21 | 22 | :::warning 23 | 使用 `hIcon` 时请加 `*` 以使用副本,如 `hIcon:* 123456`,避免原图标句柄用后销毁 24 | ::: 25 | 26 | ## 参数 27 | 28 | - ### pluginName \{String\} 29 | 30 | 插件名称,即插件文件名、插件id 31 | 32 | ## 返回值 \{Int\} 33 | 34 | 如果插件有图标,则返回hIcon,否则返回0 -------------------------------------------------------------------------------- /CreateShortcut.vbs: -------------------------------------------------------------------------------- 1 | set WshShell=WScript.CreateObject("WScript.Shell") 2 | Set Ws = CreateObject("Scripting.FileSystemObject") 3 | '获取当前脚本路径 4 | currentpath = Ws.GetFile(Wscript.ScriptFullName).ParentFolder.Path 5 | set oShellLink=WshShell.CreateShortcut(currentpath & "\Starter.lnk") 6 | oShellLink.TargetPath= currentpath & "\src\AutoHotkey64.exe" 7 | oShellLink.Arguments = "Starter.ah2" 8 | oShellLink.IconLocation= currentpath & "\resource\img\Starter.ico" 9 | oShellLink.WorkingDirectory=currentpath & "\src" 10 | oShellLink.Save -------------------------------------------------------------------------------- /docs/guide/user-data/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 用户数据 3 | icon: user 4 | author: AkiChase 5 | date: 2023-04-11 6 | --- 7 | 8 | 右键右下角托盘图标打开软件菜单,点击**用户数据**进入**子菜单** 9 | 10 | ## 打开数据目录 11 | 12 | 打开用户数据所在目录 13 | 14 | ## 导出用户数据 15 | 16 | ![导出用户数据](./export.jpg) 17 | 18 | 进入导出用户数据界面,选择保存目录、导出选项,将相关内容导出为 **zip** 文件 19 | 20 | ## 导入用户数据 21 | 22 | ![导入用户数据](./import.jpg) 23 | 24 | 进入导入用户数据界面,选择数据路径、导入选项,将相关内容导入至 **Starter** 25 | 26 | ::: warning 27 | 导入的内容会直接覆盖同名文件,担心误操作请提前导出数据作为备份。 28 | 29 | 目前导出时请不要覆盖插件,而是手动复制插件的配置文件,粘贴到新版本插件目录内。 30 | ::: -------------------------------------------------------------------------------- /docs/api/setSearchTextSel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: setSearchTextSel 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static setSearchTextSel(start := 0, end := -1) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态方法 17 | 18 | ## 说明 19 | 20 | 设置搜索框的选中文本区域。 21 | 22 | ## 参数 23 | 24 | - `start`: 选中文本区域的开始位置,默认值为 `0`,表示从第一个文本之前开始选中。 25 | - `end`: 选中文本区域的结束位置,默认值为 `-1`,表示选中最后一个文本。 26 | 27 | :::tip 28 | 当 `start` 和 `end` 的值相同时,表示将游标移动到指定位置。 29 | 30 | 使用 `setSearchTextSel(StrLen(searchText), -1)` 将游标移动到最后 31 | ::: -------------------------------------------------------------------------------- /docs/api/winInfoMatchFlag.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: winInfoMatchFlag 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static winInfoMatchFlag => SearchGui.winInfoMatchFlag 12 | ``` 13 | 14 | ## 类型 15 | 16 | 动态属性 \{Bool\} 17 | 18 | ## 说明 19 | 20 | 返回一个布尔值,表示当前是否处于工作窗口模式。 21 | 22 | :::tip 23 | 工作窗口模式是按下**智能搜索**快捷键时,若当前**没有选中任何内容**,则**Starter**将获取工作窗口信息,进入工作窗口模式下的智能模式搜索框。 24 | 25 | 搜索框会短暂显示工作窗口的信息,插件可以根据此时的工作窗口信息进行匹配 26 | ::: 27 | 28 | ## 返回值 \{Bool\} 29 | 30 | 返回一个布尔值,表示当前是否处于工作窗口模式。如果处于工作窗口模式,则返回 `true`,否则返回 `false`。 -------------------------------------------------------------------------------- /docs/api/pastedContent.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: pastedContent 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static pastedContent => SearchGui._pastedContent 12 | ``` 13 | 14 | ## 类型 15 | 16 | 动态属性 \{(Array|ImagePutBuffer|String)\} 17 | 18 | ## 说明 19 | 20 | 返回**当前搜索框**粘贴的内容,或者按下**智能搜索框**时选中的内容。 21 | 22 | 根据[pastedContentType](./pastedContentType.md)的不同,可返回文件路径数组,ImagePut缓冲对象ImagePutBuffer、字符串。 23 | 24 | 25 | ## 返回值 \{(Array|Int|String)\} 26 | 27 | 根据[pastedContentType](./pastedContentType.md)的不同,可返回文件路径数组,ImagePutBuffer、字符串。 -------------------------------------------------------------------------------- /docs/guide/intelligent/usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用 3 | icon: keyboard 4 | order: 3 5 | author: AkiChase 6 | date: 2023-04-11 7 | --- 8 | 9 | ![使用](./usage.jpg) 10 | 11 | ## 进入智能模式 12 | 13 | 进入智能模式有几种方式 14 | 15 | 1. 按下 `呼出搜索框` 快捷键,呼出**启动模式搜索框**,按 `Tab` 键切换到智能模式。 16 | 17 | 2. 选中任意内容,按下 `智能搜索框` 快捷键,将获取选中内容,切换到智能模式,将选中内容输入搜索框。 18 | 19 | 3. 当剪切板中复制有文件、位图时,呼出**启动模式搜索框**,粘贴后,将切换到智能模式。 20 | 21 | ::: warning 22 | 只有仅输入文本时,才可能匹配到**原生智能项**,可以通过 `Esc`键,清除其他输入内容。 23 | 24 | 第2种,第3种方式一般都是用于快速匹配相关的**插件智能项**的。 25 | ::: 26 | 27 | ## 启动智能项 28 | 29 | 在搜索界面中,操作方式和启动模式下基本相同,只是没有了双击 `Right` 在文件夹中打开的功能。 -------------------------------------------------------------------------------- /src/Utils/IME.ah2: -------------------------------------------------------------------------------- 1 | ; IME_GET 获取输入法状态 2 | ; 返回 0 英文 3 | ; 返回 1 中文 4 | IME_GET() { 5 | return DllCall("SendMessage" 6 | , "UInt", DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", WinActive("A")) 7 | , "UInt", 0x0283 ;Message : WM_IME_CONTROL 8 | , "Int", 0x0005 ;wParam : IMC_GETOPENSTATUS 9 | , "Int", 0) 10 | } 11 | 12 | ; IME_SET 设置输入法状态 13 | ; state := 0 英文 14 | ; state := 1 中文 15 | IME_SET(state := 0) { 16 | return DllCall("SendMessage" 17 | , "UInt", DllCall("imm32\ImmGetDefaultIMEWnd", "Uint", WinActive("A")) 18 | , "UInt", 0x0283 ;Message : WM_IME_CONTROL 19 | , "Int", 0x006 ;wParam : IMC_SETOPENSTATUS 20 | , "Int", state) ;lParam : 0 or 1 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter", 3 | "version": "0.4.0", 4 | "description": "starter docs", 5 | "license": "GPL-3.0", 6 | "type": "module", 7 | "scripts": { 8 | "docs:build": "vuepress build docs", 9 | "docs:clean-dev": "vuepress dev docs --clean-cache", 10 | "docs:dev": "vuepress dev docs", 11 | "docs:update-package": "pnpm dlx vp-update" 12 | }, 13 | "devDependencies": { 14 | "@vuepress/client": "2.0.0-beta.61", 15 | "@vuepress/plugin-shiki": "2.0.0-beta.61", 16 | "vue": "^3.2.47", 17 | "vuepress": "2.0.0-beta.61", 18 | "vuepress-plugin-auto-catalog": "2.0.0-beta.202", 19 | "vuepress-plugin-search-pro": "2.0.0-beta.202", 20 | "vuepress-theme-hope": "2.0.0-beta.202" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/plugin/file-selection-dialog-nav.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 文件选择对话框导航 3 | author: AkiChase 4 | date: 2023-04-12 5 | --- 6 | 7 | ## 介绍 8 | 9 | 文件对话框中路径导航功能,模仿Listary的 `Ctrl+G` 功能 10 | 11 | 让文件对话框导航到**资源管理器**中正在浏览的文件夹。 12 | 13 | ![文件选择对话框导航:主界面](./images/file-selection-dialog-nav-1.jpg) 14 | 15 | ## 使用方式 16 | 17 | 当你正处于某个文件选择对话框时,按下 `智能搜索框` 快捷键(不能选中任何内容),进入**工作窗口模式**的智能搜索框 18 | 19 | 在搜索结果中可以看到**文件选择对话框路径导航**,启动此**插件智能项**即可进入插件界面 20 | 21 | 插件界面的列表中会列举所有正在运行的**资源管理器**中正在浏览的文件夹,**输入内容到搜索框可以对其进行筛选** 22 | 23 | 筛选依据为:输入的内容是否为列表项**标题**或者**将标题内中文转拼音首字母后的标题**的一部分 24 | 25 | 最后,启动某列表项即可完成导航到指定路径。 26 | 27 | ![文件选择对话框导航:导航提示](./images/file-selection-dialog-nav-2.jpg) 28 | 29 | ::: warning 30 | 按下 `智能搜索框` 快捷键之前,请不要选中任何文件或者文本,否则插件智能项不会出现在列表中(因为无法进入工作窗口模式) 31 | ::: 32 | -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 使用指南 3 | icon: lightbulb 4 | --- 5 | 6 | ## 从这里开始 7 | 8 | 在开始使用 Starter 之前,建议先阅读[快速上手](./get-started/intro),以便快速了解软件的基本功能和操作。 9 | 10 | 若想深入了解软件的**所有功能**,可以查阅使用指南的其他内容,其中包含了详细的介绍和操作说明。 11 | 12 | ::: tip 13 | 若在使用 Starter 过程中遇到无法解决的问题,欢迎前往 [Issues](https://github.com/AkiChase/Starter/issues) 提交问题报告。 14 | 15 | 在提交问题报告时,请尽可能提供详细的信息,例如出错时的错误提示、错误复现流程和软件版本等,以便更好地解决问题。感谢你的支持和理解! 16 | ::: 17 | 18 | ## 万能搜索框 ✨ 19 | 20 | ![万能搜索框](../images/search-box.jpg) 21 | 22 | 通过搜索和情景匹配,回应符合要求的内容 23 | ## 轻量 & 绿色 ✨ 24 | 25 | ![软件体积](../images/virustotal.jpg) 26 | 27 | Starter 使用AHK编写,绿色免安装,体积 < 2M 28 | ## 简洁 & 颜值在线 ✨ 29 | 30 | ![智能模式](../images/intelligent-mode.jpg) 31 | 32 | Starter 设计简洁美观,UI的「颜值即正义」 33 | ## 插件化 ✨ 34 | 35 | ![插件化](../images/plugin.jpg) 36 | 37 | 自由装载插件,打造个人专属效率工具 -------------------------------------------------------------------------------- /docs/guide/boot/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 自启界面 3 | icon: desktop 4 | author: AkiChase 5 | date: 2023-04-11 6 | --- 7 | 8 | ## 开机自启动 9 | 10 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**开机启动**,即可切换开机自启状态。 11 | 12 | ::: tip 13 | **Starter** 开机自启通过写入注册表实现 14 | ::: 15 | 16 | ## 自启界面 17 | 18 | ![自启界面](./boot.jpg) 19 | 20 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**自启界面**。 21 | 22 | - Starter**开机自启时**会自动运行自启界面列表中的所有项,**按名称顺序**。 23 | ::: warning 24 | **开启**开机自启时,Starter才会在**开机自启动**时运行自启界面列表中的所有项。 25 | 26 | 因为写入开机自启注册表时为启动 **Starter** 程序附加了 `"the_startup"` 命令行参数 27 | ::: 28 | 29 | - **右键**点击列表打开**菜单**进行添加、刷新、删除操作。 30 | - **拖动文件**到自启界面中可以**批量添加**自启项。 31 | - **双击**列表项可以直接启动对应项。 32 | 33 | ::: tip 34 | 添加的自启项将会创建快捷方式到**用户数据目录**的 `boot` 文件夹下 35 | 36 | 如果有需要改变自启顺序,可以通过修改`boot` 文件夹对应**快捷方式的文件名**来修改顺序 37 | 38 | 比如为快捷方式文件名加上 `01`,`02` 等前缀 39 | ::: -------------------------------------------------------------------------------- /docs/plugin/window-switcher.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 窗口切换 3 | author: AkiChase 4 | date: 2023-05-12 5 | --- 6 | 7 | ## 介绍 8 | 9 | 窗口切换功能,模仿Switcheroo 10 | 11 | 显示当前窗口列表,输入内容对列表进行检索,支持拼音首字母。 12 | 13 | ![窗口切换:无输入内容](./images/window-switcher-1.jpg) 14 | 15 | ![窗口切换:有输入内容](./images/window-switcher-2.jpg) 16 | 17 | 18 | ## 使用方式 19 | 20 | 插件入口在启动模式中,其他关键词为`["CKQH"]`。 21 | 22 | 启动该插件启动项,进入窗口切换插件。 23 | 24 | 当未输入任何内容时,在搜索结果中可以看到当前所有窗口的标题列表,即介绍中的第一张图片。 25 | 26 | 输入任意文本,即可对窗口标题进行检索,**支持拼音首字母**。 27 | 28 | 最后,启动某列表项即可完成切换到指定窗口。 29 | 30 | ## 进阶技巧 31 | 32 | 添加对应的[关键词快捷键](https://AkiChase.github.io/Starter/guide/setting/#控制),可以通过快捷键**快速启动插件**。 33 | 34 | ![窗口切换:关键词快捷键](./images/window-switcher-3.jpg) 35 | 36 | 参考设置如上图,可以通过按下`Alt` + `CapsLK`快捷键来启动该插件。 37 | 38 | :::tip 39 | 如果需要用来代替`Alt` + `Tab`功能,请将**按键类型**设置为**自定义**,**按键内容**设置为`!Tab` 40 | ::: -------------------------------------------------------------------------------- /docs/plugin/file-find.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 文件搜索 3 | author: AkiChase 4 | date: 2023-04-11 5 | order: 2 6 | --- 7 | 8 | ## 介绍 9 | 10 | 调用Everything进行文件搜索。 11 | 12 | ![文件搜索:主界面](./images/file-find.jpg) 13 | 14 | ## 插件项入口 15 | 16 | ### 1.启动模式 17 | 18 | 关键词为`["WJSS", "Everything"]` 19 | 20 | ### 2. 智能模式 21 | 22 | - 输入任意内容,进入插件时保留输入内容 (优先级为 **1**) 23 | - 搜索框带有**单个文件夹**时,进入插件时保留输入内容和文件夹信息 (优先级为 **1**) 24 | - 搜索框带有**单个文件夹**时且输入 `Everything`, `WJSS` 开头或全部的文本, 进入插件时**不保留输入内容**但**保留文件夹信息** (优先级为 **2**) 25 | 26 | ::: tip 27 | 优先级越高,在搜索结果中排序越靠前,且**插件智能项**优先级高于**原生智能项**。 28 | ::: 29 | 30 | ## 使用方式 31 | 32 | 1. 搜索语法见: [Searching - voidtools](https://www.voidtools.com/zh-cn/support/everything/searching/) 33 | 2. 右键任意搜索结果打开功能菜单,进行文件夹中显示、复制、删除、设置等操作 34 | 3. 双击`Right`键也可以打开功能菜单 35 | 4. 搜索框可以拖入、粘贴**单个文件夹**,此时将在该文件夹内搜索 36 | 37 | ::: tip 38 | 相当于加入前缀 `"文件夹路径xxx"` + `空格` 39 | ::: 40 | -------------------------------------------------------------------------------- /src/Utils/QuickSort.ah2: -------------------------------------------------------------------------------- 1 | QuickSort(arr, function) { 2 | if 2 > len := arr.Length 3 | return arr 4 | ranges := [1 | (len << 32)], ranges.Length := len, i := 2 5 | while i > 1 { 6 | range := ranges[--i] 7 | if (start := range & 0xffffffff) >= (end := range >> 32) 8 | continue 9 | mid := arr[(start + end) / 2], left := start, right := end 10 | loop { 11 | while function(arr[left], mid) < 0 12 | ++left 13 | while function(arr[right], mid) > 0 14 | --right 15 | if left <= right 16 | temp := arr[left], arr[left] := arr[right], arr[right] := temp, left++, right-- 17 | } until left > right 18 | if start < right 19 | ranges[i++] := start | right << 32 20 | if end > left 21 | ranges[i++] := left | end << 32 22 | } 23 | return arr 24 | } -------------------------------------------------------------------------------- /src/Utils/MapArrClone.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * MapArrClone(obj) 3 | * AHK v2.x 4 | * Created by AkiChase on 2023.2.17 5 | * 深拷贝 支持值类型和Map、Array引用类型 6 | */ 7 | 8 | MapArrClone(obj) { 9 | m := Map() 10 | _deep(target) { 11 | if (!isObject(target)) ; 非对象直接返回 12 | return target 13 | 14 | if !(target is Array or obj is Map) ; 对象类型仅支持Map和Array 15 | throw Format("Only Array and Map are supported: {}", Type(obj)) 16 | 17 | if (m.Has(target)) ; 存在已递归过的对象,直接返回上次的值,避免循环递归溢出 18 | return m[target] 19 | 20 | if (target is Array) { ; 创建新对象 21 | result := Array() 22 | result.Capacity := target.Capacity 23 | } else 24 | result := Map() 25 | 26 | m[target] := result ; 记录已递归 27 | 28 | for k, v in target 29 | (target is Array) ? result.Push(v) : result[k] := _deep(v) ; 递归 30 | return result 31 | } 32 | return _deep(obj) 33 | } -------------------------------------------------------------------------------- /docs/plugin/ocr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: OCR识别文字 3 | author: AkiChase 4 | date: 2023-04-13 5 | --- 6 | 7 | ## 介绍 8 | 9 | 调用百度OCR在线识别文字,需要自行申请API密钥 10 | 11 | ![文件搜索:主界面](./images/ocr-1.jpg) 12 | 13 | 14 | ## 配置 15 | 16 | 请先申请百度OCR,方法自行上网搜索。 17 | 18 | 右键 **Starter** 右下角托盘图标 > **Starter菜单** > **插件功能** > **OCR识别文字** > **设置** 19 | 20 | 设置界面分别填入申请到的 `id` 和 `secret` ,点击**保存** 21 | 22 | ## 使用 23 | 24 | - ### 方式一 25 | 26 | 使用任意截图工具截取图片到**剪切板**,呼出**搜索框**,粘贴,转到**智能模式搜索框** 27 | 28 | ![文件搜索:搜索框](./images/ocr-2.jpg) 29 | 30 | 启动对应**插件智能项**进入识别界面,并识别图片 31 | 32 | - ### 方式二 33 | 34 | 启动模式搜索框中关键词为`["OCR识别文字", "OCRSBWZ"]` 35 | 36 | 启动对应**插件启动项**进入识别界面 37 | 38 | - ### 方式三 39 | 40 | 右键 **Starter** 右下角托盘图标 > **Starter菜单** > **插件功能** > **OCR识别文字** > **显示**,进入识别界面 41 | 42 | ## 识别界面 43 | 44 | 左侧为正在识别的图片,**不用在意显示的图片变形,仅仅是控件显示问题** 45 | 46 | 右侧为识别结果 47 | 48 | :::tip 49 | **直接粘贴图片**就可以进行图片识别 50 | 51 | **点击左侧图片**位置可以通过文件选择进行图片识别 52 | ::: 53 | 54 | ::: warning 55 | 修改识别结果**连接方式**或者**自动复制**选项时,打开**设置界面**并点击保存, 56 | 57 | **下次启动时才会使用保存的选择** 58 | ::: -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | icon: home 4 | title: 主页 5 | heroImage: /logo.svg 6 | heroImageDark: /logo-tp.svg 7 | heroText: Starter 8 | tagline: 智能、快捷地启动文件和插件应用,提供便利与高效的使用体验✨ 9 | actions: 10 | - text: 使用指南 💡 11 | link: /guide/ 12 | type: primary 13 | 14 | - text: 插件开发 🔧 15 | link: /dev/ 16 | 17 | features: 18 | - title: 万能搜索框 19 | icon: magnifying-glass 20 | details: Starter 通过搜索和情景匹配,回应符合要求的多种内容。 21 | 22 | - title: 轻量 & 绿色 23 | icon: feather-pointed 24 | details: Starter 使用AHK编写,绿色免安装,体积 < 2M 。 25 | 26 | - title: 简洁 & 颜值在线 27 | icon: heart 28 | details: Starter 设计简洁美观,UI的「颜值即正义」。 29 | 30 | - title: 插件化 31 | icon: list-ul 32 | details: Starter 可以自由装载插件,打造个人专属效率工具。 33 | 34 | - title: 插件 - 文件搜索 35 | icon: folder-closed 36 | details: 基于Everything而更便捷的文件秒搜 37 | 38 | - title: 插件 - 网页搜索 39 | icon: cloud 40 | details: 带有联想词的搜索框,支持百度、必应、谷歌搜索 41 | 42 | - title: 更多插件开发中 43 | icon: ellipsis 44 | details: 提供插件开发模板与实例,欢迎参与开源,提交PR 45 | --- 46 | 47 | -------------------------------------------------------------------------------- /docs/.vuepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineUserConfig } from "vuepress"; 2 | import theme from "./theme.js"; 3 | // 搜索插件 4 | import { searchProPlugin } from "vuepress-plugin-search-pro"; 5 | // 自定义代码高亮 6 | import { shikiPlugin } from "@vuepress/plugin-shiki"; 7 | 8 | 9 | import { readFileSync } from 'fs'; 10 | import { join } from 'path'; 11 | 12 | export default defineUserConfig({ 13 | base: "/Starter/", 14 | 15 | locales: { 16 | "/": { 17 | lang: "zh-CN", 18 | title: "Starter文档", 19 | description: "Starter - 极简效率工具", 20 | }, 21 | }, 22 | theme, 23 | plugins: [ 24 | searchProPlugin({ 25 | indexContent: true, 26 | }), 27 | shikiPlugin({ 28 | theme: "one-dark-pro", 29 | langs: [{ 30 | id: "autohotkey", 31 | scopeName: 'source.ahk2', 32 | grammar: JSON.parse(readFileSync(join(__dirname, 'ahk2.tmLanguage.json'), "utf-8")), 33 | aliases: ['ahk', 'ahk2', 'ah2'], 34 | path: join(__dirname, 'ahk2.tmLanguage.json') 35 | }] 36 | }), 37 | ], 38 | }); 39 | 40 | 41 | -------------------------------------------------------------------------------- /.github/workflows/sync2gitee.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: sync2gitee 4 | 5 | # Controls when the workflow will run 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | release: 10 | types: [published] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "repo-sync" 18 | repo-sync: 19 | # The type of runner that the job will run on 20 | runs-on: ubuntu-latest 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | - name: sync-gitee-mirror 25 | uses: abersheeran/sync-gitee-mirror@v1-beta 26 | with: 27 | # Gitee 仓库,例如 abersheeran/gitee-mirror 28 | repository: ${{ github.repository }} 29 | # Gitee 用户名,用于登录。 30 | username: AkiChase 31 | # Gitee 密码,用于登录。 32 | password: ${{ secrets.GITEE_PASSWORD }} 33 | -------------------------------------------------------------------------------- /src/Utils/DataHelper.ah2: -------------------------------------------------------------------------------- 1 | class DataHelper { 2 | static Join(sep, arr) { 3 | for item in arr 4 | str .= item . sep 5 | return SubStr(str, 1, -StrLen(sep)) 6 | } 7 | 8 | ;加载数据 9 | static loadData(path) { 10 | out := [] 11 | Loop read, path ;不使用StrSplit 12 | { 13 | if (StrLen(A_LoopReadLine)) 14 | out.Push(StrSplit(A_LoopReadLine, "¢")) 15 | } 16 | return out 17 | } 18 | 19 | ;保存数据到文件 20 | static storeData(data, path) { 21 | content := "" 22 | for item in data { 23 | row := this.Join("¢", item) 24 | content .= row "`n" 25 | } 26 | f := FileOpen(path, "w", "UTF-8-RAW") 27 | f.Write(SubStr(content, 1, -1)) 28 | f.Close() 29 | } 30 | 31 | static loadJSONFile(path) { 32 | json := FileRead(path) 33 | return Jxon_Load(&json) 34 | } 35 | 36 | static storeJSONFile(json, path) { 37 | f := FileOpen(path, "w") 38 | f.Write(Jxon_Dump(json, 2)) 39 | f.Close() 40 | } 41 | } -------------------------------------------------------------------------------- /src/Utils/Start.ah2: -------------------------------------------------------------------------------- 1 | class Start { 2 | /** 3 | * @description: 运行文件 4 | */ 5 | static startFile(path, workingDir := "", options := "", beforeRun?) { 6 | if !(FileExist(path)) { ;文件不存在 7 | res := MsgBox("文件不存在:" path "`n是否尝试打开原文件夹?", "文件不存在", 0x40041) 8 | if (res = "OK") { 9 | SplitPath(path, , &outDir) 10 | try Run(outDir) 11 | } 12 | } else { 13 | if !(workingDir) ;默认使用文件所在文件夹作为工作目录 14 | SplitPath(path, , &workingDir) 15 | 16 | if (IsSet(beforeRun)) 17 | beforeRun() 18 | try { 19 | Run(path, workingDir, options, &outPID) 20 | return outPID 21 | } 22 | } 23 | } 24 | 25 | /** 26 | * @description: 在文件夹中打开 27 | */ 28 | static openFileInFolder(path) { 29 | if (FileExist(path)) { 30 | try Run('explorer.exe /select,"' path '"') 31 | } else { 32 | SplitPath(path, , &outDir) 33 | try Run(outDir) 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | 2 | name: 部署文档 3 | 4 | on: 5 | push: 6 | branches: 7 | # 确保这是你正在使用的分支名称 8 | - master 9 | 10 | jobs: 11 | deploy-gh-pages: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | # 如果你文档需要 Git 子模块,取消注释下一行 19 | # submodules: true 20 | 21 | - name: 安装 pnpm 22 | uses: pnpm/action-setup@v2 23 | with: 24 | version: 8 25 | run_install: true 26 | 27 | 28 | - name: 设置 Node.js 29 | uses: actions/setup-node@v3 30 | with: 31 | node-version: 18 32 | cache: pnpm 33 | 34 | 35 | - name: 构建文档 36 | env: 37 | NODE_OPTIONS: --max_old_space_size=8192 38 | run: |- 39 | pnpm run docs:build 40 | > docs/.vuepress/dist/.nojekyll 41 | 42 | - name: 部署文档 43 | uses: JamesIves/github-pages-deploy-action@v4 44 | with: 45 | # 这是文档部署到的分支名称 46 | branch: gh-pages 47 | folder: docs/.vuepress/dist 48 | -------------------------------------------------------------------------------- /src/Utils/GetSelectedText.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: GetSelectedText 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-07 7 | * @Description: 获取选中内容的文本和内容类型 8 | */ 9 | 10 | /** 11 | * 获取选中内容的文本和内容类型 12 | * @param clipWaitSec 剪切板等待选中内容的时间 13 | * @param &outType 返回选中内容的类型, bitmap:位图, file:文件, text:文本, unknow:未知类型, timeout:剪切板等待超时 14 | */ 15 | GetSelectedText(clipWaitSec := 0.5, &outType := unset) { 16 | seleted := "" 17 | ClipSaved := ClipboardAll() 18 | 19 | A_Clipboard := "" 20 | Send("^c") 21 | if (ClipWait(clipWaitSec)) { 22 | if (DllCall("IsClipboardFormatAvailable", "uint", 2)) { ; 位图 23 | outType := "bitmap" 24 | return ; 位图情况下不还原剪切板,直接返回 25 | } 26 | else if (DllCall("IsClipboardFormatAvailable", "uint", 15)) ; 文件 27 | outType := "file" 28 | else ; 文本或未知 29 | outType := DllCall("IsClipboardFormatAvailable", "uint", 1) ? "text" : "unknow" 30 | seleted := A_Clipboard 31 | } else 32 | outType := "timeout" ; 超时 33 | 34 | A_Clipboard := ClipSaved 35 | return seleted 36 | } -------------------------------------------------------------------------------- /src/Utils/UrlEncode.ah2: -------------------------------------------------------------------------------- 1 | UrlEncode(str, sExcepts := "-_.", enc := "UTF-8") 2 | { 3 | hex := "00", func := "msvcrt\swprintf" 4 | buff := Buffer(StrPut(str, enc)), StrPut(str, buff, enc) ;转码 5 | encoded := "" 6 | Loop { 7 | if (!b := NumGet(buff, A_Index - 1, "UChar")) 8 | break 9 | ch := Chr(b) 10 | ; "is alnum" is not used because it is locale dependent. 11 | if (b >= 0x41 && b <= 0x5A ; A-Z 12 | || b >= 0x61 && b <= 0x7A ; a-z 13 | || b >= 0x30 && b <= 0x39 ; 0-9 14 | || InStr(sExcepts, Chr(b), true)) 15 | encoded .= Chr(b) 16 | else { 17 | DllCall(func, "Str", hex, "Str", "%%%02X", "UChar", b, "Cdecl") 18 | encoded .= hex 19 | } 20 | } 21 | return encoded 22 | } 23 | 24 | 25 | ; Decode precent encoding 26 | UrlDecode(Url, Enc := "UTF-8") 27 | { 28 | Pos := 1 29 | Loop { 30 | Pos := RegExMatch(Url, "i)(?:%[\da-f]{2})+", &code, Pos++) 31 | If (Pos = 0) 32 | Break 33 | code := code[0] 34 | var := Buffer(StrLen(code) // 3, 0) 35 | code := SubStr(code, 2) 36 | loop Parse code, "`%" 37 | NumPut("UChar", Integer("0x" . A_LoopField), var, A_Index - 1) 38 | Url := StrReplace(Url, "`%" code, StrGet(var, Enc)) 39 | } 40 | Return Url 41 | } -------------------------------------------------------------------------------- /src/Utils/ChineseFirstChar.ah2: -------------------------------------------------------------------------------- 1 | ChineseFirstChar(str) 2 | { 3 | static bufferObj := Buffer(2) 4 | VarSetStrCapacity(&var, 2) 5 | static array := [[-20319, -20284, "A"], [-20283, -19776, "B"], [-19775, -19219, "C"], [-19218, -18711, "D"], [-18710, -18527, "E"], [-18526, -18240, "F"], [-18239, -17923, "G"], [-17922, -17418, "H"], [-17417, -16475, "J"], [-16474, -16213, "K"], [-16212, -15641, "L"], [-15640, -15166, "M"], [-15165, -14923, "N"], [-14922, -14915, "O"], [-14914, -14631, "P"], [-14630, -14150, "Q"], [-14149, -14091, "R"], [-14090, -13319, "S"], [-13318, -12839, "T"], [-12838, -12557, "W"], [-12556, -11848, "X"], [-11847, -11056, "Y"], [-11055, -10247, "Z"]] 6 | 7 | ; 如果不包含中文字符,则直接返回原字符 8 | if !RegExMatch(str, "[^\x{00}-\x{ff}]") 9 | return str 10 | 11 | loop parse, str 12 | { 13 | if (Ord(A_LoopField) >= 0x2E80 and Ord(A_LoopField) <= 0x9FFF) 14 | { 15 | StrPut(A_LoopField, bufferObj, "CP936") 16 | nGBKCode := (NumGet(bufferObj, 0, "UChar") << 8) + NumGet(bufferObj, 1, "UChar") - 65536 17 | for item in array { 18 | if (nGBKCode >= item[1] and nGBKCode <= item[2]) { 19 | out .= item[3] 20 | Break 21 | } 22 | } 23 | } 24 | else 25 | out .= A_LoopField 26 | } 27 | 28 | return out 29 | } -------------------------------------------------------------------------------- /docs/dev/others/ahk-gui.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: AHK Gui编程范式 3 | icon: code 4 | date: 2023-04-11 5 | --- 6 | 7 | 介绍 **Starter** 中AHK Gui的编程范式,为使用Gui控件提供一个思路。 8 | 9 | ## 驱动方式 10 | 11 | ### 1. 事件驱动 12 | 13 | 传统的Gui编程是基于事件驱动 14 | 15 | 比如用户点击按钮、输入内容等事件触发回调,在回调中我们对需要改变的数据直接进行修改、对要需要改变的UI控件进行操作。 16 | 17 | 好处是简单粗暴,缺点也很明显,当某事件与很多控件、数据相关时,这个直接修改的代码写起来会非常痛苦,又臭又长。 18 | 19 | ### 2. 数据驱动 20 | 21 | 而数据驱动则是另一种思想:一切皆数据,我们的UI控件表现如此是因为我们的数据如此,当我们的数据修改后,UI界面就应该根据数据表现为新的状态。 22 | 23 | 缺点是代码写起来比较复杂,优点是对于复杂的事件回调,代码反而简洁许多。 24 | 25 | ahk v2版本的新语法中支持了比较现代的`class`、`getter`、`setter`,因此我们可以方便地使用数据驱动(相对v1版本) 26 | 27 | 数据驱动的关键是定义好数据(MVC中的`Model`)与视图(MVC中的`View`)的交互层(`MVC`中的`Controller`) 28 | 29 | ::: note 30 | 由于 **Starter** 一开始也是使用事件驱动,后来才尝试改用数据驱动,因此没有很好的使用MVC规范,仅供参考 31 | ::: 32 | 33 | ## 实现思路 34 | 35 | - 数据 -> UI: 36 | 37 | 用抽象的数据(AHK中称为动态属性)来实现。对抽象的数据进行修改可以触发抽象数据的`setter`,在其`setter`中修改真实数据(若关联到其他数据,也可以修改其他数据的抽象数据,进一步套娃触发),并且对当前数据直接关联的UI控件进行修改。如此,实现了改变数据后UI也随之变动。 38 | 39 | - UI -> 数据: 40 | 41 | 为UI控件设置事件回调`OnEvent`。用户的交互信息,如点击、输入内容等可以触发相应的事件回调,事件回调中修改对应抽象数据的值为当前控件的值(或其他值)。 42 | 43 | - 注意事项 44 | 45 | 因为代码层面上对AHK控件修改不会触发其`OnEvent`,所以用户操作引起**UI -> 数据 -> UI**就终止,并不会造成死循环。其他语言环境则需要考虑在**数据->UI**环节中鉴别是否需要中断(比如判断对应值无变化则中断)。 46 | 47 | ::: tip 48 | 具体用法可以参考**Starter**源码 49 | ::: -------------------------------------------------------------------------------- /docs/api/addPluginToStartupMode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: addPluginToStartupMode 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static addPluginToStartupMode( 12 | name, 13 | title, 14 | keywords, 15 | startHandler, 16 | doubleRightHandler?, 17 | contextHandler?, 18 | hIcon? 19 | ) 20 | ``` 21 | 22 | ## 类型 23 | 24 | 静态方法 25 | 26 | ## 说明 27 | 28 | 添加**插件启动项**,应用方式见[插件启动项](../dev/startup/) 29 | 30 | ## 参数 31 | 32 | - ### name \{String\} 33 | 34 | 插件id,即插件文件名 35 | 36 | - ### title \{String\} 37 | 38 | 插件启动项显示标题 39 | 40 | - ### keywords \{Array\} 41 | 42 | 插件启动项其他关键字,比如 `["其他","QTGJZ"]` 43 | 44 | - ### startHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} 45 | 46 | ```ahk 47 | (obj, searchText) => Any 48 | ``` 49 | 50 | - 接收参数 51 | - `obj`:指向添加的插件启动项对象。 52 | - `searchText`:当前搜索内容。 53 | 54 | 运行(鼠标双击或回车)该插件启动项时执行的函数。 55 | 56 | - ### doubleRightHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} (可选) 57 | 58 | ```ahk 59 | (obj, searchText) => Any 60 | ``` 61 | 62 | - 接收参数 63 | - `obj`:指向添加的插件启动项对象。 64 | - `searchText`:当前搜索内容。 65 | 66 | 选填,选中该插件启动项后,双击 `Right` 按键时执行的函数。 67 | 68 | - ### contextHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} (可选) 69 | 70 | ```ahk 71 | (obj) => Any 72 | ``` 73 | 74 | - 接收参数 75 | - `obj`:指向添加的插件启动项对象。 76 | 77 | 选填,右键该插件启动项时的处理函数。 78 | 79 | - ### hIcon \{Int\} (可选) 80 | 81 | 插件启动项显示的图标hIcon,忽略则使用插件的图标。 -------------------------------------------------------------------------------- /docs/guide/get-started/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 项目介绍 3 | icon: circle-info 4 | order: 1 5 | author: AkiChase 6 | date: 2023-04-09 7 | --- 8 | 9 | ## 项目初衷 10 | 11 | **Starter** 的初衷源于我没有找到一个**真正让自己满意的**启动工具或效率工具。 12 | 13 | 我只是想要一个搜索框,输入内容,就能启动我想要的东西,最好还能有一些支持扩展的功能。 14 | 15 | 在启动软件的设计上,我比较喜欢 [uTools](https://u.tools/) 或者其开源替代品 [rubick](https://github.com/rubickCenter/rubick)。 16 | 17 | 然而,它们都存在一些问题: 18 | 19 | - 基于 Electron(相当于一个浏览器),对于启动工具来说过于臃肿了。 20 | - 不能自由设置**双击、长按、任意**快捷键呼出搜索框,而其他快捷键或是不方便或是易误触。 21 | - 搜索结果不能完全自定义。虽然能通过插件曲线救国,但是终究不太方便。 22 | 23 | 总之,虽然各种快速启动工具很多,但它们总会有不符合我个人习惯的地方, 24 | 或是 UI 太丑,或是操作逻辑不适应,或是匹配方式无法完全自定义,或是捆绑太多内容,或是体积庞大、性能有限... 25 | 26 | 因此,我决定自己开发一个启动工具,以满足自己的需求,并分享给其他需要的人使用。 27 | 28 | 这就是 **Starter** 的初衷。 29 | 30 | ## 项目特点 31 | 32 | - ### 编程语言 33 | 34 | [Autohotkey](https://www.autohotkey.com/)是一门小众而冷门的脚本语言,但它确实是我的编程启蒙语言。 35 | 尽管我曾一度嫌弃 `AHK` 那些不规范的语法,但 `AHK` 的 **V2** 版本已经规范了代码格式,并且支持了更多现代化语法。 36 | 37 | 这门语言语法简单、开发快速、无需安装运行环境,因此,完全可以作为 **Starter** 的开发语言。 38 | 39 | 作为我掌握时间最久,却没有产出任何开源作品的编程语言,我决定用它来开发 **Starter**。 40 | 41 | 虽然 `AHK` 可以使用基于 `WebView` 的 UI界面,而且使用 **Web 开发** 的方式可以获得更加精美的界面, 42 | 但为了确保开源项目能被更多 **AHKer** 接受,我决定采用 `AHK` 自带的 `Gui` 控件,即 **Win32 Gui** 控件。 43 | 44 | 这样也能为 **AHKer** 提供一个使用 `AHK` 开发的实例。 45 | 46 | - ### 小巧精美 47 | 48 | **Starter** 将贯彻**轻量**、**绿色**、**简洁**和**美观**的理念,为用户提供更好的使用体验。 49 | 50 | - ### 可扩展 51 | 52 | 每个 **AHKer** 都能够使用 **Starter** 提供的 `API` 接口来[开发](../../dev/)和加载自己的插件扩展,从而打造专属于自己的工具。 53 | 54 | 非开发者也可以在 **Starter** 日渐丰富的[插件库](../../plugin/)中挑选自己心仪的插件进行装载,扩展 **Starter** 的能力 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Starter 2 | 3 |

Starter

4 | 5 |

Starter

6 |

智能、快捷地启动文件和插件应用,提供便利和高效的使用体验。

7 | 8 |

9 | License 10 | Version 11 | Downloads 12 | Stars 13 |

14 | 15 | 16 | ## 特性 17 | 18 | - 万能搜索框 —— 通过搜索和情景匹配,回应符合要求的多种内容。 19 | - 轻量 & 绿色 —— 使用AHK编写,绿色免安装,体积 < 2M 。 20 | - 简洁 & 颜值在线 —— 设计简洁美观,UI的「颜值即正义」。 21 | - 插件化 —— 自由装载插件,打造个人专属效率工具。 22 | 23 | ## 文档 24 | 25 | [Starter文档 - GitHub Pages](https://AkiChase.github.io/Starter/) 26 | 27 | [Starter文档 - Gitee Pages](https://AkiChase.gitee.io/Starter/) 28 | 29 | ## 下载 30 | 31 | - Github下载:[Github Releases](https://github.com/AkiChase/Starter/releases) 32 | 33 | - 蓝奏云下载:[蓝奏云](https://wwi.lanzoup.com/b01kb1g4j) 密码: 9vge 34 | 35 | **Starter 优先使用** `雅痞-简` 字体,可在上方蓝奏云链接内下载安装。 36 | 37 | ## 运行 38 | 39 | 将下载的压缩包**解压**,运行文件夹中的 `创建快捷方式.bat` ,将在文件夹内创建 `Starter.lnk` 快捷方式,双击即可运行软件。 40 | 41 | 刚刚安装好的 **Starter** 就像一张白纸,但在添加各种配置之后,它将成为你最得心应手的启动工具。 42 | -------------------------------------------------------------------------------- /docs/dev/image-put.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ImagePut 3 | icon: image 4 | author: AkiChase 5 | date: 2023-04-14 6 | --- 7 | 8 | > ImagePut 是 iseahound 写的一个图片操作库,大部分常见图片操作都可以用极为简单的方式实现。 9 | 10 | - [简单、高效、实用的图片操作库 —— ImagePut 轻松实现截图、转换、缩放、裁剪等各种功能 - AutoAHK](https://www.autoahk.com/archives/37246) 11 | - [ImagePut —— 裁剪、缩放 & 其他选项](./others/image-put-doc-1.md) 12 | - [ImagePut —— 输入类型 & 输出函数](./others/image-put-doc-2.md) 13 | 14 | **Starter** 也引入了这个强大的图片操作库,并将其主要API访问方式添加到了 `PluginHelper.Utils.ImagePutHelper` 工具类中,参考[ImagePutHelper](../api/utils/ImagePutHelper.md) 15 | 16 | 可以通过这个工具类调用 `ImagePut`,完成你需要的图片相关操作。 17 | 18 | 在此给出[OCR识别文字](../plugin/ocr.md)插件中的部分代码作为示例: 19 | 20 | ```ahk {14,15} 21 | ; ocr识别 image为ImagePut类型 22 | static ocr(image) { 23 | oldTitle := this.gui.Title 24 | this.gui.Title := "正在进行文字识别..." 25 | ; accessToken是否过期判断 26 | if (!this.accessToken) 27 | if (this.accessToken := this.genAccessToken()) { 28 | this.storeData() 29 | } else { 30 | this.gui.Title := oldTitle 31 | return 32 | } 33 | 34 | ; 使用 ImagePutURI 将图片直接转化为 base64 用于上传图片 35 | base64 := PluginHelper.Utils.ImagePutHelper.ImagePutURI(image, "jpg", 100) 36 | 37 | if (res := this.baiduOcr(base64)) { 38 | out := [] 39 | ; 根据模式选择拼接方式 目前使用换行符拼接 40 | for words in res["words_result"] 41 | out.Push(words["words"]) 42 | this.ocrRes := out 43 | this.gui.Title := oldTitle 44 | return true 45 | } 46 | this.gui.Title := oldTitle 47 | return false 48 | } 49 | ``` 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/guide/intelligent/edit.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 配置 3 | icon: gear 4 | order: 2 5 | author: AkiChase 6 | date: 2023-04-11 7 | --- 8 | 9 | 在理解了智能模式的相关概念之后,你就可以正确地配置**原生智能项**了 10 | 11 | ## 智能项示例 12 | 13 | 右键右下角托盘图标打开菜单,点击**编辑智能模式**,进入**智能模式编辑界面** 14 | 15 | ![智能模式编辑界面](../../images/edit-intelligent-mode.jpg) 16 | 17 | 默认情况下,**Starter** 带有三个**原生智能项**作为你配置智能项的参考。你可以根据需要添加、编辑或删除智能项,以便根据个人习惯打开应用程序、网站等。 18 | 19 | 打开网址位于**启动分组**,百度搜索和谷歌搜索位于**搜索分组**。匹配模式都是 `reg`,即使用正则表达式进行匹配和替换。 20 | 21 | 接下来依次解释这三个实例的具体功能与含义,可以与[基本概念](./concept.md)相互对照。 22 | 23 | ### 1. 打开网址 24 | 25 | 该智能项带有两对正则表达式 26 | 27 | 1. `[^\s]*(\.[a-zA-z]{2}[^\s]*){2}`,`$0` 28 | 29 | 匹配带有两个以上"."的文本,不替换任何内容(实际上是将原始文本替换成原始文本) 30 | 31 | 2. `https?:/{2}(.+)`,`$0` 32 | 33 | 匹配以 `http://` 或者 `https://` 开头的文本,不替换任何内容(同上) 34 | 35 | 脚本模式为 `none`,启动处理程序直接执行输入的内容 36 | 37 | **实现的效果就是如果输入文本是网址链接,那么将使用默认浏览器打开这个链接** 38 | 39 | ### 2. 百度搜索 {#baidu-search-example} 40 | 41 | 该智能项带有一对正则表达式 42 | 43 | `(bd|baidu|百度)\s+(?.*)`, `${query}` 44 | 45 | 匹配以 `bd`、`baidu`、`百度` 开头 + `空格` + `搜索词` 的文本,将原始文本替换为`搜索词` 46 | 47 | 示例:`bd xxx` 将被替换为 `xxx`,作为**传递的内容**。 48 | 49 | **实现的效果就是如果输入文本是 `bd xxx`,那么将使用默认浏览器进行百度搜索 `xxx`** 50 | 51 | ### 3. 谷歌搜索 52 | 53 | 该智能项带有一对正则表达式 54 | 55 | `(gg|谷歌)\s+(?.*)`, `${query}` 56 | 57 | 与 [2. 百度搜索](#baidu-search-example) 一样,除了开头是 `gg` 或 `谷歌` 58 | 59 | **实现的效果就是如果输入文本是 `gg xxx`,那么将使用默认浏览器进行百度搜索 `xxx`** 60 | 61 | ## 更多配置 62 | 63 | 1. 在 `reg` 匹配模式时,右键**正则匹配替换模式列表**可以打开菜单,进行添加、修改、删除等操作。 64 | 2. 在**图标文件**一栏中,点击**选择**按钮,可以选择带有图片资源的文件作为智能项的图标,储存至用户数据。 65 | 3. 在**搜索分组**时,点击**获取**按钮,可以尝试获取**搜索URL**对应的网站图标,下载至用户数据。 66 | -------------------------------------------------------------------------------- /docs/guide/startup/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 启动模式 3 | icon: bolt 4 | author: AkiChase 5 | date: 2023-04-11 6 | --- 7 | 8 | ![启动模式](./search.jpg) 9 | 10 | 启动模式的使用,在 [快速上手 - 下载与使用](../get-started/install-usage.md) 已经描述了一遍,在此作为一个补充。 11 | 12 | ::: warning 13 | 此处的启动项指的是**非插件添加的**启动项,也就是**原生启动项**,可以在启动模式编辑界面中添加、修改等。 14 | ::: 15 | 16 | ## 匹配 17 | 18 | **Starter** 根据**显示名称**和**其他关键字**来识别原生启动项。 19 | 20 | 在**启动模式搜索框**中搜索时,只要搜索内容是 **显示名称的一部分** 或 **某关键字的一部分**,该启动项就会出现在搜索结果中。 21 | 22 | ## 配置 23 | 24 | ![启动模式编辑界面](./edit.jpg) 25 | 26 | ### 1. 编辑界面 27 | 28 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**编辑启动模式**。 29 | 30 | 没有配置任何启动项,会弹窗提示“启动列表内无可编辑项,请添加文件”,在弹窗后出现的文件选择框中选择任意文件添加为启动项即可。 31 | 32 | ### 2. 添加启动项 33 | 34 | 你可以通过3种方式来添加启动项: 35 | 36 | 1. 点击**启动模式编辑界面**上的**添加**按钮,在文件选择框中选择要添加的文件 37 | 2. 将所需添加的文件拖拽到**启动模式编辑界面**窗口中,这种方式既快速又方便,可以一次性添加多个启动项。 38 | 3. 将所需添加的文件拖拽到**启动模式搜索框**中,这种方式类似于第二种,但由于**搜索框会自动隐藏**,需要一定的手速。 39 | 40 | ### 3. 编辑启动项 41 | 42 | 在启动项编辑界面,你可以直接在编辑框中输入和修改启动项的内容。 43 | 44 | - **图标路径**可以指向任意含有图像资源的文件,若图标加载失败,**Starter**会使用默认图标代替。 45 | - **文件路径**指向的文件不存在时,该启动项在搜索结果的图标将变成警告图标。 46 | - **优先级**指的是启动项在启动模式搜索结果中排序的优先级,数字越大排序越靠前。 47 | 48 | ::: tip 49 | 启动项带有**动态优先级**,**每次**启动、在文件夹中打开,都会**增加**启动项的优先级,范围 0-999。 50 | 51 | **原生启动项**的优先级 < **插件启动项**的优先级 52 | ::: 53 | 54 | - 右键点击**其他关键字列表**,可以打开菜单进行增加、删除或修改操作。 55 | 56 | ## 使用 57 | 58 | - 呼出搜索框 59 | 60 | 按下 `呼出搜索框` 快捷键,呼出**启动模式搜索框**,输入任意内容,可以看到搜索结果列表。 61 | 62 | - 切换选中项 63 | 64 | `Up`、`Down` 按键可在列表中**上下切换**选中的条目。 65 | 66 | `Alt` + `数字` 按键可以快速选中**当前视图**中的第n个条目 (`Alt` + `0` 代表选中第10个)。 67 | 68 | - 启动选中项 69 | 70 | `回车` 可以启动当前选中项。 71 | 72 | - 文件夹中显示 73 | 74 | 双击 `Right`按键可以在文件夹中显示当前选中的**原生启动项**的文件路径。 75 | 76 | - 清空与关闭 77 | 78 | `Esc`按键将**清空** 输入内容,或者**隐藏**搜索界面。 79 | 80 | 搜索界面失去焦点一段时间后**自动隐藏**,自动隐藏后15s内**保留搜索结果**。 81 | 82 | - 右键菜单 83 | 84 | 右键点击任意结果条目,将会打开菜单以进行更多操作。 85 | 86 | -------------------------------------------------------------------------------- /docs/api/addPluginToIntelligentMode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: addPluginToIntelligentMode 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static addPluginToIntelligentMode( 12 | name, title, matchHandler, runHandler, contextHandler?, hIcon? 13 | ) 14 | ``` 15 | 16 | ## 类型 17 | 18 | 静态方法 19 | 20 | ## 说明 21 | 22 | 添加**插件智能项**,应用方式见[插件智能项](../dev/intelligent/) 23 | 24 | ## 参数 25 | 26 | - ### name \{String\} 27 | 28 | 插件id,即插件文件名 29 | 30 | - ### title \{String\} 31 | 32 | 插件智能项显示标题 33 | 34 | - ### matchHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} 35 | 36 | ```ahk 37 | ( 38 | obj, 39 | searchText, 40 | pastedContentType, 41 | pastedContent, 42 | workWinInfo, 43 | winInfoMatchFlag 44 | ) => Number 45 | ``` 46 | - 接收参数 47 | - `obj`:指向添加的插件智能项对象。 48 | - `searchText`:当前搜索内容。 49 | - `pastedContentType`:粘贴内容类型。 50 | - `pastedContent`:粘贴内容。 51 | - `workWinInfo`:工作窗口信息对象 52 | - `winInfoMatchFlag`:是否开启工作窗口模式 53 | - 返回值 54 | - 1:显示该插件智能项 55 | - 0:不显示该插件智能项 56 | 57 | 智能插件项的自定义匹配函数,返回值决定当前插件项是否显示 58 | 59 | :::tip 60 | 工作窗口模式是按下**智能搜索**快捷键时,若当前**没有选中任何内容**,则**Starter**将获取工作窗口信息,进入工作窗口模式下的智能模式搜索框。 61 | 62 | 搜索框会短暂显示工作窗口的信息,插件可以根据此时的工作窗口信息进行匹配 63 | ::: 64 | 65 | - ### runHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} 66 | 67 | ```ahk 68 | (obj, searchText) => Any 69 | ``` 70 | 71 | - 接收参数 72 | - `obj`:指向添加的插件智能项对象。 73 | - `searchText`:当前搜索内容。 74 | 75 | 运行(鼠标双击或回车)该插件智能项时执行的函数。 76 | 77 | - ### contextHandler \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} (可选) 78 | 79 | ```ahk 80 | (obj) => Any 81 | ``` 82 | - 接收参数 83 | - `obj`:指向添加的插件智能项对象。 84 | 85 | 选填,右键该插件智能项时执行的函数。 86 | 87 | - ### hIcon \{Int\} (可选) 88 | 89 | 选填,插件智能项显示的图标hIcon,忽略则使用插件的图标。 -------------------------------------------------------------------------------- /docs/guide/setting/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 设置界面 3 | icon: gear 4 | author: AkiChase 5 | date: 2023-04-13 6 | --- 7 | 8 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**设置界面**。 9 | 10 | ## 常规 11 | 12 | ![设置界面-常规](../../images/setting-1.jpg) 13 | 14 | 可通过勾选**开机自启**,修改软件是否开启自启动。 15 | 16 | 对于其他快捷键类型的设置项目,点击**修改**按钮可以修改对应的功能快捷键。 17 | 18 | 显示了每个快捷键的**具体内容**,若按键**无效**也会有对应显示。 19 | 20 | 快捷键设置,点击**修改**按钮可以修改对应的功能快捷键。 21 | 22 | 显示每个快捷键的**具体内容**,若按键**无效**也会有对应显示。 23 | 24 | ### 1. 按键类型 - 普通 25 | 26 | 普通按键类型仅仅支持 `Ctrl`, `Alt`, `Shift` 作为修饰的组合按键,或者单独按键。 27 | 28 | 可勾选**是否屏蔽原按键功能**,勾选后快捷键原功能将被屏蔽。 29 | 30 | 点击按键内容**热键输入框**,按下指定按键即可显示输入的按键内容,点击**确定**进行保存。 31 | 32 | ### 2. 按键类型 - 自定义 33 | 34 | **自定义**按键类型可以**自由填写**热键内容,包括鼠标按键等, 35 | 36 | 参考[热键 - 定义 & 使用 | AutoHotkey v2](https://orz707.gitee.io/v2/docs/Hotkeys.htm#toc) 37 | 38 | :::warning 39 | 热键内容的填写需要有一定AHK经验,否则建议使用其他类型 40 | ::: 41 | 42 | ### 3. 按键类型 - 双击 43 | 44 | **双击**按键类型必须填写**单个**按键名, 45 | 46 | 参考[按键列表(键盘, 鼠标和操纵杆) | AutoHotkey v2](https://orz707.gitee.io/v2/docs/KeyList.htm) 47 | 48 | 可勾选**是否屏蔽原按键功能**,勾选后快捷键原功能将被屏蔽。 49 | 50 | :::tip 51 | 双击要求第一次键击**按住**时间小于500ms,且第一次键击**松开**到第二次键击**按下**的时间间隔小于500ms 52 | ::: 53 | 54 | ### 4. 按键类型 - 长按 55 | 56 | **双击**按键类型必须填写**单个**按键名, 57 | 58 | 参考[按键列表(键盘, 鼠标和操纵杆) | AutoHotkey v2](https://orz707.gitee.io/v2/docs/KeyList.htm) 59 | 60 | 可勾选**是否屏蔽原按键功能**,勾选后快捷键原功能将被屏蔽。 61 | 62 | :::tip 63 | 长按要求第一次键击**按住**时间超过500ms 64 | ::: 65 | 66 | 67 | ## 控制 68 | 69 | ![设置界面-控制](../../images/setting-2.jpg) 70 | 71 | - 可以在此处点击按钮来添加、删除**关键词快捷键** 72 | - 双击列表中任意项可以修改对应**关键词快捷键** 73 | 74 | 按下关键词快捷键可以快速*以指定模式*搜索*既定的关键词*,并可选**是否启动搜索结果的第一项**。 75 | 76 | :::tip 77 | 以截图中的关键词快捷键内容为例: 78 | 79 | 1. 快捷键为 `CapsLock & O`,即按下`CapsLock` + `O` 快捷键触发 80 | 2. 搜索模式为启动模式 81 | 3. 关键词为“OCR识别文字” 82 | 4. 勾选了启动搜索结果 83 | 84 | 添加上述关键词快捷键后,就可以通过按下`CapsLock` + `O`快速启动搜索“OCR识别文字”,并启动搜索结果。 85 | 86 | 由于搜索到的首个结果是**插件启动项**`OCR识别文字`,因此可以实现快捷键启动该插件。 87 | ::: 88 | -------------------------------------------------------------------------------- /src/Utils/CopyToClipboard.ah2: -------------------------------------------------------------------------------- 1 | ; CopyToClipboard(".\test.png", 1) 2 | 3 | CopyToClipboard(filePath, cut := false) { 4 | FileCount := 0 5 | PathLength := 0 6 | ;Count files and total string length 7 | 8 | pid := DllCall("GetCurrentProcessId", "Uint") 9 | hwnd := WinExist("ahk_pid " . pid) 10 | DllCall("OpenClipboard", "Ptr", hwnd) 11 | hPath := DllCall("GlobalAlloc", "uint", 0x42, "uint", 20 + (StrLen(filePath) + 2) * 2, "Ptr") ; 0x42 = GMEM_MOVEABLE(0x2) | GMEM_ZEROINIT(0x40) 12 | pPath := DllCall("GlobalLock", "Ptr", hPath) ; Lock the moveable memory, retrieving a pointer to it. 13 | NumPut("uint", 20, pPath + 0), pPath += 16 ; DROPFILES.pFiles = offset of file list 14 | NumPut("uint", 1, pPath + 0), pPath += 4 ;fWide = 0 -->ANSI, fWide = 1 -->Unicode 15 | StrPut(filePath, pPath, StrLen(filePath) + 1, "UTF-16") 16 | 17 | DllCall("GlobalUnlock", "Ptr", hPath) 18 | ;hPath must not be freed! ->http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx 19 | 20 | DllCall("EmptyClipboard") ; Empty the clipboard, otherwise SetClipboardData may fail. 21 | result := DllCall("SetClipboardData", "uint", 0xF, "Ptr", hPath) ; Place the data on the clipboard. CF_HDROP=0xF 22 | 23 | ;Write Preferred DropEffect structure to clipboard to switch between copy/cut operations 24 | mem := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 4, "Ptr") ; 0x42 = GMEM_MOVEABLE(0x2) | GMEM_ZEROINIT(0x40) 25 | str := DllCall("GlobalLock", "Ptr", mem) 26 | if (!cut) 27 | DllCall("RtlFillMemory", "UInt", str, "UInt", 1, "UChar", 0x05) 28 | else 29 | DllCall("RtlFillMemory", "UInt", str, "UInt", 1, "UChar", 0x02) 30 | DllCall("GlobalUnlock", "Ptr", mem) 31 | cfFormat := DllCall("RegisterClipboardFormat", "Str", "Preferred DropEffect") 32 | result := DllCall("SetClipboardData", "UInt", cfFormat, "Ptr", mem) 33 | DllCall("CloseClipboard") 34 | ;mem must not be freed! ->http://msdn.microsoft.com/en-us/library/ms649051(VS.85).aspx 35 | } -------------------------------------------------------------------------------- /src/Utils/EditCtrlFunc.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: 名称 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-07 7 | * @Description: 介绍 8 | */ 9 | class EditCtrlFunc { 10 | ; Set placeholder 11 | static setPlaceholder(ctrl, placeholder) { 12 | DllCall("User32.dll\SendMessageW", "Ptr", ctrl.Hwnd, "Uint", (0x1500 + 1), "Ptr", True, "WStr", placeholder) 13 | } 14 | 15 | ; Gets the start and end offset of the current selection. 16 | static getSelection(&start, &end, ctrl) { 17 | s := Buffer(4, 0), e := Buffer(4, 0) 18 | SendMessage(0xB0, s.Ptr, e.Ptr, ctrl) ;EM_GETSEL 19 | start := NumGet(s, 0, "UInt"), end := NumGet(e, 0, "UInt") 20 | } 21 | 22 | ; Selects text in a edit box, given absolute character positions (starting at 0.) 23 | ; 24 | ; start: Starting character offset, or -1 to deselect. 25 | ; end: Ending character offset, or -1 for "end of text." 26 | static editSelect(ctrl, start := 0, end := -1) { 27 | SendMessage(0xB1, start, end, ctrl) ;EM_SETSEL 28 | } 29 | 30 | ; Selects a line of text. 31 | ; 32 | ; line: One-based line number, or 0 to select the current line. 33 | ; include_newline: Whether to also select the line terminator (`r`n). 34 | ; 35 | static editSelectline(ctrl, line := 0, include_newline := false) 36 | { 37 | if (line < 1) 38 | line := EditGetCurrentLine(ctrl) 39 | 40 | offset := SendMessage(0xBB, line - 1, 0, ctrl) ; EM_LINEINDEX 41 | lineLen := SendMessage(0xC1, offset, 0, ctrl) ; EM_LINELENGTH 42 | 43 | if (include_newline) { 44 | lineLen += (ctrl.ClassNN = "Edit" || ctrl.ClassNN = "Edit1") ? 2 : 1 ; `r`n : `n 45 | } 46 | 47 | ; Select the line. 48 | SendMessage(0xB1, offset, offset + lineLen, ctrl) ; EM_SETSEL 49 | } 50 | 51 | ; Deletes a line of text. 52 | ; 53 | ; line: One-based line number, or 0 to delete current line. 54 | ; 55 | static editDeleteline(ctrl, line := 0) { 56 | ; Select the line. 57 | this.editSelectline(ctrl, line, true) 58 | ControlSend("{Delete}", ctrl) 59 | } 60 | } -------------------------------------------------------------------------------- /docs/.vuepress/sidebar.ts: -------------------------------------------------------------------------------- 1 | import { sidebar } from "vuepress-theme-hope"; 2 | 3 | export const zhSidebar = sidebar({ 4 | "/guide/": [ 5 | { 6 | text: "快速上手", 7 | icon: "lightbulb", 8 | link: "/guide/get-started/intro", 9 | prefix: "get-started/", 10 | children: "structure" // structure生成会包括README, README定义index:false将其隐藏 11 | }, 12 | { 13 | text: "启动模式", 14 | icon: "bolt", 15 | link: "/guide/startup/", 16 | }, 17 | { 18 | text: "智能模式", 19 | icon: "face-laugh", 20 | collapsible: true, 21 | link: "/guide/intelligent/", 22 | prefix: "intelligent/", 23 | children: "structure" 24 | }, 25 | { 26 | text: "插件界面", 27 | icon: "list-ul", 28 | link: "/guide/plugin/", 29 | }, 30 | { 31 | text: "设置界面", 32 | icon: "gear", 33 | link: "/guide/setting/", 34 | }, 35 | { 36 | text: "自启界面", 37 | icon: "desktop", 38 | link: "/guide/boot/", 39 | }, 40 | { 41 | text: "关于界面", 42 | icon: "circle-info", 43 | link: "/guide/about/", 44 | }, 45 | { 46 | text: "用户数据", 47 | icon: "user", 48 | link: "/guide/user-data/", 49 | } 50 | ], 51 | "/plugin/": "structure", 52 | "/api/": "structure", 53 | "/dev/": [ 54 | { 55 | text: "快速上手", 56 | icon: "lightbulb", 57 | link: "/dev/get-started", 58 | }, 59 | { 60 | text: "插件启动项", 61 | icon: "bolt", 62 | link: "/dev/startup", 63 | }, 64 | { 65 | text: "插件智能项", 66 | icon: "face-laugh", 67 | link: "/dev/intelligent/", 68 | prefix: "intelligent/", 69 | children: "structure" 70 | }, 71 | { 72 | text: "插件模式", 73 | icon: "list-ul", 74 | link: "/dev/plugin-mode/", 75 | prefix: "plugin-mode/", 76 | children: "structure" 77 | }, 78 | { 79 | text: "ImagePut", 80 | icon: "image", 81 | link: "/dev/image-put", 82 | }, 83 | { 84 | text: "其他内容", 85 | icon: "ellipsis", 86 | link: "/dev/others/", 87 | collapsible: true, 88 | prefix: "others/", 89 | children: "structure" 90 | }, 91 | ], 92 | "/history": [] 93 | }); 94 | -------------------------------------------------------------------------------- /docs/api/showPluginMode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: showPluginMode 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-15 6 | --- 7 | 8 | ## 定义 9 | 10 | ```ahk 11 | static showPluginMode(pluginSearchData, searchHandler, runHandler, options := {}) 12 | ``` 13 | 14 | ## 类型 15 | 16 | 静态方法 17 | 18 | ## 说明 19 | 20 | 启动插件模式。参考[插件模式](../dev/plugin-mode/)。 21 | 22 | ## 参数 23 | 24 | - \{Array\} `pluginSearchData`: **必填参数**,用于插件模式搜索的数据。 25 | - \{(that, searchText) => any\} `searchHandler`: **必填参数**,插件模式搜索处理函数,**额外说明见下方**。 26 | - \{(that, searchText) => any\} `runHandler`: **必填参数**,插件模式回车、双击任意项时的处理函数。 27 | - \{Object\} `options`: 可选参数,**具体使用见下方说明**。 28 | 29 | ### options 中可选参数的说明 30 | 31 | - `doubleRightHandler`(可选)\{(that, rowNum) => void\} 32 | - 插件模式双击 Right 键时的处理函数,`rowNum` 为当前选中项的行号。 33 | 34 | - `loadImgsHandler`(可选)\{(that) => void\} 35 | - 加载插件模式图片的处理函数,若需要显示图标一定要带有此参数。 36 | 37 | - `toBottomHandler`(可选)\{(that) => void\} 38 | - 列表最后一行可见(触底)处理函数。 39 | 40 | - `pasteContentHandler`(可选)\{(that, typeName, content?) => bool\} 41 | - `Ctrl + V` 粘贴内容时的处理函数,如果粘贴的是 bitmap 或者 file 类型,需要额外特殊处理,**具体使用见下方说明**。 42 | 43 | - `dropFilesHandler`(可选)\{(that, fileList, pre) => bool\} 44 | - 拖入文件到搜索框的处理函数,**具体使用见下方说明**。 45 | 46 | - `initHandler`(可选)\{(that) => void\} 47 | - 初始化处理函数,在进入插件模式后被调用。 48 | 49 | - `searchText`(可选): 启动时设置搜索框文本。 50 | - `placeholder`(可选): 设置搜索框占位符。 51 | - `thumb`(可选): hICON,设置插件模式搜索框图标。 52 | 53 | ### searchHandler 处理函数说明{#searchHandler} 54 | 55 | ```ahk 56 | (that, searchText) => any 57 | ``` 58 | 59 | 为规范使用,请将搜索结果存放至 that.pluginSearchResult 然后对其进行渲染(添加到 listview)。 60 | 61 | ### pasteContentHandler 处理函数说明{#pastecontenthandler} 62 | 63 | - 函数形式: 64 | 1. `(that, typeName) => bool` 65 | 2. `(that, typeName, content) => any` 66 | 67 | - 函数返回值: 68 | 1. 在粘贴**bitmap/file**内容前会以形式一调用,则需要返回一个布尔值,表示是否允许进行粘贴操作(一般可以通过 typeName 判断是否是需要支持的粘贴内容)。 69 | 2. 在粘贴**bitmap/file**内容后还会以形式二调用,作为粘贴完成的通知 70 | 71 | :::tip 72 | typeName: file 则 content为文件路径数组 \{Array\} 73 | 74 | typeName: bitmap 则 content为位图句柄 hBitmap \{Int\} 75 | ::: 76 | 77 | ### dropFilesHandler 处理函数说明{#dropFilesHandler} 78 | 79 | - 函数形式: 80 | 1. `(that, fileList, true) => bool` 81 | 2. `(that, fileList, false) => any` 82 | 83 | - 函数返回值: 84 | 1. 在拖入文件后会以形式一触发,则需要返回一个布尔值,表示是否允许拖入操作生效。 85 | 2. 拖入生效后还会以形式二触发,作为拖入生效的通知。 -------------------------------------------------------------------------------- /docs/.vuepress/theme.ts: -------------------------------------------------------------------------------- 1 | import { hopeTheme } from "vuepress-theme-hope"; 2 | import { zhNavbar } from "./navbar.js"; 3 | import { zhSidebar } from "./sidebar.js"; 4 | 5 | 6 | 7 | export default hopeTheme({ 8 | hostname: "https://AkiChase.github.io/Starter/", 9 | 10 | author: { 11 | name: "AkiChase", 12 | url: "https://github.com/AkiChase", 13 | email: "xiangyangxiao@outlook.com" 14 | }, 15 | 16 | 17 | iconAssets: [ 18 | "https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/solid.min.css", 19 | "https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/fontawesome.min.css" 20 | ], 21 | iconPrefix: "fas fa-", 22 | 23 | favicon: "/favicon.ico", 24 | logo: "/logo.svg", 25 | logoDark: "/logo-tp.svg", 26 | 27 | 28 | repo: "AkiChase/Starter", 29 | docsDir: "/docs", 30 | docsBranch: "master", 31 | 32 | navbarLayout: { 33 | start: ["Brand"], 34 | center: [], 35 | end: ["Links", "Language", "Repo", "Outlook", "Search"] 36 | }, 37 | 38 | // 禁用打印 39 | print: false, 40 | 41 | locales: { 42 | // Chinese locale config 43 | "/": { 44 | // navbar 45 | navbar: zhNavbar, 46 | // sidebar 47 | sidebar: zhSidebar, 48 | footer: 'This site is served by GitHub Pages', 49 | displayFooter: true, 50 | // 页面元数据 51 | metaLocales: { 52 | editLink: "在 GitHub 上编辑此页", 53 | }, 54 | }, 55 | }, 56 | 57 | plugins: { 58 | prismjs: false, 59 | comment: { 60 | provider: "Giscus", 61 | repo: "AkiChase/giscusRepo", 62 | repoId: "R_kgDOJUjnlw", 63 | category: "Announcements", 64 | categoryId: "DIC_kwDOJUjnl84CVpfq", 65 | }, 66 | 67 | mdEnhance: { 68 | // 提示、注释、警告等自定义容器 69 | container: true, 70 | // 选项卡 71 | tabs: true, 72 | // 附加属性 73 | attrs: true, 74 | // 自定义对齐 75 | align: true, 76 | // 上下标 77 | sub: true, 78 | sup: true, 79 | // 底部注释引用 80 | footnote: true, 81 | // 高亮标记 82 | mark: true, 83 | // 启用图片懒加载 84 | imgLazyload: true, 85 | // 启用图片标记 86 | imgMark: true, 87 | // 启用图片大小 88 | imgSize: true, 89 | // 任务列表 90 | tasklist: true, 91 | // 导入md文件 92 | include: true, 93 | // 流程图支持 94 | flowchart: true, 95 | }, 96 | }, 97 | }); 98 | -------------------------------------------------------------------------------- /src/Gui/AboutGui.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: 关于界面 3 | * @Version: 0.0.3 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-05-12 7 | * @Description: 关于界面显示版本信息、项目主页、检查更新 8 | */ 9 | class AboutGui { 10 | static version := "0.5.3" 11 | ; static gui:=unset 12 | 13 | static init() { 14 | this.gui := Gui("-Resize", "关于界面") 15 | this.gui.SetFont("s14 q5 c333333", "NSimSun") 16 | this.gui.SetFont(, "雅痞-简") ;优先使用更好看的字体 17 | 18 | this.gui.AddPicture("w75 h75", GlobalData.imgDir "\Starter.png") 19 | this.gui.AddText("x160 yp h50 w200", "Starter").SetFont("bold s30") 20 | this.gui.AddText("x160 yp+50", "当前版本: " this.version) 21 | 22 | this.gui.AddText("x140 yp+50", "开发者:") 23 | this.gui.AddText("xp+90 yp w200", "AkiChase").SetFont("bold") 24 | this.gui.AddLink("x120 yp+35", '项目主页: Github Starter') 25 | this.gui.AddLink(, '项目主页: Gitee Starter') 26 | 27 | this.gui.AddButton("x175 yp+50", "检查更新").OnEvent("Click", (*) => this.checkUpdates()) 28 | 29 | this.gui.OnEvent("Close", (guiObj) => guiObj.Hide()) 30 | this.checkUpdates(true) 31 | } 32 | 33 | static showGui() { 34 | this.gui.Show() 35 | } 36 | 37 | static versionCompare(v1, v2) { 38 | v1 := StrSplit(v1, ".") 39 | v2 := StrSplit(v2, ".") 40 | loop 3 { 41 | if (v1[A_Index] == v2[A_Index]) 42 | continue 43 | return StrCompare(v1[A_Index], v2[A_Index]) 44 | } 45 | return 0 46 | } 47 | 48 | ; 检查最新版本 49 | static checkUpdates(silent := false) { 50 | this.gui.Title := "正在检查更新..." 51 | try { 52 | res := WinHttp().Download("https://gitee.com/api/v5/repos/AkiChase/Starter/tags?access_token=14eff4e12ee77cfd7ea5410727cb1526") 53 | res_json := Jxon_Load(&res) 54 | latest := SubStr(res_json[1]["name"], 2) 55 | if (this.versionCompare(latest, this.version) > 0) { 56 | Tip.show("检测到新版本: " latest 57 | , "请自行前往项目主页下载, 右键右下角托盘图标导出用户数据后可以导入新版本中", , , true) 58 | } else if (!silent) 59 | Tip.show("更新提示", "无需更新,已更新至最新版本: " latest) 60 | } catch 61 | Tip.show("错误", "检查更新失败") 62 | this.gui.Title := "关于界面" 63 | } 64 | } -------------------------------------------------------------------------------- /src/Starter.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: Starter 3 | * @Version: 0.5.2 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-05-12 7 | * @Description: 介绍Starter 是一款AHK编写的快速启动工具,旨在方便快捷地启动文件、文件夹,提高电脑的使用效率。 8 | */ 9 | #NoTrayIcon ; 先隐藏图标 10 | 11 | ListLines False 12 | #SingleInstance Force 13 | InstallKeybdHook(true, true) 14 | InstallMouseHook(true, true) 15 | KeyHistory(3) ; 为了双击验证 16 | FileEncoding("UTF-8-RAW") 17 | SetWinDelay(0) 18 | 19 | 20 | ; 调试模式下,退出时不保存用户数据,不会动态载入插件 21 | ; 手动#inclide 需要调试的插件 22 | ; DEBUG := true 23 | ; #Include .\Plugin\窗口切换.ahk 24 | ; #Include .\Plugin\文件搜索.ahk 25 | ; #Include .\Plugin\文件选择对话框导航.ahk 26 | ; #Include .\Plugin\网页搜索.ahk 27 | ; #Include .\Plugin\demo.ahk 28 | 29 | #Include .\Utils\GlobalData.ah2 30 | GlobalData.init() 31 | TraySetIcon(GlobalData.imgDir "\Starter.ico", , true) ; 设置图标 32 | 33 | #Include .\Utils\Class_Loader.ah2 34 | #Include .\Utils\LoadIconFromBase64.ah2 35 | #Include .\Gui\PluginGui.ah2 36 | PluginGui.init() ;同时重启以加载插件 37 | 38 | #Include .\Utils\ChineseFirstChar.ah2 39 | #Include .\Utils\MapArrClone.ah2 40 | #Include .\Utils\GetSelectedText.ah2 41 | #Include .\Utils\HotkeyHelper.ah2 42 | #Include .\Utils\JXON.ah2 43 | #Include .\Utils\WinHttp.ah2 44 | #Include .\Utils\IME.ah2 45 | #Include .\Utils\QuickSort.ah2 46 | #Include .\Utils\DataHelper.ah2 47 | #Include .\Utils\Start.ah2 48 | #Include .\Utils\WiseGuiHelper.ah2 49 | #Include .\Utils\UrlEncode.ah2 50 | #Include .\Utils\EditCtrlFunc.ah2 51 | #Include .\Utils\CopyToClipboard.ah2 52 | #Include .\Utils\7Zip\SevenZip.ahk 53 | #Include .\Utils\ImagePut.ah2 54 | 55 | Tip := WiseGuiHelper("tooltip") 56 | Tip.setOpt(, , "s14 Bold, 雅痞-简", "s12, 雅痞-简", 225 57 | , "0x805D6B, 0xFFFFFF, 0xF5F5DC, *" LoadPicture(GlobalData.imgDir "\Starter.ico", , &_) ;加*使用副本 58 | , , , "4,1,1,3", "1", , "SlideWest@150") 59 | 60 | #Include .\Gui\BootGui.ah2 61 | BootGui.init() 62 | 63 | #Include .\Gui\SearchGui\SearchGui.ah2 64 | #Include .\Gui\SearchGui\StartupMode.ah2 65 | #Include .\Gui\SearchGui\IntelligentMode.ah2 66 | #Include .\Gui\SearchGui\PluginMode.ah2 67 | SearchGui.init() 68 | 69 | #Include .\Gui\StartupEditGui.ah2 70 | StartupEditGui.init() 71 | 72 | #Include .\Gui\IntelligentEditGui.ah2 73 | IntelligentEditGui.init() 74 | 75 | #Include .\Gui\AboutGui.ah2 76 | AboutGui.init() 77 | 78 | #Include .\Gui\TrayMenu.ah2 79 | TrayMenu.init() 80 | 81 | #Include .\Utils\PluginHelper.ah2 82 | PluginHelper.init() ;同时执行所有插件的入口函数 83 | 84 | #Include .\Gui\SettingGui.ah2 85 | SettingGui.init() 86 | 87 | if (!IsSet(DEBUG) || !DEBUG) 88 | OnExit((*) => GlobalData.onExitHandler()) ;退出时保存数据 89 | 90 | if (IsSet(DEBUG) && DEBUG) 91 | Hotkey("^!q", (*) => (Send("^s"), Reload())) -------------------------------------------------------------------------------- /docs/dev/others/image-put-doc-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ImagePut —— 裁剪、缩放 & 其他选项 3 | icon: image 4 | author: AkiChase 5 | date: 2023-04-16 6 | --- 7 | 8 | :::warning 9 | 本文仅仅翻译[Crop, Scale, & Other Flags · iseahound/ImagePut Wiki (github.com)](https://github.com/iseahound/ImagePut/wiki/Crop,-Scale,-&-Other-Flags),可能存在不准确的地方,仅供参考,请以原 wiki 文档为准。 10 | ::: 11 | 12 | # 剪裁、缩放和其他标志 13 | 14 | ### 用法 15 | 16 | 指定为对象键以覆盖所有可能的输入。`image` 17 | 18 | ``` 19 | ImagePutWindow({image: "cats.jpg", scale: 2}) 20 | ``` 21 | 22 | 或使用 [特定的数据类型](./image-put-doc-2.md#输入类型),例如:`file` 23 | 24 | ``` 25 | ImagePutWindow({file: "cats.jpg", scale: 2}) 26 | ``` 27 | 28 | ### 剪裁 Crop 29 | 30 | 传递一个 [x、y、w、h] 数组。所有四个成员都是必需的。 31 | 32 | ``` 33 | ; 剪裁到从 (100, 100) 处开始的 250 x 250 像素的图像。 34 | ImagePutWindow({image: "cats.jpg", crop: [100, 100, 250, 250]}) 35 | ``` 36 | 37 | 任何负值都会被解释为“从边缘开始”。宽度和高度分别对应右边和下边。 38 | 39 | ``` 40 | ; 从每个边缘裁剪 10 像素。 41 | ImagePutWindow({image: "cats.jpg", crop: [-10, -10, -10, -10]}) 42 | ``` 43 | 44 | 如果这些值中有任何一个是百分比,它们应该作为字符串输入。 45 | 46 | ``` 47 | ; 从每个边缘裁剪 10%。 48 | ImagePutWindow({image: "cats.jpg", crop: ["-10%", "-10%", "-10%", "-10%"]}) 49 | ``` 50 | 51 | 指定的剪裁数组比原始文件大不会发生什么。将忽略超出边界的值。 52 | 53 | ### 缩放 Scale 54 | 55 | 传递一个正实数。缩放算法使用 GDI+ HighQualityBicubic。 56 | 57 | ``` 58 | ; 缩放倍数为2.5倍。 59 | ImagePutWindow({image: "cats.jpg", scale: 2.5}) 60 | ``` 61 | 62 | 要缩放到特定的宽度和高度,请使用 2 成员数组。宽度和高度必须是整数值。 63 | 64 | ``` 65 | ; 调整大小为640 x 480。 66 | ImagePutWindow({image: "cats.jpg", scale: [640, 480]}) 67 | ``` 68 | 69 | 要计算缺失的边,请使用大小数组,并将参数替换为任何字符串。这将保持原始纵横比。 70 | 71 | ``` 72 | ; 缩放高度为480像素。 73 | ImagePutWindow({image: "cats.jpg", scale: ["", 480]}) 74 | ImagePutWindow({image: "cats.jpg", scale: ["auto", 480]}) ; 更易于理解。 75 | ``` 76 | 77 | ### 解码 Decode 78 | 79 | 当设置为 true 时,禁用快速流转换。影响:pdf、url、file、stream、randomaccessstream、base64 和 hex。默认值为 false。 80 | 81 | 启用此标志将删除原始文件编码,包括原始文件格式的任何压缩增益。这可能有助于验证像素数据的内容,否则将保持未检查状态。会降低转换速度。 82 | 83 | ``` 84 | ; 图像将解码为像素数据并重新编码为 PNG。 85 | str := ImagePutBase64({image: "cats.jpg", decode: True}) 86 | 87 | ; 设置全局解码标志。 88 | ImagePut.decode := True ; 默认值为 false。 89 | ``` 90 | 91 | ### 验证 Validate 92 | 93 | 立即通过将位图加载到内存中检查像素数据。将来,这可能会检查流数据的标头。 94 | 95 | 如果调用:`ImagePutBitmap` 96 | 97 | - 如果启用,则对位图调用:`GdipValidateImage`。 98 | - 强制将图像加载到内存中。(糟糕:增加了峰值内存使用量。) 99 | - 避免返回对图像数据的引用。(好:防止多个引用导致数据竞争。) 100 | - 避免通过读取时复制或写入时复制实现延迟渲染。(好:立即加载图像数据可防止将意外修改复制。) 101 | - 附带的一项副作用是立即将图像加载到内存中,意味着对原始图像数据的任何修改都不会被复制过来。 102 | - 修复了一种罕见的错误,即位图克隆的位图克隆(2 层引用)在从流中解码图像时可能会导致数据竞争。 103 | - 当返回 GDI+ 位图时,建议将此标志设置为 true,因为在位图的立即创建之后的任何时候调用都会导致秘密失败。`validate``GdipValidateImage``GdipValidateImage` 104 | 105 | 默认值为 false,这意味着在需要时拉取像素数据,以节省内存并加快速度。除非满足上述条件,否则不需要将此标志设置为 true。 106 | 107 | ``` 108 | ; 立即将 cats.jpg 加载到内存中。 109 | pBitmap := ImagePutBitmap({image: "cats.jpg", validate: true}) 110 | 111 | ; 设置全局验证标志。 112 | ImagePut.validate := True ; 默认值为 false。 113 | ``` 114 | -------------------------------------------------------------------------------- /src/Plugin/win10任务栏透明.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: win10任务栏透明.ahk 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-03-29 7 | * @Description: win10任务栏透明功能 8 | */ 9 | 10 | /* 11 | ===Starter Plugin Info==> 12 | { 13 | "author": "AkiChase", 14 | "version": "0.0.1", 15 | "introduction": "任务栏透明化,仅支持win10,打开开始菜单透明化会消失", 16 | "icon": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAKtJREFUWEdjZBhgwDjA9jMMOgfMxBEiZxkYGGbhkPPBIf6cgYEBpA8vQA8BkAOMsegAWY7LAWl4HD3qgNEQIDkEcCUoPQYGhh14Ujs2KREGBoZLhPQQWw6AspovDsPScYhLMjAwSGGR+8zAwHALJj7qgNEQGHQhMFoXjIbAaAiMVka4GpCjteFoCIBCQB1HAunF0yLixSFHcouIUAeHbHliq2OyLSCkccAdAADLclshWioLowAAAABJRU5ErkJggg==" 17 | } 18 | <==Starter Plugin Info=== 19 | */ 20 | 21 | PluginHelper.addEntryFunc((*) => Plugin_win10任务栏透明.main()) ; 添加入口函数等待执行 22 | 23 | class Plugin_win10任务栏透明 { 24 | static menu := Menu() 25 | static state := false 26 | ; static f:=unset 27 | 28 | static TaskBar_SetAttr(accent_state := 0, gradient_color := "0x01000000") 29 | { 30 | static init := 0 31 | static hTrayWnd := 0 32 | static ver := DllCall("GetVersion") & 0xff < 10 33 | static pad := A_PtrSize = 8 ? 4 : 0, WCA_ACCENT_POLICY := 19 34 | 35 | if !(init) { 36 | if (ver) 37 | throw Error("Minimum support client: Windows 10", -1) 38 | if !(hTrayWnd := DllCall("user32\FindWindow", "str", "Shell_TrayWnd", "ptr", 0, "ptr")) 39 | throw Error("Failed to get the handle", -1) 40 | init := 1 41 | } 42 | 43 | ACCENT_POLICY := Buffer(16, 0) 44 | accent_size := ACCENT_POLICY.Size 45 | NumPut("int", (accent_state > 0 && accent_state < 4) ? accent_state : 0, ACCENT_POLICY, 0) 46 | 47 | if (accent_state >= 1) && (accent_state <= 2) && (RegExMatch(gradient_color, "0x[[:xdigit:]]{8}")) 48 | NumPut("int", gradient_color, ACCENT_POLICY, 8) 49 | 50 | WINCOMPATTRDATA := Buffer(4 + pad + A_PtrSize + 4 + pad, 0) 51 | NumPut("int", WCA_ACCENT_POLICY, WINCOMPATTRDATA, 0,) 52 | NumPut("ptr", ACCENT_POLICY.Ptr, WINCOMPATTRDATA, 4 + pad) 53 | NumPut("uint", accent_size, WINCOMPATTRDATA, 4 + pad + A_PtrSize) 54 | if !(DllCall("user32\SetWindowCompositionAttribute", "ptr", hTrayWnd, "ptr", WINCOMPATTRDATA.Ptr)) 55 | throw Error("Failed to set transparency / blur", -1) 56 | return 0 57 | } 58 | 59 | static toggleState() { 60 | if (this.state) { 61 | SetTimer(this.f, 0) ;关闭计时器 62 | this.menu.Uncheck("开启") 63 | } else { 64 | SetTimer(this.f, 8000) ;开启计时器 65 | this.TaskBar_SetAttr(2) ; 透明化 66 | this.menu.check("开启") 67 | } 68 | this.state := !this.state 69 | } 70 | 71 | static main() { 72 | this.f := this.TaskBar_SetAttr.Bind(this, 2) 73 | this.menu.Add("开启", (*) => this.toggleState()) 74 | PluginHelper.pluginMenu.Add("win10任务栏透明", this.menu) 75 | this.toggleState() 76 | } 77 | } -------------------------------------------------------------------------------- /docs/guide/intelligent/concept.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 基本概念 3 | icon: list 4 | order: 1 5 | author: AkiChase 6 | date: 2023-04-11 7 | --- 8 | 9 | 智能模式的功能比较特殊,有一些相关概念需要你提前了解,以便更好地理解如何使用智能模式。 10 | 11 | ## 智能项 12 | 13 | **原生智能项**是指你在智能模式编辑界面中手动添加、删除或修改的智能项,与插件无关。 14 | 15 | **插件智能项**是指通过插件添加的智能项。插件智能项只能通过插件的编写者更新或修改,有的插件可能会为用户提供**增删改**智能项的方式。 16 | 17 | ::: tip 18 | 本章提到的内容都针对**原生智能项**,非开发者只需要了解**原生智能项**即可,若有兴趣请移步[插件开发 - 插件智能项](../../dev/intelligent/basics.md) 19 | ::: 20 | 21 | **原生智能项**的功能或许很强大,或许很鸡肋,因人而异。 22 | 23 | - 它可以根据你的文本输入,快速打开某个网页的搜索页面,并搜索输入的内容。 24 | 25 | - 它可以根据你的文本输入,启动某个程序,并传递输入的命令行参数。 26 | 27 | ::: warning 28 | 接下来的内容比较复杂、繁琐,如果你没有上诉需求或者不喜欢折腾,可以停止阅读本章。 29 | ::: 30 | 31 | ## 匹配模式 32 | 33 | **匹配**指的是当前智能项会出现在搜索结果中,设定不同匹配模式可以灵活的匹配不同的输入内容 34 | 35 | ::: warning 36 | 目前,**原生智能项**仅支持匹配**文本**输入类型的内容,暂时不支持**工作窗口信息、文件、位图**等输入类型的匹配。 37 | 38 | 这是为了避免**原生智能项**的相关概念变得更加复杂,同时也降低了**智能模式编辑界面**的复杂程度。 39 | 40 | **工作窗口信息、文件、位图**等输入类型,只有**插件智能项**才可以设定相关的匹配条件,相关内容请移步[插件开发 - 插件智能项](../../dev/intelligent/basics.md) 41 | ::: 42 | 43 | ### 1. 字符串模式(str) 44 | 45 | **智能模式搜索框**输入内容为**任意字符串**时匹配,输入内容**原封不动地传递**给启动处理程序。 46 | 47 | ::: tip 48 | 启动处理程序具体做什么在后续会有解释,此时你只要知道它会接收**传递的内容**作为参数进行后续处理 49 | ::: 50 | 51 | ### 2. 正则匹配替换模式(reg) 52 | 53 | 在**正则匹配替换模式**下,原生智能项会有一个正则表达式二元列表,列表的每项包含一对表达式:**匹配表达式**,**替换表达式** 54 | 55 | 在智能模式搜索框输入文本时,对于每个原生智能项,**Starter** 依次使用该项中所有**匹配表达式**进行正则匹配,直到有一个表达式与输入文本匹配为止。如果所有表达式都无法匹配,则该智能项不会出现在搜索结果中。 56 | 57 | ::: tip 58 | 对某个智能项的**匹配表达式**的匹配顺序是**从上到下**,也就是排在越前面的表达式优先级越高。 59 | ::: 60 | 61 | 对于这一类智能项,**Starter** 将使用该项的替换表达式对输入内容进行替换,替换后的结果就是所谓的**传递的内容**。 62 | 63 | 正则匹配与替换参考: 64 | 65 | 1. [正则表达式30分钟入门教程 (deerchao.cn)](https://deerchao.cn/tutorials/regex/regex.htm#mission) 66 | 2. [RegExReplace - 语法 & 使用 | AutoHotkey v2](https://orz707.gitee.io/v2/docs/commands/RegExReplace.htm) 67 | 68 | ### 3. 匹配优先级 69 | 70 | 搜索结果展示的优先级取决于匹配规则的优先级,匹配规则的优先级越高在搜索结果越靠前。在**原生智能项**中,所有 `reg` 匹配的规则优先级都高于 `str` 匹配的规则,而**插件智能项**的规则优先级更高。 71 | 72 | ## 智能项分组 73 | 74 | **原生启动项**有两个分组,**启动分组**和**搜索分组**,不同分组中的**启动处理程序**略有区别 75 | 76 | 启动处理接下来介绍不同分组、不同条件下**启动处理程序**的具体功能 77 | 78 | ### 1. 启动分组(run-with) 79 | 80 | ![run-with分组](./run-with.jpg) 81 | 82 | **启动处理程序**将收到的**传递内容**按当前原生智能项的**脚本模式**执行启动。 83 | 84 | ::: tip 85 | 脚本模式是**启动分组**内原生智能项**特有**的选项,**搜索分组**内原生智能项不具有该选项 86 | ::: 87 | 88 | - 空模式(none) 89 | 90 | 启动处理程序**直接执行传递的内容**,即直接执行 `Run(传递的内容)`,此时程序路径无效。 91 | 92 | `Run()` 函数细节可参考[Run / RunWait - 语法 & 使用 ](https://orz707.gitee.io/v2/docs/commands/Run.htm)中的 `Target` 参数。 93 | 94 | - 单参数模式(arg) 95 | 96 | 启动处理程序将**传递的内容**视为**一个**命令行参数(对**传递的内容**中的双引号进行转义,再用双引号包裹),然后用此参数启动填写的**程序路径** 97 | 98 | 即执行 `Run(程序路径 "传递的内容")` 99 | 100 | - 多参数模式(args) 101 | 102 | 启动处理程序将**传递的内容**视为**完整的**命令行参数(对**传递的内容**不做任何处理),用此参数启动填写的**程序路径**。 103 | 104 | 即执行 `Run(程序路径 传递的内容)` 105 | 106 | ### 2. 搜索分组(search) 107 | 108 | ![run-with分组](./search.jpg) 109 | 110 | **启动处理程序**将收到的内容填充到**搜索URL**中"{}"所在位置,启动填充后的URL。 111 | 112 | ::: tip 113 | 启动处理程序的根本功能就是 `Run(Target)` 参考[Run / RunWait - 语法 & 使用 ](https://orz707.gitee.io/v2/docs/commands/Run.htm) 114 | ::: -------------------------------------------------------------------------------- /docs/guide/get-started/install-usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 下载与使用 3 | icon: download 4 | order: 2 5 | author: AkiChase 6 | date: 2023-04-09 7 | --- 8 | 9 | ::: center 10 | Starter 11 |
Starter
12 |

智能、快捷地启动文件、文件夹,执行操作,提高电脑的使用效率

13 | ::: 14 | 15 |

16 | License 17 | Version 18 | Downloads 19 | Stars 20 |

21 | 22 | ## 下载 23 | 24 | - Github下载:[Github Releases](https://github.com/AkiChase/Starter/releases) 25 | 26 | - 蓝奏云下载:[蓝奏云](https://wwi.lanzoup.com/b01kb1g4j) 密码: 9vge 27 | 28 | **Starter 优先使用** `雅痞-简` 字体,可在上方蓝奏云链接内下载安装。 29 | 30 | ## 运行 31 | 32 | 将下载的压缩包**解压**,运行文件夹中的 `创建快捷方式.bat` ,将在文件夹内创建 `Starter.lnk` 快捷方式,双击即可运行软件。 33 | 34 | 35 | :::tip 36 | 刚刚安装好的 **Starter** 就像一张白纸,但在添加各种配置之后,它将成为你最得心应手的启动工具。 37 | ::: 38 | 39 | ## 配置启动项 40 | 41 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**编辑启动模式**。 42 | 43 | ::: tip 44 | 由于初次运行,没有配置任何启动项,会弹窗提示“启动列表内无可编辑项,请添加文件”。 45 | 46 | 点击确认,在随后出现的文件选择框中随意选择一个文件,比如QQ,以添加到启动列表。 47 | 48 | 然后就可以打开编辑界面,开始个性化设置启动项了。 49 | ::: 50 | 51 | ![启动模式编辑界面](../../images/edit-startup-mode.jpg) 52 | 53 | - ### 添加启动项 54 | 55 | 在启动模式编辑界面中,你可以通过两种方式来添加启动项: 56 | 57 | 1. 你可以点击界面上的**添加**按钮,在文件选择框中选择要添加的文件 58 | 2. 你也可以将所需添加的文件拖拽到窗口中,这种方式既快速又方便,可以一次性添加多个启动项。 59 | 60 | ::: tip 61 | 此处的启动项指的是非插件添加的启动项,也就是**原生启动项**,可以在启动模式编辑界面中添加、修改等。 62 | ::: 63 | 64 | - ### 编辑启动项 65 | 66 | **Starter** 根据**显示名称**和**其他关键字**来识别启动项。在搜索框中搜索时,只要搜索内容是 **显示名称的一部分** 或 **某行关键字的一部分**,该启动项就会出现在搜索结果中。 67 | 68 | 在启动项编辑界面,你可以直接在编辑框中输入和修改启动项的内容。此外,如果你需要增加、删除或修改其他关键字,可以右键点击其他关键字列表,打开菜单进行相应操作。最后,点击保存按钮进行保存。 69 | 70 | ## 启用插件 71 | 72 | 运行 **Starter** 后,右键右下角托盘图标打开菜单,点击**插件界面**。 73 | 74 | ![启动模式编辑界面](../../images/plugin-2.jpg) 75 | 76 | 在插件列表中双击任意一行,即可启用或禁用该插件。在修改完所需的插件状态后,点击**保存重载**按钮,软件会重启且只会载入所有已启用的插件。 77 | 78 | 插件的使用请参考[插件指南](../../plugin/) 79 | 80 | ## 开始使用 81 | 82 | - 呼出搜索框 83 | 84 | 按下 `呼出搜索框` 快捷键,呼出**启动模式搜索框**,输入任意内容,可以看到搜索结果列表。 85 | 86 | - 切换选中项 87 | 88 | `Up`、`Down` 按键可在列表中**上下切换**选中的条目。 89 | 90 | `Alt` + `数字` 按键可以快速选中**当前视图**中的第n个条目 (`Alt` + `0` 代表选中第10个)。 91 | 92 | - 启动选中项 93 | 94 | `回车` 可以启动当前选中项。 95 | 96 | - 文件夹中显示 97 | 98 | 双击 `Right`按键可以在文件夹中显示当前选中的**原生启动项**。 99 | 100 | ::: tip 101 | 如果该启动项来自于某个插件,即**插件启动项**,则执行相应的插件设定。 102 | ::: 103 | 104 | - 清空与关闭 105 | 106 | `Esc`按键将**清空** 输入内容,或者**隐藏**搜索界面。 107 | 108 | 搜索界面失去焦点一段时间后**自动隐藏**,自动隐藏后15s内**保留搜索结果**。 109 | 110 | - 右键菜单 111 | 112 | 右键点击任意结果条目,将会打开菜单以进行更多操作。 113 | 114 | ## 更多 115 | 116 | ::: tip 117 | 以上是 **Starter** 中最简单、常用的搜索与启动功能,是你快速上手 **Starter** 的最好方式。 118 | ::: 119 | 120 | **Starter** 还具备其他更丰富的功能,比如[插件](../../plugin/)、[智能模式](../intelligent/)、[开机启动](../boot/)等等 121 | ,建议你细读本文档的其他章节,探索其他功能。 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /docs/dev/startup.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 插件启动项 3 | icon: bolt 4 | author: AkiChase 5 | date: 2023-04-14 6 | --- 7 | 8 | 插件启动项类似于原生启动项,但是更加**自由**、**强大**。 9 | 10 | ## API 11 | 12 | 添加**插件启动项**需要使用 `PluginHelper.addPluginToStartupMode()` API 13 | 14 | 参数及说明:[PluginHelper.addPluginToStartupMode](../api/addPluginToStartupMode.md) 15 | 16 | ## 简单应用 17 | 18 | 在启动该插件项时触发某个功能,参考[快速上手 - 添加插件启动项](./get-started.md#add-plugin-to-startup-mode) 19 | 20 | ## 进阶应用 21 | 22 | 主要通过ahk**闭包特性**进行更多操作,和 `js` 中的闭包一样,用起来非常灵活。 23 | ### 1. 同时添加多个插件项 24 | 25 | 批量添加多个插件项,通过 `obj` 对象的属性、ahk**闭包特性**执行预期的内容。 26 | 27 | ```ahk 28 | ; 添加插件启动项 29 | static addStartupItem() { 30 | demoData := Map( 31 | "明月几时有", "把酒问青天", 32 | "不知天上宫阙", "今夕是何年", 33 | "我欲乘风归去", "又恐琼楼玉宇" 34 | ) 35 | 36 | ; 利用闭包特性,可以访问到demoData 37 | startHandler(obj, searchText) { 38 | PluginHelper.Utils.tip(this.name, demoData[obj.title]) 39 | } 40 | 41 | for k, v in demoData { 42 | ; 如果startHandler闭包在此处,甚至可以直接访问到k,v 43 | 44 | PluginHelper.addPluginToStartupMode( 45 | this.name, 46 | k, 47 | ; 使用工具函数将其转换为拼音首字母作为关键词 48 | [PluginHelper.Utils.chineseFirstChar(k)], 49 | startHandler 50 | ) 51 | } 52 | } 53 | ``` 54 | 55 | 批量添加结果 56 | 57 | ![批量添加启动项](./images/startup-1.jpg) 58 | 59 | 搜索拼音首字母 `wy` 得到唯一匹配启动项 60 | 61 | ![搜索](./images/startup-2.jpg) 62 | 63 | 启动结果如下图 64 | 65 | ![执行](./images/startup-3.jpg) 66 | 67 | 68 | ### 2. 右键菜单 69 | 70 | 右键当前项时可以通过 `contextHandler` 显示菜单,而通过**闭包**或者为函数*bind参数*可以区分出具体是哪一项 71 | 72 | - 纯闭包实现 73 | ```ahk 74 | ; 添加插件启动项 75 | static addStartupItem() { 76 | demoData := Map( 77 | "明月几时有", "把酒问青天", 78 | "不知天上宫阙", "今夕是何年", 79 | "我欲乘风归去", "又恐琼楼玉宇" 80 | ) 81 | 82 | ; 利用闭包 83 | curContextObj := 0 84 | m := Menu() 85 | m.Add("显示后半句", (*) => PluginHelper.Utils.tip(this.name, demoData[curContextObj.title])) 86 | contextHandler(obj){ 87 | ; 利用闭包,在显示菜单时将 curContextObj 赋值,从而能在外访问到符合预期的后半句 88 | curContextObj:=obj 89 | m.Show() ; 显示菜单 90 | } 91 | 92 | for k, v in demoData { 93 | PluginHelper.addPluginToStartupMode( 94 | this.name, 95 | k, 96 | [PluginHelper.Utils.chineseFirstChar(k)], 97 | (*) => 0,, 98 | contextHandler 99 | ) 100 | } 101 | } 102 | ``` 103 | 104 | - 闭包 + Bind 实现 105 | ```ahk 106 | static addStartupItem() { 107 | demoData := Map( 108 | "明月几时有", "把酒问青天", 109 | "不知天上宫阙", "今夕是何年", 110 | "我欲乘风归去", "又恐琼楼玉宇" 111 | ) 112 | 113 | ; 利用闭包 114 | words := 0 115 | m := Menu() 116 | m.Add("显示后半句", (*) => PluginHelper.Utils.tip(this.name, words)) 117 | contextHandler(bindParam, obj) { 118 | ; 利用闭包,在显示菜单时将 words 赋值,从而能在外访问到符合预期的后半句 119 | words := bindParam 120 | m.Show() ; 显示菜单 121 | } 122 | 123 | for k, v in demoData { 124 | PluginHelper.addPluginToStartupMode( 125 | this.name, 126 | k, 127 | [PluginHelper.Utils.chineseFirstChar(k)], 128 | (*) => 0, , 129 | contextHandler.Bind(v) 130 | ) 131 | } 132 | } 133 | ``` 134 | 135 | 效果相同,只是思路上有细微的区别。 136 | 137 | ![执行](./images/startup-4.jpg) 138 | 139 | ![执行](./images/startup-5.jpg) 140 | 141 | -------------------------------------------------------------------------------- /docs/dev/intelligent/basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 基础 3 | icon: square-caret-up 4 | author: AkiChase 5 | date: 2023-04-14 6 | order: 1 7 | --- 8 | 9 | **插件智能项**的功能非常强大,可以通过自定义匹配函数,根据当前输入内容的类型,具体内容,工作窗口等信息判断是否匹配当前插件智能项。 10 | 11 | 具体来说,比如,将图片粘贴到智能模式搜索框中,就可以根据输入内容的类型是图片,而匹配到**OCR图片识别**插件添加的智能项。 12 | 13 | 比如,将单个文件夹粘贴到搜索框中,就可以根据输入内容的类型是文件,再根据当前文件是文件夹,而匹配到通过**文件搜索**插件添加的智能项。 14 | 15 | 而其他方面的操作类似于**插件启动项**,在此不多赘述。 16 | 17 | :::tip 18 | 与**插件启动项**不同,**智能启动项**没有 `doubleRightHandler` 参数,其他用法类似。 19 | ::: 20 | 21 | 主要内容集中在**自定义匹配**函数 22 | 23 | ## API 24 | 25 | 添加**插件智能项**需要使用 `PluginHelper.addPluginToIntelligentMode()` API 26 | 27 | 参数及说明:[PluginHelper.addPluginToIntelligentMode](../../api/addPluginToIntelligentMode.md) 28 | 29 | ## 简单应用 30 | 31 | 自定义匹配函数的应用,即 `matchHandler` 参数 32 | 33 | ### 1. 文本匹配 34 | 35 | 自由组合文本匹配,matchHandler返回0则不匹配,返回非0则匹配 36 | 37 | ```ahk 38 | ; 添加插件智能项 39 | static addIntelligentItem() { 40 | ; 自定义匹配函数 41 | matchHandler(obj, searchText, pastedContentType, *) { ; 使用*忽略多余参数 42 | ; 带有非文本类型的都不匹配 43 | if (pastedContentType != 'text') 44 | return 0 45 | ; 若searchText为"PPWB"开头部分 46 | if (PluginHelper.Utils.strStartWith("PPWB", searchText) 47 | || searchText == '1234' ;或者searchText为1234 48 | || InStr(searchText, "6") ; 或者searchText包含6 49 | ) 50 | return 1 ;则匹配 51 | 52 | return 0 ; 其他情况也不匹配 53 | } 54 | 55 | PluginHelper.addPluginToIntelligentMode(this.name, 56 | "匹配文本", 57 | matchHandler, 58 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 59 | ) 60 | } 61 | ``` 62 | 63 | 如图,符合`InStr(searchText, "6")`而匹配 64 | 65 | ![文本匹配](../images/intelligent-1.jpg) 66 | 67 | ### 2. 输入类型匹配 68 | 69 | ```ahk 70 | ; 添加插件智能项 71 | static addIntelligentItem() { 72 | ; 自定义匹配函数 73 | matchHandler(obj, searchText, pastedContentType, pastedContent, *) { ; 使用*忽略多余参数 74 | if (pastedContentType == 'file' && ; 文件类型 且 75 | pastedContent.Length == 1 && ; 文件数量为1 且 76 | InStr(FileExist(pastedContent[1]), "D") ; 文件为文件夹 77 | ) 78 | return 1 ; 符合则匹配 79 | return 0 ; 否则不匹配 80 | } 81 | 82 | PluginHelper.addPluginToIntelligentMode(this.name, 83 | "匹配单个文件夹", 84 | matchHandler, 85 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 86 | ) 87 | } 88 | ``` 89 | 90 | 如图,符合单个文件夹而匹配 91 | 92 | ![单文件夹匹配](../images/intelligent-2.jpg) 93 | 94 | ### 3. 工作窗口匹配 95 | 96 | :::tip 97 | 工作窗口模式是按下**智能搜索**快捷键时,若当前**没有选中任何内容**,则**Starter**将获取工作窗口信息,进入工作窗口模式下的智能模式搜索框。 98 | 99 | 搜索框会短暂显示工作窗口的信息,插件可以根据此时的工作窗口信息进行匹配 100 | ::: 101 | 102 | 通过工作窗口信息,匹配资源管理器 103 | 104 | ```ahk 105 | static addIntelligentItem() { 106 | ; 自定义匹配函数 107 | matchHandler(obj, searchText, pastedContentType, pastedContent, workWinInfo, winInfoMatchFlag) { 108 | ; 非工作窗口模式不匹配 109 | ; 只有工作窗口模式下,窗口信息才是有效的 110 | ; 否则可能是上次工作窗口模式的信息 111 | if (!winInfoMatchFlag) 112 | return 0 113 | 114 | pName := workWinInfo.processName 115 | cName := workWinInfo.class 116 | ; 返回是否满足资源管理器窗口特征 (用或连接) 117 | return (pName == 'explorer.exe' || cName == "CabinetWClass" || cName == "ExploreWClass") 118 | } 119 | 120 | PluginHelper.addPluginToIntelligentMode(this.name, 121 | "匹配资源管理器", 122 | matchHandler, 123 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 124 | ) 125 | } 126 | ``` 127 | 128 | ![匹配资源管理器](../images/intelligent-3.jpg) 129 | -------------------------------------------------------------------------------- /src/Utils/Class_Loader.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: Class_Loader 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-03-29 7 | * @Description: Loading files(or code) dynamically. But it needs to be loaded all in one call(loadFromFiles() or loadFromText()). 8 | */ 9 | 10 | #Requires AutoHotkey v2.0 11 | 12 | #Include "*i \\.\pipe\3a9e1e44-8c0b-4e18-a0d3-63d1cadc4000" 13 | class Loader { 14 | static _loaded := false 15 | 16 | /** 17 | * @description: loading files dynamically 18 | * @param fileList The file Path(Absolute or Relative) Array 19 | */ 20 | static loadFromFiles(fileList) { 21 | includeText := "" 22 | for f in fileList { 23 | includeText .= "`n#Include " this._getFullPathName(f) 24 | } 25 | this.loadFromText(includeText) 26 | } 27 | 28 | /** 29 | * @description: loading code dynamically 30 | * @param text Code to load 31 | */ 32 | static loadFromText(text) { 33 | if (this._loaded) ; Prevent dead cycle 34 | return 35 | 36 | text := "Loader._loaded := true`n" text 37 | ;// Create named pipes 38 | pipe := [0, 0] 39 | Loop 2 40 | { 41 | if ((pipe[A_Index] := DllCall( 42 | "CreateNamedPipe", ; http://goo.gl/3aJQg7 43 | "Str", "\\.\pipe\3a9e1e44-8c0b-4e18-a0d3-63d1cadc4000", ; lpName 44 | "UInt", 2, ; dwOpenMode = PIPE_ACCESS_OUTBOUND 45 | "UInt", 0, ; dwPipeMode = PIPE_TYPE_BYTE 46 | "UInt", 255, ; nMaxInstances 47 | "UInt", 0, ; nOutBufferSize 48 | "UInt", 0, ; nInBufferSize 49 | "Ptr", 0, ; nDefaultTimeOut 50 | "Ptr", 0 ; lpSecurityAttributes 51 | )) == -1) ; INVALID_HANDLE_VALUE = -1 52 | throw A_ThisFunc . "() - Failed to create named pipe.`nGetLastError: " . A_LastError 53 | } 54 | 55 | ;// Process command line argument(s) 56 | q := Chr(34) ;// double quote for v1.1 and v2.0-a compatibility 57 | params := "" 58 | for arg in A_Args 59 | { 60 | i := 0 61 | while (i := InStr(arg, q, , i + 1)) ;// escape '"' with '\' 62 | if (SubStr(arg, i - 1, 1) != "\") 63 | arg := SubStr(arg, 1, i - 1) . "\" . SubStr(arg, i++) 64 | params .= (A_Index > 1 ? " " : "") . (InStr(arg, " ") ? q . arg . q : arg) 65 | } 66 | 67 | ;// Reload script passing args(if any) 68 | Run(Format('"{}" /restart "{}" {}', A_AhkPath, A_ScriptFullPath, params)) 69 | 70 | DllCall("ConnectNamedPipe", "Ptr", pipe[1], "Ptr", 0) ;// http://goo.gl/pwTnxj 71 | DllCall("CloseHandle", "Ptr", pipe[1]) 72 | 73 | ; The second pipe is for the actual file contents. 74 | DllCall("ConnectNamedPipe", "Ptr", pipe[2], "Ptr", 0) 75 | ;// Write dynamic code into pipe 76 | if !(f := FileOpen(pipe[2], "h", "UTF-8")) ;// works on both Unicode and ANSI 77 | return A_LastError 78 | f.Write(text) 79 | f.Close(), DllCall("CloseHandle", "Ptr", pipe[2]) ;// close pipe handle 80 | ExitApp() 81 | } 82 | 83 | static _getFullPathName(path) { 84 | cc := DllCall("GetFullPathNameW", "str", path, "uint", 0, "ptr", 0, "ptr", 0, "uint") 85 | buf := Buffer(cc * 2, 0) 86 | DllCall("GetFullPathNameW", "str", path, "uint", cc, "ptr", buf, "ptr", 0, "uint") 87 | return StrGet(buf) 88 | } 89 | } -------------------------------------------------------------------------------- /src/Utils/HotkeyHelper.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: HotkeyHelper 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-16 7 | * @Description: Hotkey Settings Tool Functions 8 | */ 9 | 10 | class HotkeyHelper { 11 | /** 12 | * @description: 13 | * @param key the hotkey without modifier symbols 14 | * @param time The longest interval from first release to second press 15 | * @param CB Long press callback 16 | * @param block set false to add '~' for hotkey 17 | * @param physical set true to add '$' for hotkey 18 | * @return total hotkey string 19 | */ 20 | static longPressHotKey(key, time, CB, block := false, physical := true) { 21 | totalKey := Format("{}{}{}", block ? "" : "~", physical ? "$" : "", key) 22 | time := String(Round(time, 1)) 23 | handler(*) { 24 | if (!KeyWait(key, "T" time)) { 25 | CB() 26 | ; Endless waiting for release to avoid repeated calls 27 | KeyWait(key) 28 | } 29 | } 30 | Hotkey(totalKey, handler, "On") 31 | return totalKey 32 | } 33 | 34 | /** 35 | * @description: 36 | * @param key the hotkey without modifier symbols 37 | * @param time The longest interval from first release to second press 38 | * @param doubleCB double press callback 39 | * @param singleCB single press callback 40 | * @param block set false to add '~' for hotkey 41 | * @param physical set true to add '$' for hotkey 42 | * @return total hotkey string 43 | */ 44 | static doubleAndSingleHotKey(key, time, doubleCB, singleCB := (*) => 0, block := false, physical := true) { 45 | totalKey := Format("{}{}{}", block ? "" : "~", physical ? "$" : "", key) 46 | time := String(Round(time, 1)) 47 | handler(*) { 48 | ; Wait for release 49 | if (KeyWait(key)) { 50 | if (KeyWait(key, "DT" time) && A_PriorKey = key) { 51 | doubleCB() 52 | KeyWait(key) 53 | } else 54 | singleCB() 55 | } 56 | } 57 | Hotkey(totalKey, handler, "On") 58 | return totalKey 59 | } 60 | 61 | /** 62 | * @description: example of double and long press for the same key. 63 | * @param key the hotkey without modifier symbols 64 | * @param time1 Press for more than this time, it means long press 65 | * @param time2 The longest interval from first release to second press 66 | * @param longCB Long press callback 67 | * @param doubleCB double press callback 68 | * @param block set false to add '~' for hotkey 69 | * @param physical set true to add '$' for hotkey 70 | * @return total hotkey string 71 | */ 72 | static doubleAndLongHotkey(key, time1, time2, longCB, doubleCB, block := false, physical := true) { 73 | ; Construct key string 74 | totalKey := Format("{}{}{}", block ? "" : "~", physical ? "$" : "", key) 75 | ; Round the time and convert it to string 76 | time1 := String(Round(time1, 1)) 77 | time2 := String(Round(time2, 1)) 78 | handler(*) { 79 | if (KeyWait(key, "T" time1)) { 80 | ; Released within time1, waiting for the second press 81 | if (KeyWait(key, "DT" time2) && A_PriorKey = key) { 82 | ; Second Press within time2 83 | doubleCB() 84 | ; Endless waiting for release to avoid repeated calls 85 | KeyWait(key) 86 | ; if you like, you can add 'KeyWait(key, "DT" time2)' for triple key, uadruple key... 87 | } 88 | } else { 89 | ; Press over the time1 90 | longCB() 91 | ; Endless waiting for release to avoid repeated calls 92 | KeyWait(key) 93 | ; Finally, the long press released. 94 | } 95 | } 96 | Hotkey(totalKey, handler, "On") 97 | return totalKey 98 | } 99 | } -------------------------------------------------------------------------------- /src/Gui/BootGui.ah2: -------------------------------------------------------------------------------- 1 | class BootGui { 2 | ; static gui := unset 3 | ; static listView := unset 4 | ; static itemList := unset 5 | ; static menu := unset 6 | static menuRow := 0 7 | 8 | static init() { 9 | this.gui := Gui("-Resize", "自启界面") 10 | this.gui.BackColor := "FFFFFF" 11 | this.gui.SetFont("s14 q5 c333333", "NSimSun") 12 | this.gui.SetFont(, "雅痞-简") ;优先使用更好看的字体 13 | 14 | this.gui.OnEvent("Close", (guiObj) => this.gui.Hide()) 15 | this.gui.OnEvent("DropFiles", this.addItem) 16 | 17 | this.listView := this.gui.Add("ListView", "x0 y0 w300 h300 -Multi -hdr Grid", [""]) 18 | this.listView.SetFont("s12") 19 | this.listView.OnEvent("ContextMenu", (_lv, rowNum, *) => this.showMenu(rowNum)) 20 | this.listView.OnEvent("DoubleClick", (_lv, rowNum) => this.startSelectedItem(rowNum)) 21 | 22 | 23 | this.gui.Show("Hide w300 h300") 24 | this.menuInit() 25 | 26 | ; 判断是否刚开机 27 | for arg in A_Args { 28 | if (arg = "the_Startup") { 29 | this.boot() 30 | break 31 | } 32 | } 33 | } 34 | 35 | static menuInit() { 36 | this.menu := Menu() 37 | this.menu.Add("添加`t(&A)", (*) => this.choseAddItem()) 38 | this.menu.Add("刷新`t(&R)", (*) => this.loadItems()) 39 | this.menu.Add("删除`t(&X)", (*) => this.delItem(this.menuRow)) 40 | this.menu.Add("目录`t(&X)", (*) => Run(GlobalData.bootDir)) 41 | 42 | this.menu.SetIcon("1&", GlobalData.imgDir "\add.ico") 43 | this.menu.SetIcon("2&", GlobalData.imgDir "\reload.ico") 44 | this.menu.SetIcon("3&", GlobalData.imgDir "\delete.ico") 45 | this.menu.SetIcon("4&", GlobalData.imgDir "\folder.ico") 46 | } 47 | 48 | static showGui() { 49 | this.loadItems() 50 | this.gui.Show() 51 | } 52 | 53 | ; 显示菜单 54 | static showMenu(rowNum) { 55 | if (rowNum) { 56 | this.menuRow := rowNum 57 | this.menu.Enable("3&") 58 | } else { 59 | this.menu.Disable("3&") 60 | } 61 | this.menu.Show() 62 | } 63 | 64 | ; 加载boot项 65 | static loadItems() { 66 | this.itemList := [] 67 | this.listView.Delete() 68 | loop files GlobalData.bootDir "\*.*" { 69 | this.itemList.Push(A_LoopFileFullPath) 70 | this.listView.Add(, A_LoopFileName) 71 | } 72 | } 73 | 74 | ; 启动点击项 75 | static startSelectedItem(rowNum) { 76 | if (rowNum) { 77 | Start.startFile(this.itemList[rowNum]) 78 | } 79 | } 80 | 81 | ; 选择文件路径 82 | static choseAddItem() { 83 | res := FileSelect("M35", , "请选择文件") 84 | if (res.Length) { 85 | this.addItem(res) 86 | } 87 | } 88 | 89 | ; 添加回调 90 | static addItem(args*) { 91 | fileList := args.Length == 1 ? args[1] : args[2] 92 | if (fileList.Length) { 93 | for filePath in fileList { 94 | if (SubStr(filePath, -4) == ".lnk") ; 快捷方式直接复制 95 | FileCopy(filePath, GlobalData.bootDir) 96 | else { ; 创建快捷方式到boot文件夹 97 | SplitPath(filePath, , &outDir, , &fileNameNoExt) 98 | FileCreateShortcut(filePath, GlobalData.bootDir "\" fileNameNoExt ".lnk", outDir) 99 | } 100 | } 101 | BootGui.loadItems() ; 重载列表项 102 | ; 此处this不一定是BootGui 103 | } 104 | } 105 | 106 | ; 删除 107 | static delItem(rowNum) { 108 | fileName := this.listView.GetText(rowNum) 109 | if (MsgBox("当前操作不可恢复,是否将当前项删除?", "提示", 0x2044) = "Yes") { 110 | FileDelete(GlobalData.bootDir "\" fileName) 111 | this.itemList.RemoveAt(rowNum) 112 | this.listView.Delete(rowNum) 113 | } 114 | } 115 | 116 | ; 启动 117 | static boot() { 118 | loop files GlobalData.bootDir "\*.*" 119 | Run(A_LoopFileFullPath) 120 | } 121 | } -------------------------------------------------------------------------------- /docs/history.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 更新日志 3 | icon: clock 4 | --- 5 | 6 | ## [Starter v0.5.3](https://github.com/AkiChase/Starter/releases/tag/v0.5.3) (2023-05-12) 7 | 1. 新增: 窗口切换插件 - [相关文档](https://AkiChase.github.io/Starter/plugin/window-switcher) 8 | 2. 修复: PluginHelper模块注释内容错误 9 | 10 | ## [Starter v0.5.2](https://github.com/AkiChase/Starter/releases/tag/v0.5.2) (2023-05-11) 11 | 12 | 1. 新增: 关键词快捷键 - [相关文档](https://AkiChase.github.io/Starter/guide/setting/#控制) 13 | 2. 优化: 稳定搜索框自动隐藏的时间 14 | 3. 修复: 文件选择对话框导航在某些窗口不生效的问题 15 | 4. 修复: 智能模式下打开文件搜索插件的错误 16 | 17 | ## [Starter v0.5.1](https://github.com/AkiChase/Starter/releases/tag/v0.5.1) (2023-04-16) 18 | 19 | 1. 新增: 完善文档内容 - [插件开发](https://AkiChase.github.io/Starter/dev/) 20 | 2. 新增: 完善文档内容 - [API](https://AkiChase.github.io/Starter/api/) 21 | 3. 新增: pluginHelper插件工具模块完整注释 22 | 4. 修复: 修复双击类型快捷键,双击期间按下其他按键仍会触发的问题 23 | 5. 修复: 导入数据界面按钮失效 24 | 6. 修复:显示插件模式切换错误 25 | 26 | ## [Starter v0.5.0](https://github.com/AkiChase/Starter/releases/tag/v0.5.0) (2023-04-14) 27 | 28 | 1. 新增:添加工作窗口信息作为插件智能模式匹配条件 29 | 2. 新增:文件搜索插件,新增explorer工作窗口下在当前文件夹搜索 30 | 3. 新增:文件选择对话框导航插件,效果类似于Listary的Ctrl+G功能 31 | 4. 新增:使用ImagePut作为图片相关处理工具,添加相关PluginHelper API 32 | 5. 新增:新增OCR识别文字插件,调用百度OCR进行图片识别 33 | 6. 新增:设置界面,添加快捷键修改功能,丰富快捷键选择 34 | 7. 新增:更新相关插件文档,设置界面文档 35 | 8. 优化:提升使用体验,修复快捷键卡顿问题 36 | 9. 修复:修复已知问题 37 | 38 | ## [Starter v0.4.1](https://github.com/AkiChase/Starter/releases/tag/v0.4.1) (2023-04-11) 39 | 40 | 1. 新增: 导入用户数据插件前,询问是否导入,避免误操作覆盖插件 41 | 2. 新增: [Starter 文档](https://AkiChase.github.io/Starter/)✨ 42 | 3. 新增: 自启界面添加打开自启目录菜单项 43 | 4. 修复: 文件搜索插件错误 44 | 45 | ## [Starter v0.4.0](https://github.com/AkiChase/Starter/releases/tag/v0.4.0) (2023-04-08) 46 | 47 | 1. 新增: 长按获取选中内容,并粘贴到智能模式搜索框中。若未选中任何内容则获取工作窗口信息,进入窗口匹配模式 48 | 2. 新增: 可粘贴文件、位图(图片)到智能、插件模式搜索框中 49 | 3. 新增: 可拖拽文件到智能、插件模式搜索框中 50 | 4. 新增: 点击粘贴的文件图标,显示文件信息 51 | 5. 新增: 窗口匹配模式下点击智能模式图标,显示工作窗口信息 52 | 6. 新增: 文件搜索插件支持粘贴单文件夹,快速在文件夹内搜索 53 | 7. 新增: 搜索界面中使用Alt+N (N为0-9, 0表示第10行) 快速选中第N行 54 | 8. 调整: 修改插件智能模式匹配条件为自定义函数匹配 55 | 9. 修复: 修复已知问题,优化使用细节 56 | 57 | ## [Starter v0.3.3](https://github.com/AkiChase/Starter/releases/tag/v0.3.3) (2023-04-06) 58 | 59 | 1. 新增: "文件搜索"插件,可调用Everything进行文件搜索 60 | 2. 调整: 将“双击Left”快捷键调整为“双击Right” 61 | 3. 调整: 增加软件搜索界面尺寸,显示更多内容 62 | 4. 调整: 更新“demo”插件示例,修复问题 63 | 5. 优化: 优化细节使用体验 64 | 6. 修复: 修复已知问题 65 | 66 | ## [Starter v0.3.2](https://github.com/AkiChase/Starter/releases/tag/v0.3.2) (2023-04-04) 67 | 68 | 1. 新增: "网页搜索"插件新增必应搜索 69 | 2. 修复: 删除、编辑选中项错位等bug 70 | 3. 修复: 网页搜索插件bug 71 | 72 | ## [Starter v0.3.1](https://github.com/AkiChase/Starter/releases/tag/v0.3.1) (2023-04-04) 73 | 74 | 1. 调整: 调整图标尺寸 75 | 2. 修复: 修复bugs 76 | 77 | ## [Starter v0.3.0](https://github.com/AkiChase/Starter/releases/tag/v0.3.0) (2023-04-04) 78 | 79 | 1. 新增: 插件模式及相关插件API 80 | 2. 新增: "网页搜索"插件,支持百度、谷歌搜索,可使用代理 81 | 3. 调整: 更新demo.ahk插件示例 82 | 4. 调整: 搜索界面自动隐藏后15s内保留搜索结果 83 | 5. 调整: 热键"Shift+Esc" 改为 "Esc" 84 | 6. 调整: 使用更具统一风格的图标 85 | 7. 修复: 修复小bug 86 | 87 | ## [Starter v0.2.2](https://github.com/AkiChase/Starter/releases/tag/v0.2.2) (2023-04-02) 88 | 89 | 1. 新增: 定时提醒插件 90 | 2. 新增: 使用更美观的消息通知组件 91 | 3. 调整: 更新demo插件示例至v0.0.2 92 | 4. 修复: 添加检查更新无响应的消息反馈 93 | 5. 修复: 修复快捷方式图标不正确问题 94 | 6. 优化: 使用更简单的插件图标加载方式 95 | 96 | ## [Starter v0.2.1](https://github.com/AkiChase/Starter/releases/tag/v0.2.1) (2023-04-01) 97 | 98 | 1. 调整: 修改软件图标 99 | 100 | ## [Starter v0.2.0](https://github.com/AkiChase/Starter/releases/tag/v0.2.0) (2023-04-01) 101 | 102 | 1. 新增: 插件界面 103 | 2. 新增: 长按“CapsLK”以智能模式搜索选中内容 104 | 3. 优化: 用户数据导入导出优化,使用7zip进行压缩、解压,支持导入/导出数据、插件 105 | 4. 调整: 取消文件编译,使用AutoHotkey64.exe直接运行源码 106 | 5. 调整: 移动"自启目录"到“用户数据”目录下 107 | 6. 调整: reg匹配模式下搜索结果排序微调 108 | 109 | ## [Starter v0.1.1](https://github.com/AkiChase/Starter/releases/tag/v0.1.1) (2023-02-20) 110 | 111 | 1. 修复: 错误修复 112 | 113 | ## [Starter v0.1.0](https://github.com/AkiChase/Starter/releases/tag/v0.1.0) (2023-02-20) 114 | 115 | 1. 新增: 智能模式 116 | 2. 新增: 关于界面,检查更新 117 | 3. 新增: 用户数据导入、导出 118 | 4. 调整: 完善README 119 | 5. 优化: UI细节 120 | 121 | ## [Starter v0.0.5](https://github.com/AkiChase/Starter/releases/tag/v0.0.5) (2023-02-08) 122 | 123 | 1. 修复: 一些小bug,细节优化 124 | 2. 新增: 双击自启界面列表项可以启动对应项 125 | 126 | ## [Starter v0.0.4](https://github.com/AkiChase/Starter/releases/tag/v0.0.4) (2023-01-17) 127 | 128 | 1. 调整: 添加搜索框50ms防抖 129 | 2. 修复: 解决无启动项时无法打开编辑界面的问题 130 | 3. 修复: 添加启动项目失败的异常处理 131 | 132 | ## [Starter v0.0.3](https://github.com/AkiChase/Starter/releases/tag/v0.0.3) (2023-01-09) 133 | 134 | 1. 调整: 移除搜索框防抖 135 | 2. 修复: lnk文件图标问题 136 | 137 | ## [Starter v0.0.2](https://github.com/AkiChase/Starter/releases/tag/v0.0.2) (2023-01-05) 138 | 139 | 1. 修复:一些小bug 140 | 141 | ## [Starter v0.0.1](https://github.com/AkiChase/Starter/releases/tag/v0.0.1) (2023-01-04) 142 | 143 | 1. 发布: 第一个版本 144 | -------------------------------------------------------------------------------- /src/Plugin/定时提醒.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: 定时提醒.ahk 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-06 7 | * @Description: 定时提醒 可设置定时时间和提示语 8 | */ 9 | 10 | /* 11 | ===Starter Plugin Info==> 12 | { 13 | "author": "AkiChase", 14 | "version": "0.0.1", 15 | "introduction": "定时提醒, 可设置定时时间和提示语", 16 | "icon": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABXJJREFUWEfNVwtsU2UU/s7fhQUYEGAbDw0D5CGi8wEhPBKdQduuBcNYr0YUAoqIkJigYAQlYJwQohgjykvi2FQI3tuBQm97K2QEGApmTkMcTweyBcSMgYypY9x7zN+u2K3tVh4J3KRJe//zn/Od7//+c04Jt/mh2xwfdx6A3FzlPousBQByQNTAjKAl6KOdO9TT18PW+Ly8nimNKevA1igmSifgOIj3COvv+X6/vzHiqwUDdrdnLDHKWgciUC2I5wR8mpoMCIdD6QEb7wTwcBz7ckPXRsYAyMlR0lI74ygsToXgzWSS17JRvbCsWUwkPy/TVWgsOJeAmSDuC1CfsCM+C6YzIHzFNtolmpqYha2UiGQyJWTRXhassAUFhBzpK+hT18ud1xhwupUXmXkDwBsN3TsjGrnTmZ8NoqlMmAqgV3ssEPN6S4j1QZ9aHm3ryFUKQPwWQNsMXc1rAcDh8nwNQAHYY+heb2Sj0+VZyMCyKEclTPSTMK0KZq64mppqiaamB0kgm5gl0Dww0pqdLwro2vJrvpyT+rNIOQngoqFr3VsByC8EaDoxVgX82qty0e7yLCVgSbODI8y0OOhXtbYYaNbR+wDGhg4HeCeoa0vld6dr8nSGKARzteH39msBIDqYoWvknKCMYYv3x8skBM5u70y2rrPJxocDO7x6a1BOl/Iag1eG9hOelgK2uxUfMbsA8hq66mkBQP5w5HpKIZDR2IlGpzbwHqliJpoT9KlrYgI4PTksUArGbsOvPR6PlYJZj32+tyYjpCcmGklsrQEo5Wqq+eSurVvPxwCQL2QdMImfJ2BhNNIbAcBFmdkFZcN+2VeTASlMQSkrTdOqNQy1LuYaRl64XM8MMWEeBeiCSWLcTt+Ww/GycybBAG/K7HX6fMc/5u16xGq4YhM22Ibq+pZj0f5iSrEj1/M6CB8AvMnQvc8lElwyAELUF2Xwih+GofT3TKnI+YZfC+kiIQMhHRByCFgc0LWCmwHAhRkPQaBiU2UWig/1lwBi9BLLgCtf0j+EgYlBXdtxkwCmQ6DwwJmeWLL3fsnHMUP3Dm2bAZfnEoAuMK2+hlFyNhkArW0IOCRrCRenrwDTG3X/dMCUb8dIs3pD17reEgDhrsm/JgIoEwg8u28tiJ+q+7cDpnyTNIDwEZDF7kAgtsBEB7S7lRHC5C6tQXAHqgnkl94L0Ha59uPZHli854Ekj6BZhEz0dtCnvpcwwzYW+ONBqej2Vw2AdGm2ubIfig4NSFaEnpkAPpNt1NC1/BsCUJQhm9nkyN53y4ajrCaE5SVD1za0qQH7RGUAmVwVLpPWjIBesvF6QHBRejFAsm2Hnu9O9sbKg2Hhs40GBrershsmrgNyxe7yLJOl2Ea4pPu0bskA4OKMuWDI1ttCE8rWcai/kiK74vKgri2Kc2Ni3ecoSlqkGWV1baheZz8wjl6oq44R25c97oZlk/KeAMa01utv7s7Gz+dCbb+isTM9ultVLycFQBo5ncoYFrwNQOb4rHPmgtFHjoNwCowTYHQE8WiAhidiZ1X5YPhO9JXLf5JFkwIB9ft4tm2O5U63R2GGnJSQ3rER80Ydw4je1xpZ3NhVF9PwSflgVNaG601kFkgENCEAhyt/NkDhOUBmTRgkv469qxZZ3RowqPtlDO1ZDwHG8Qtd8NvFNFRf6oT9NelmoylsDFQRMDAcmF8xdO/apBloOYrRNENXv7C7PBMImCNHhnZEeZDZ+jDoL9nicClTAS4O5/D/aJbMLfhUBotHn8Od/wRbGEyC7pEZMhOB+BQxn7RAVa0bWOQYGVgd1LW5SYnQ4ZjcRwhbd79frUzmCrZnE+oblnkhXnO78/4btpfNrV7/D4b1gz/RPr0sAAAAAElFTkSuQmCC" 17 | } 18 | <==Starter Plugin Info=== 19 | */ 20 | #Include ..\Utils\PluginHelper.ah2 21 | PluginHelper.addEntryFunc((*) => Plugin_定时提醒.main()) ; 添加入口函数等待执行 22 | 23 | class Plugin_定时提醒 { 24 | static time := 30 25 | static words := "起来走走, 休息一下眼睛!`n记得要喝水, 保持身体 hydrated!" 26 | static state := false 27 | static menu := Menu() 28 | 29 | 30 | static toggleState() { 31 | if (this.state) { 32 | SetTimer(this.f, 0) ;关闭计时器 33 | this.menu.Uncheck("开启") 34 | } else { 35 | SetTimer(this.f, this.time * 60 * 1000) ;开启计时器 36 | PluginHelper.Utils.tip("定时提醒已开启", "提醒间隔时间: " this.time "分钟", 2500) 37 | this.menu.check("开启") 38 | } 39 | this.state := !this.state 40 | } 41 | 42 | static setting() { 43 | g := Gui(, "定时提醒设置") 44 | g.BackColor := "FFFFFF" 45 | g.SetFont("s14 q5 c333333", "NSimSun") 46 | g.SetFont(, "雅痞-简") ;优先使用更好看的字体 47 | g.AddText(, "提醒间隔时间(min)") 48 | timeEdit := g.AddEdit("w300 ", this.time) 49 | g.AddText(, "提醒内容") 50 | wordsEdit := g.AddEdit("w300 h100", this.words) 51 | f(*) { 52 | this.time := timeEdit.Value, this.words := wordsEdit.Value, this.state := false 53 | this.storeData() 54 | g.Destroy() 55 | this.toggleState() 56 | } 57 | g.AddButton("x135", "保存").OnEvent("Click", f) 58 | g.OnEvent("Close", (*) => g.Destroy()) 59 | g.Show() 60 | } 61 | 62 | static storeData() { 63 | SplitPath(A_LineFile, , &dir) 64 | dataPath := dir "\定时提醒配置.txt" 65 | f := FileOpen(dataPath, "w") 66 | f.Write(this.time "`r`n" this.words) 67 | f.Close() 68 | } 69 | 70 | static loadDataAndStart() { 71 | SplitPath(A_LineFile, , &dir) 72 | dataPath := dir "\定时提醒配置.txt" 73 | if (FileExist(dataPath)) { 74 | content := FileRead(dataPath) 75 | sep := "`r`n" 76 | i := InStr(content, sep) 77 | this.time := Float(SubStr(content, 1, i - 1)) 78 | this.words := SubStr(content, i + StrLen(sep)) 79 | this.toggleState() 80 | } else { 81 | this.setting() 82 | } 83 | } 84 | 85 | static main() { 86 | FileEncoding("UTF-8-RAW") 87 | this.menu.Add("开启", (*) => this.toggleState()) 88 | this.menu.Add("设置", (*) => this.setting()) 89 | PluginHelper.pluginMenu.Add("定时提醒", this.menu) 90 | this.f := (*) => PluginHelper.Utils.tip(Format("定时提醒: {}min", this.time), this.words) 91 | this.loadDataAndStart() 92 | } 93 | } -------------------------------------------------------------------------------- /docs/api/utils/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 其他 3 | author: AkiChase 4 | order: 1 5 | date: 2023-04-14 6 | --- 7 | 8 | ## chineseFirstChar(str){#chineseFirstChar} 9 | 10 | 将文本的中文部分转换为拼音首字母,多音字转换不准确 11 | 12 | - 参数 13 | - \{String\} `str`: 要进行转换的文本 14 | 15 | - 返回值 16 | - \{String\} 将中文部分转为拼音首字母后的字符串 17 | 18 | ## strStartWith(ori, sub, caseSense := false){#strStartWith} 19 | 20 | ori是否以sub开头 21 | 22 | - 参数 23 | - \{String\} `ori`: 进行判断的文本 24 | - \{String\} `sub`: 开头的文本 25 | - \{Bool\} `caseSense`: 是否大小写敏感 26 | 27 | - 返回值 28 | - \{Bool\} `true` 则 ori 以 sub 开头 29 | 30 | ## pathStrCompact(fullPath, maxChars){#pathStrCompact} 31 | 32 | 压缩文件路径文本,使其不超过最大字符数 33 | 34 | - 参数 35 | - \{String\} `fullPath`: 文件完整路径 36 | - \{Number\} `maxChars`: 压缩后最大字符数 37 | 38 | - 返回值 39 | - \{String\} 压缩后的文件路径文本 40 | 41 | ## copyToClipboard(filePath, cut := false){#copyToClipboard} 42 | 43 | 复制指向文件的路径到剪切板,可选择进行剪切而不是复制 44 | 45 | - 参数 46 | - \{String\} `filePath`: 指向文件的路径 47 | - \{Bool\} `cut`: 是否剪切,默认为 `false` 48 | 49 | - 返回值 50 | - 无 51 | 52 | ## startFile(path, workingDir := "", options := "", beforeRun?){#startFile} 53 | 54 | 运行指定路径的文件 55 | 56 | - 参数 57 | - \{String\} `path`: 文件路径 58 | - \{String\} `workingDir`: 工作目录,见[Run / RunWait - 语法 & 使用 | AutoHotkey v2](https://orz707.gitee.io/v2/docs/commands/Run.htm) 59 | - \{String\} `options`: 见[Run / RunWait - 语法 & 使用 | AutoHotkey v2](https://orz707.gitee.io/v2/docs/commands/Run.htm) 60 | - \{[Closure](https://orz707.gitee.io/v2/docs/Functions.htm#closures)\} `beforeRun`: 运行文件前执行的处理函数 61 | 62 | - 返回值 \{String\} 63 | - PID 如果无法确定 PID, 返回空字符串 64 | 65 | ## openFileInFolder(path){#openFileInFolder} 66 | 67 | 在资源管理器中显示指定路径的文件 68 | 69 | - 参数 70 | - \{String\} `path`: 文件路径 71 | 72 | - 返回值 73 | - 无 74 | 75 | ## UrlEncode(str){#UrlEncode} 76 | 77 | 对给定字符串进行 URL 编码 78 | 79 | - 参数 80 | - \{String\} `str`: 需要编码的字符串 81 | 82 | - 返回值 83 | - \{String\} URL 编码后的字符串 84 | 85 | ## UrlDecode(str){#UrlDecode} 86 | 87 | 对给定字符串进行 URL 解码 88 | 89 | - 参数 90 | - \{String\} `str`: 需要解码的字符串 91 | 92 | - 返回值 93 | - \{String\} URL 解码后的字符串 94 | 95 | ## globalMatch(Haystack, NeedleRegEx, StartingPos := 1){#globalMatch} 96 | 97 | 从给定字符串的指定起始位置处查找符合正则表达式的所有子字符串,即正则全局模式 98 | 99 | 参数可以参考[RegExMatch - 语法 & 使用 | AutoHotkey v2](https://orz707.gitee.io/v2/docs/commands/RegExMatch.htm) 100 | 101 | - 参数 102 | - \{String\} `Haystack`: 给定字符串 103 | - \{RegExp\} `NeedleRegEx`: 正则表达式对象 104 | - \{Number\} `StartingPos`: 起始位置,默认为 1 105 | 106 | - 返回值 107 | - \{Array\} 匹配对象数组,参考[匹配对象 - MatchObject](https://orz707.gitee.io/v2/docs/commands/RegExMatch.htm#MatchObject) 108 | 109 | ## Jxon_Load(src, args*){#Jxon_Load} 110 | 111 | 将给定 JSON 文本数据加载成为 AutoHotkey 变量 112 | 113 | :::warning 114 | 对象转换后都会变成 Map 类型 115 | ::: 116 | 117 | - 参数 118 | - \{String\} `src`: JSON 文本的引用(因为 AHK 中没有传递引用的概念) 119 | - `args`:额外参数列表,需要则查看 `src\Utils\JXON.ah2` 源码 120 | 121 | - 返回值 122 | - AutoHotkey 变量 123 | 124 | ## Jxon_Dump(obj, indent := "", lvl := 1){#Jxon_Dump} 125 | 126 | 将给定的 AutoHotkey 变量转换为 JSON 文本 127 | 128 | :::warning 129 | 不支持 Object 类型,请使用 Map 类型代替 130 | ::: 131 | 132 | - 参数 133 | - \{*\} `obj`:需要转换的 AutoHotkey 变量 134 | - \{String\} `indent`:缩进用的字符串,默认为空字符串 135 | - \{Number\} `lvl`:当前递归的深度,默认为 1 136 | 137 | - 返回值 138 | - \{String\} 转换后的 JSON 文本 139 | 140 | ## WinHttp(args*){#WinHttp} 141 | 142 | 参考 WinHttp 库函数源码,`src\Utils\WinHttp.ah2` 143 | 144 | - 参数 145 | - `args`:参数列表,根据不同的请求类型和参数,具体参数需要参考文档说明 146 | 147 | - 返回值 148 | - \{WinHttp\} WinHttp 对象 149 | 150 | ## associatedHIcon(filePath){#associatedHIcon} 151 | 152 | 获取 Windows 中与指定文件关联的图标,并返回一个 hIcon 句柄 153 | 154 | - 参数 155 | - \{String\} `filePath`:需要获取关联图标的文件路径 156 | 157 | - 返回值 158 | - \{Int\} 获取到的关联图标的 HICON 句柄 159 | 160 | # base64ToHICON(base64){#base64ToHICON} 161 | 162 | 载入无头部的图片base64文本,返回 hIcon 句柄 163 | 164 | ## 参数 165 | 166 | - \{String\} `base64`:无头部的图片base64文本 167 | 168 | ## 返回值 169 | 170 | - \{Int\} 载入后图标的 HICON 句柄 171 | 172 | ## tip(title, content, time?, unique := false){#tip} 173 | 174 | 在右下角弹出一条 `WiseGui` 通知,可设置标题、内容、计时消失时间、是否唯一,返回该通知的 id 175 | 176 | - 参数 177 | - \{String\} `title`:通知标题 178 | - \{String\} `content`:通知内容 179 | - \{Number\} `time`:计时消失时间(单位为毫秒),选填,默认为 0,表示不消失 180 | - \{Boolean\} `unique`:是否保证唯一(不会被覆盖),选填,默认为 false 181 | 182 | - 返回值 183 | - \{String\} 该通知窗口的 id(如果 `unique` 为 true,则返回 A_TickCount 值作为 id) 184 | 185 | ## quickSort(arr, fn){#quickSort} 186 | 187 | 对给定的数组进行**快速排序**(不生成副本),并返回排序后的数组 188 | 189 | ```ahk 190 | ; 升序排序示例 191 | arr:=[1,9,2,8,3,7] 192 | QuickSort(arr, (a,b)=>a-b) 193 | ``` 194 | 195 | - 参数 196 | - `arr`:待排序数组 197 | - `fn`:比较函数,接受两个参数 itemA 和 itemB,返回值为数字类型,表示 itemA 和 itemB 的大小关系。如果 itemA 大于 itemB,返回正数;如果 itemA 小于 itemB,返回负数;如果 itemA 等于 itemB,返回 0。 198 | 199 | - 返回值 200 | - 排序后的数组 -------------------------------------------------------------------------------- /src/Gui/PluginGui.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: PluginGui 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-13 7 | * @Description: 插件界面 8 | */ 9 | 10 | class PluginGui { 11 | ; static gui:=unset 12 | ; static LV:=unset 13 | static pluginList := [] 14 | static IL := 0 15 | 16 | static init() { 17 | if (!IsSet(DEBUG) || !DEBUG) 18 | this.loadPlugins() 19 | this.gui := Gui("-Resize", "插件界面") 20 | this.gui.SetFont("s12 q5 c333333", "NSimSun") 21 | this.gui.SetFont(, "雅痞-简") ;优先使用更好看的字体 22 | this.gui.BackColor := "FFFFFF" 23 | 24 | this.LV := this.gui.Add("ListView", "x0 y0 w500 h320 -E0x200 Grid", ["名称", "启用", "作者", "版本", "介绍"]) 25 | this.LV.OnEvent("DoubleClick", (_LV, rowNum) => this.togglePluginEnabled(rowNum)) 26 | 27 | this.gui.AddButton("x120 y330", "刷新列表").OnEvent("Click", (*) => this.refreshPluginsList()) 28 | this.gui.AddButton("x280 y330", "保存重载").OnEvent("Click", (*) => this.storeAndReload()) 29 | 30 | this.gui.OnEvent("Close", (guiObj) => guiObj.Hide()) 31 | this.gui.Show("w500 h380 Hide") 32 | 33 | this.refreshPluginsList() 34 | } 35 | 36 | static loadPlugins() { 37 | fList := [] 38 | pluginList := [] 39 | for name in GlobalData.config["plugin"] { 40 | path := GlobalData.pluginDir "\" name 41 | if (FileExist(path)) ; 仅添加有效的文件 42 | fList.Push(GlobalData.pluginDir "\" name), pluginList.Push(name) 43 | } 44 | 45 | ; 如果插件列表出现变动 46 | if (pluginList.Length != GlobalData.config["plugin"].Length) { 47 | GlobalData.config["plugin"] := pluginList 48 | GlobalData.storeConfig() 49 | } 50 | Loader.loadFromFiles(fList) 51 | } 52 | 53 | static storeAndReload() { 54 | plugin := [] 55 | for info in this.pluginList { 56 | if (info["enabled"]) { 57 | plugin.Push(info["name"]) 58 | } 59 | } 60 | GlobalData.config["plugin"] := plugin 61 | Reload() 62 | } 63 | 64 | static togglePluginEnabled(rowNum) { 65 | if (rowNum > 0) { 66 | flag := this.pluginList[rowNum]["enabled"] 67 | this.pluginList[rowNum]["enabled"] := !flag 68 | this.LV.Modify(rowNum, , , flag ? "" : "是") 69 | this.gui.Title := "插件界面 *未保存" 70 | } 71 | } 72 | 73 | static refreshPluginsList() { 74 | this.LV.Opt("-Redraw") 75 | this.LV.Delete() 76 | if (this.IL) 77 | IL_Destroy(this.IL) 78 | this.IL := IL_Create() 79 | this.LV.SetImageList(this.IL) 80 | 81 | 82 | ; 插件文件后缀判断 83 | fList := [] 84 | Loop Files, GlobalData.pluginDir "\*.*" { 85 | if (InStr(A_LoopFileExt, "ahk") || InStr(A_LoopFileExt, "ah2")) 86 | fList.Push(A_LoopFileFullPath) 87 | } 88 | 89 | this.pluginList := [] 90 | 91 | if (!fList.Length) { 92 | this.LV.Opt("Redraw") 93 | return 94 | } 95 | 96 | s := "===Starter Plugin Info==>", e := "<==Starter Plugin Info===", len := StrLen(s) 97 | pluginCopy := GlobalData.config["plugin"].Clone() 98 | 99 | 100 | for f in fList { 101 | content := FileRead(f) 102 | l := InStr(content, s) + len, r := InStr(content, e) 103 | if (r <= l || l == len) ;未解析到插件信息,跳过 104 | continue 105 | 106 | content := SubStr(content, l, r - l) 107 | info := Jxon_Load(&content) 108 | 109 | ; 插件信息格式不正确,跳过 110 | if (Type(info) != "Map") 111 | continue 112 | 113 | SplitPath(f, &name) 114 | info["name"] := name 115 | 116 | ; 默认值 117 | for name in ["author", "version", "introduction", "icon"] { 118 | if (!info.Has(name)) 119 | info[name] := "" 120 | } 121 | 122 | info["filePath"] := f 123 | 124 | ; 加载图标至图像列表 125 | base64 := info["icon"] 126 | info["iconBase64"] := base64 127 | if (base64) { 128 | hIcon := LoadIconFromBase64(base64, 32, 32) 129 | info["icon"] := IL_Add(this.IL, "HICON:*" hIcon) 130 | info["iconHIcon"] := hIcon 131 | } else 132 | info["icon"] := 0 133 | 134 | info["enabled"] := this._isPluginEnabled(pluginCopy, info["name"]) ? 1 : 0 135 | this.pluginList.Push(info) 136 | } 137 | 138 | this.LV.SetImageList(this.IL) 139 | for info in this.pluginList { 140 | this.LV.Add("Icon" info["icon"], A_Space info["name"], 141 | info["enabled"] ? "是" : "", 142 | info["author"], info["version"], info["introduction"]) 143 | } 144 | 145 | 146 | this.LV.ModifyCol() 147 | this.LV.ModifyCol(2, "AutoHdr Center") 148 | 149 | this.LV.Opt("Redraw") 150 | 151 | this.gui.Title := "插件界面" 152 | } 153 | 154 | static _isPluginEnabled(arr, name) { 155 | for i, v in arr { 156 | if (name == v) { 157 | arr.RemoveAt(i) 158 | return true 159 | } 160 | } 161 | return false 162 | } 163 | 164 | static showGui() { 165 | this.gui.Show() 166 | } 167 | } -------------------------------------------------------------------------------- /src/Gui/TrayMenu.ah2: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: TrayMenu 3 | * @Version: 0.0.2 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-14 7 | * @Description: 菜单栏 8 | */ 9 | 10 | class TrayMenu { 11 | ; static menu := unset 12 | ; static 7z:=unset 13 | ; static gui:=unset 14 | 15 | static init() { 16 | A_IconTip := "Starter" 17 | 18 | this.menu := A_TrayMenu 19 | this.menu.Delete() 20 | 21 | this.menu.Add("搜索界面`t双击CapsLock", (*) => StartupMode.showGui()) 22 | this.menu.Add("编辑启动模式`t(&2)", (*) => StartupEditGui.showGui()) 23 | this.menu.Add("编辑智能模式`t(&3)", (*) => IntelligentEditGui.showGui()) 24 | this.menu.Add("自启界面`t(&4)", (*) => BootGui.showGui()) 25 | this.menu.Add("插件界面`t(&5)", (*) => PluginGui.showGui()) 26 | this.menu.Add("设置界面`t(&6)", (*) => SettingGui.showGui()) 27 | this.menu.Add("关于界面`t(&7)", (*) => AboutGui.showGui()) 28 | this.menu.Add() 29 | this.menu.Add("开机启动", (*) => this.toggleStartupState()) 30 | 31 | userDataMenu := Menu() 32 | userDataMenu.Add("打开数据目录`t(&1)", (*) => Start.startFile(GlobalData.dataDir)) 33 | userDataMenu.Add("导出用户数据`t(&2)", (*) => this.guideGui(0)) 34 | userDataMenu.Add("导入用户数据`t(&3)", (*) => this.guideGui(1)) 35 | this.menu.Add("用户数据`t(&D)", userDataMenu) 36 | 37 | this.menu.Add("退出`t(&X)", (*) => ExitApp()) 38 | 39 | this.menu.SetIcon("1&", GlobalData.imgDir "\Starter.png") 40 | this.menu.SetIcon("2&", GlobalData.imgDir "\edit.ico") 41 | this.menu.SetIcon("3&", GlobalData.imgDir "\edit.ico") 42 | this.menu.SetIcon("4&", GlobalData.imgDir "\run.ico") 43 | this.menu.SetIcon("5&", GlobalData.imgDir "\plugin.ico") 44 | this.menu.SetIcon("6&", GlobalData.imgDir "\setting.ico") 45 | this.menu.SetIcon("7&", GlobalData.imgDir "\info.ico") 46 | 47 | if (this.getStartupState()) 48 | this.menu.Check("开机启动") 49 | 50 | this.menu.Default := "1&" 51 | 52 | A_IconHidden := false ; 重载结束后显示图标 53 | } 54 | 55 | /** 56 | * @param mode 0: 导出用户数据; 1: 导入用户数据 57 | */ 58 | static guideGui(mode := 0) { 59 | g := Gui(, mode ? "导入用户数据" : "导出用户数据") 60 | g.BackColor := "FFFFFF" 61 | g.SetFont("s12 q5 c333333", "NSimSun") 62 | g.SetFont(, "雅痞-简") ;优先使用更好看的字体 63 | 64 | g.AddText("x10 y15", mode ? "数据路径" : "保存目录") 65 | pathEdit := g.AddEdit("xp+80 yp-5 w200 ReadOnly") 66 | 67 | g.AddButton("xp+220 yp-5", "选择").OnEvent("Click", 68 | (*) => mode ? 69 | pathEdit.Value := FileSelect(3, A_Desktop, "请选择用户数据文件", "zip (*.zip)") : 70 | pathEdit.Value := DirSelect("*" A_Desktop, , "请选择保存目录") 71 | ) 72 | 73 | g.AddText("x10 yp+50", mode ? "导入内容选项:" : "导出内容选项:") 74 | 75 | checkboxList := [ 76 | g.AddCheckbox("x75 yp+30 Checked", "用户数据"), 77 | g.AddCheckbox("xp+150 yp", "插件"), 78 | ] 79 | 80 | options() { 81 | out := [] 82 | for c in checkboxList 83 | out.Push(c.Value) 84 | return out 85 | } 86 | 87 | g.AddButton("x130 yp+40 h27 w100", mode ? "导 入" : "导 出").OnEvent("Click", 88 | (*) => mode ? this.importUserData(pathEdit.Value, options()) : this.backupUserData(pathEdit.Value, options(), g) 89 | ) 90 | 91 | g.OnEvent("Close", (*) => g.Destroy()) 92 | g.Show() 93 | } 94 | 95 | ; 导出用户数据 96 | static backupUserData(path, options, g) { 97 | if (!this.HasOwnProp("7z")) 98 | this.zipper := SevenZip() 99 | fList := [] 100 | if (options[1]) 101 | fList.Push(GlobalData.dataDir) 102 | if (options[2]) 103 | fList.Push(GlobalData.pluginDir) 104 | 105 | if (!fList.Length) { 106 | Tip.show("提示", "至少选择一项内容", 1500) 107 | return 108 | } 109 | if (!StrLen(path)) { 110 | Tip.show("提示", "请选择保存目录", 1500) 111 | return 112 | } 113 | 114 | newPath := path "\Starter_Data_" A_Now ".zip" 115 | 116 | _files := "" 117 | for v in fList 118 | _files .= v "`n" 119 | this.zipper.Add(newPath, _files, "-tzip -r -mx5 -mmt=2") 120 | g.Destroy() 121 | Tip.show("提示", "用户数据已导出: " newPath, 2500) 122 | } 123 | 124 | ; 导入用户数据 125 | static importUserData(path, options) { 126 | if (!this.HasOwnProp("7z")) 127 | this.zipper := SevenZip() 128 | if !(options[1] || options[2]) { 129 | Tip.show("提示", "至少选择一项内容", 1500) 130 | return 131 | } 132 | if (!StrLen(path)) { 133 | Tip.show("提示", "请选择保存目录", 1500) 134 | return 135 | } 136 | 137 | ; 若勾选插件且未确认则返回 138 | if (options[2] && MsgBox("导入插件时将用导入的数据覆盖所有同名文件,`n请勿在更新导入旧数据时使用, 避免旧版本插件覆盖新版本插件`n是否继续导入?", "提示", 0x2044) != "Yes") { 139 | return 140 | } 141 | 142 | tmpDir := A_Temp "\" A_TickCount "_starter" 143 | this.zipper.Extract(path, tmpDir, , "-aoa") 144 | 145 | res := "" 146 | if (options[1]) { 147 | if (FileExist(dir := tmpDir "\data")) 148 | DirCopy(dir, GlobalData.dataDir, true), res .= "用户数据导入成功`n" 149 | } 150 | if (options[2]) { 151 | if (FileExist(dir := tmpDir "\Plugin")) { 152 | DirCopy(dir, GlobalData.pluginDir, true), res .= "插件导入成功`n" 153 | } 154 | } 155 | DirDelete(tmpDir, true) 156 | if (StrLen(res)) { 157 | GlobalData.onExitHandler := (*) => 0 158 | Tip.show("提示", res "1s后软件重启") 159 | Sleep(1000) 160 | Reload() 161 | ;清除退出保存重启 162 | } else { 163 | Tip.show("提示", "导入失败") 164 | } 165 | } 166 | 167 | ; 获取加入注册表状态 168 | static getStartupState() { 169 | try 170 | return !!RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", "Starter") 171 | catch 172 | return false 173 | } 174 | 175 | ; 切换开机启动 176 | static toggleStartupState() { 177 | if (this.getStartupState()) { 178 | RegDelete("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", "Starter") 179 | this.menu.Uncheck("开机启动") 180 | } else { 181 | cmd := Format('"{}" "{}" "the_Startup"', GlobalData.rootDir "\src\AutoHotkey64.exe", GlobalData.rootDir "\src\Starter.ah2") 182 | RegWrite(cmd, "REG_SZ", "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run", "Starter") 183 | this.menu.check("开机启动") 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /docs/dev/intelligent/advanced.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 进阶 3 | icon: circle-right 4 | author: AkiChase 5 | date: 2023-04-14 6 | order: 2 7 | --- 8 | 9 | 自定义匹配函数的进阶应用自然是各种匹配条件**混合使用**,此外还有一个排序优先级的概念。 10 | 11 | ## 匹配优先级 12 | 13 | `matchHandler` 自定义匹配函数返回的值 `!= 0` 则匹配,而返回值越大,当前插件智能项在搜索结果中越靠前 14 | 15 | :::tip 16 | 优先级约定俗成: 17 | 18 | - ` 1 ` - 当前匹配条件比较广泛,所以设为基础的优先级 19 | - ` 2 ` - 当前匹配条件稍微严格,设为更高的优先级 20 | - `>=3` - 当前匹配条件苛刻,必须排序靠前、置顶 21 | - `< 0` - 当前匹配条件下不需要优先,置于最后 22 | ::: 23 | 24 | 给出一个常用的文本匹配的优先级示例 25 | 26 | ```ahk 27 | ; 添加插件智能项 28 | static addIntelligentItem() { 29 | ; 添加参照物 30 | PluginHelper.addPluginToIntelligentMode(this.name, 31 | "必定匹配且优先级为1.5的参照", ; 虽然可以使用浮点数,但是一般情况下都是用整数作为优先级 32 | (*) => 1.5, ; 无论如何都返回1.5 33 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 34 | ) 35 | 36 | ; 自定义匹配函数 37 | matchHandler(obj, searchText, pastedContentType, *) { ; 使用*忽略多余参数 38 | ; 带有非文本类型的都不匹配 39 | if (pastedContentType != 'text') 40 | return 0 41 | 42 | if (PluginHelper.Utils.strStartWith("PPYXJ", searchText)) { 43 | ; 搜索文本PPYXJ的开头 44 | return 2 ; 更高优先级 45 | } else ; 否则优先级为1 46 | return 1 ; 基本优先级 47 | 48 | ; 注意,当未输入任何内容时,智能模式不会进行匹配,所以输入框为空时没有任何智能项 49 | } 50 | 51 | PluginHelper.addPluginToIntelligentMode(this.name, 52 | "匹配优先级", 53 | matchHandler, 54 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 55 | ) 56 | } 57 | ``` 58 | 59 | 上述代码效果如图 60 | 61 | ![匹配优先级1](../images/intelligent-4.jpg) 62 | 63 | ![匹配优先级2](../images/intelligent-5.jpg) 64 | 65 | ![匹配优先级3](../images/intelligent-6.jpg) 66 | 67 | 68 | :::warning 69 | 当**未输入任何内容**(文件、图片也是输入内容的一种)时,智能模式不会进行匹配,所以输入框为空时**没有任何**智能项 70 | ::: 71 | 72 | ## 混合匹配条件 73 | 74 | 混合匹配条件相对比较简单,是为之后**动态修改插件智能项**做铺垫 75 | 76 | 这里以[文件搜索](../../plugin/file-find.md)插件源码中的 `matchHandler` 稍作修改后作为例子 77 | 78 | ```ahk 79 | static addIntelligentItem() { 80 | ; 混合匹配条件 81 | matchHandler(obj, searchText, pastedContentType, pastedContent, workWinInfo, winInfoMatchFlag) { 82 | ; 是否为窗口匹配模式 83 | if (winInfoMatchFlag) { 84 | pName := workWinInfo.processName 85 | cName := workWinInfo.class 86 | path := workWinInfo.title ; 资源管理器的标题一般为当前文件夹路径 87 | 88 | ; 匹配资源管理器 且 标题指向的文件夹存在 89 | if ((pName == 'explorer.exe' || cName == "CabinetWClass" || cName == "ExploreWClass") 90 | && InStr(FileExist(path), "D") 91 | ) { 92 | if (PluginHelper.Utils.strStartWith("Everything", searchText) 93 | || PluginHelper.Utils.strStartWith("WJSS", searchText)) { 94 | ; 搜索文本为Everything或者WJSS的开头 95 | return 2 ; 更高优先级 96 | } else 97 | return 1 ; 基本优先级 98 | } 99 | } else { 100 | if (pastedContentType == 'text' && searchText) { 101 | return 1 ; 基本优先级 102 | } else if (pastedContentType == 'file' && pastedContent.Length = 1 && InStr(FileExist(pastedContent[1]), "D")) { 103 | if (PluginHelper.Utils.strStartWith("Everything", searchText) 104 | || PluginHelper.Utils.strStartWith("WJSS", searchText)) { 105 | ; 搜索文本为Everything或者WJSS的开头 106 | return 2 ; 更高优先级 107 | } else 108 | return 1 ; 基本优先级 109 | } 110 | } 111 | return 0 ; 其他情况不匹配 112 | } 113 | 114 | PluginHelper.addPluginToIntelligentMode(this.name, 115 | "混合匹配条件", 116 | matchHandler, 117 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 118 | ) 119 | } 120 | ``` 121 | 这个自定义匹配函数的处理方式看起来可能会让人有点疑惑,但匹配函数确确实实用了很多**混合匹配条件**。 122 | 123 | ## 动态修改插件智能项对象 124 | 125 | 自定义匹配函数在执行过程中能够直接访问到插件智能项对象 `obj` 126 | 127 | 所以,可以在匹配时中动态的修改 `obj` 属性,来达到存储数据、修改匹配表现的目的 128 | 129 | 续接之前的[混合匹配条件](#混合匹配条件)代码例子: 130 | 131 | ```ahk 132 | ; 添加插件智能项 133 | static addIntelligentItem() { 134 | ; 混合匹配条件 135 | matchHandler(obj, searchText, pastedContentType, pastedContent, workWinInfo, winInfoMatchFlag) { 136 | if (winInfoMatchFlag) { 137 | pName := workWinInfo.processName 138 | cName := workWinInfo.class 139 | path := workWinInfo.title ; 资源管理器的标题一般为当前文件夹路径 140 | ; 匹配资源管理器 且 标题指向的文件夹存在 141 | if ((pName == 'explorer.exe' || cName == "CabinetWClass" || cName == "ExploreWClass") 142 | && InStr(FileExist(path), "D") 143 | ) { 144 | if (PluginHelper.Utils.strStartWith("Everything", searchText) 145 | || PluginHelper.Utils.strStartWith("WJSS", searchText)) { 146 | ; 搜索文本为Everything或者WJSS的开头 147 | obj.matchData := { type: "window", searchTextFlag: false, path: path } 148 | obj.title := "在工作窗口目录内搜索" ; 修改显示标题 149 | return 2 ; 更高优先级 150 | } else { 151 | obj.matchData := { type: "window", searchTextFlag: true, path: path } 152 | obj.title := "在工作窗口目录内搜索输入内容" ; 修改显示标题 153 | return 1 ; 基本优先级 154 | } 155 | } 156 | } else { 157 | if (pastedContentType == 'text' && searchText) { 158 | obj.matchData := { type: "text" } ; 标记匹配类型,方便进入插件模式前区分进入方式 159 | obj.title := "使用Everything搜索输入内容" ; 修改显示标题 160 | return 1 ; 基本优先级 161 | } else if (pastedContentType == 'file' && pastedContent.Length = 1 && InStr(FileExist(pastedContent[1]), "D")) { 162 | if (PluginHelper.Utils.strStartWith("Everything", searchText) 163 | || PluginHelper.Utils.strStartWith("WJSS", searchText)) { 164 | ; 搜索文本为Everything或者WJSS的开头 165 | obj.matchData := { type: "file", searchTextFlag: false } ; 标记最后不要传入searchText 166 | obj.title := "使用Everything在文件夹内搜索" ; 修改显示标题 167 | return 2 ; 更高优先级 168 | } else { 169 | obj.matchData := { type: "file", searchTextFlag: true } 170 | obj.title := "使用Everything在文件夹内搜索输入内容" ; 修改显示标题 171 | return 1 ; 基本优先级 172 | } 173 | } 174 | } 175 | return 0 ; 不匹配 176 | } 177 | 178 | PluginHelper.addPluginToIntelligentMode(this.name, 179 | "匹配优先级", 180 | matchHandler, 181 | (obj, *) => PluginHelper.Utils.tip(this.name, obj.title) 182 | ) 183 | } 184 | ``` 185 | 上述代码中,有通过 `obj.matchData := { type: "xxx", searchTextFlag: false, path: "xxx" }` 来动态添加额外数据到 `obj` 对象中, 186 | 从而在 `runHandler(obj, searchText)` 运行函数中可以根据这些额外数据执行相应的操作。 187 | 188 | 当然,`matchData`这个键名不是固定的,你可以使用任意不与原 `obj` 属性冲突的键名。 189 | 190 | 此外,还有通过 `obj.title := "xxx"` 可以动态修改插件智能项的标题 191 | 192 | 如此,**只添加一个插件智能项,却能发挥出n个智能项的效果** 193 | 194 | :::warning 195 | 智能模式搜索框中的内容每次发生变动,Starter都会依次调用一遍每个插件智能项的自定义匹配函数,以判断插件智能项是否匹配。 196 | 197 | 所以匹配函数尽快返回结果,避免调用进度因此卡顿 198 | ::: -------------------------------------------------------------------------------- /src/Plugin/文件选择对话框导航.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: 文件选择对话框导航 3 | * @Version: 0.0.3 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-05-13 7 | * @Description: 文件选择对话框路径导航 8 | */ 9 | 10 | /* 11 | ===Starter Plugin Info==> 12 | { 13 | "author": "AkiChase", 14 | "version": "0.0.3", 15 | "introduction": "文件选择对话框路径导航", 16 | "icon": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAG9ElEQVRYhe2XaXRU5RnH52NdWm21otXiUitStVUpFYEQQmayzUzmzkxm5s7c2e6sSZAtQaJIFiVIilQRoT2np4rEgAZ6IIsLS+gCJCCYDbJobf3aD7InJAGCf5/3nXtvZsh8yAe+tXPO/8w9c07O7/e873Of941O9/8PfUQxPM3mDdkpQRZBCgZYCt1+vxqj08uT73C5HA7HLTcNbvMFc0U5fkmUY3AGo3AEInD4wyii2H0hWKUgBBZPABa3H2bRhwKndMRg8N52UwQI/OXGulKMDi4D/qPk30uUvEBZnMjXJTzfHInCLfuQX+Q+fFMkXHLsekfLUpzpWobNG+P4Qx1LDBvXR/HG6xFKGBvWhbChNoT3t4ZJIo5vDkcgBiXk28X+HJurOcfqbGIxWBxN+sKipmyzfW+W2cqz0Gjdm1kg7MnIt+zJyDXzzMsx1z+vL3CrK4BjHy/Bt90TAgy+cX1kQoDgv6+VSSA0IRCQULpYwroaL9ZVS6it8mBtlRtrK0W8VunCq2tcqHnFgZrVDlSvLkL1y3ZUvWxDZYUV5csFzM8zYa6+4BVNILH0Sycv/9el2vKrcDdVX7HKizMnZVzpk4F/kdhXLPT8VZASoPiBL31KJCUeYNDNs22TFXP0xv+yLUhpPtZ4Nq+c0nyF1His+UwuL0xOCSvLJXxL8EtdAQx1B3C1X06CKwIa3DshoMAxKOLQTjtoG8AFtm2Jo20XSwxtjSxRSgSHGsOUED1TPpIpQfxjdxBnGbw7yOFDPQEM9/pxbSAw5eox6OICc/T50LHqO1oXY6T/BZz5Io6znTFKFOfo+3xXFOc7w7jQRekO42J3iMAqPKjAA7hMAiOn/LQS/hsE0levCjyXrQq0LOb7PtpfygU0eFdEgUeS4DKBZQz3BBV4gMNZRk95aSV8aZY/tfqEgC0hwPZeFWCNN9pfQuCEgAbvCRM4NBl+KkjggAL3YYxy5bQX4wPe1Oo1AVETaNthxe8W5ZEANV5HS2lK54/2xwkeUeCRJHiI4CTQG7wB7tfgV/q8tBUSrg+k23tVwJlOIHnqlWKsvzgBV6qfDE8IcPhpP8F9uKrAr/Z5cK3fg+8G01evCWTlQlfEBUrSvvdXBmJcgMN7WWSCy5PhfX4FnhC4RhkngesD7rTVY8CRKtDeXJJSPYPv+ksY/oifv/dGhwcFRR7k2UXk2VzItbpAoxd6iwPZhUUUOxaZbcgy2ZBpFJBZYMGCfAsy8gphcZpRv9mWUn1CQMDsrBzo2ODRBBT44T1RmEQ/nqvbgt/8qQG//mMDnqI8ufUDPEGZuaUej79TjxnvbMdjm7fjl2+/j0cpv9i0DY9QHn7rPTxImf7mu3i0cgPm5pqxr96qwVWB3y7UBIpTBDbVyVhYthqPfPY5HqY89OnnmP7pcfz8k+N4gHI/5WcfH8O9lGmtx3BPawd+SrmrpQM/aWnHjyl3NrfjDsoPm47iqfAS1FSYMdxlVwSK0NZgSQiwsTshkNj7urVBZFbUaPAHkwRU+H1JAgx+d5KACv9R81Eu8KvicrxUZsS54xaMdNu4wEESmJVpgI7N/PbmeErz1a0NYMGqCYHZr76RsvcGYfLe07GbsvczXqrl8Nu5QBkXuHBCwEXKWI81nYB64hVrAmr1c1dWYtlyD/Y1UD7wYH+Dm55F+mZx4cAOJw40OHFwJ2WHA7G4BU+WrtQEZsaZgAkXT1px6aSA4U4B+7ab8ewCPXTsxGtviqccuetfCyDjxWpN4PnyStRW07s9KOPsCS/OnfDg/Ek3LnR6cIky1OXG5R43RnpEjPWKWFMhcAEGvy1JQIWPdAn4LL1AsSYw/8Uqbe9VAXbkslPvwhfSZHivm+BuGkgurFllwRMl5ZrA4/EVqKAtGO60cvhYj4D9200kkE0CdN4fbYolCcRIwK8JsMabU7aG33jUM398wE+noURwD8E9SXCRBpKLbj0CbzwGv3XvEU1AhV89JeBAvQnPZJAAXblHDu6KaNWrAvNWVmmdzwTYdSv5wjFOp95Irwr3ENzN4eN9Il8BVeAWEpgRW0FbUMCbj8HHTwvY+2cjnpmfPaQzOb2tkZIADnwUwZE9YUoIK8q8msADisDSpSIO/9WHf+6WlHjw90YRbR+yo9WJQx868Dcl0Vgh73xWvSoQlPOwv76QKjcT3ASjYMDT8xc16syiOM3okFqNTumyOnbpys07Xx08TODGsctfuxvG7rxcE+bl0GXTYOQCDP4DymPR5XzsssHDGu/ZDP0QLX/jrMzMu9Ne1fPtrt1s7qe89xbbrqle9ecY8ney6xa7cLAjNwE37Jjy/wqZFtdDBPVSxRILDRoP+22qfz9Lb5pOcBfL7KxcJwv7bcoC/1Of7wFq0S6lwML8QQAAAABJRU5ErkJggg==" 17 | } 18 | <==Starter Plugin Info=== 19 | */ 20 | 21 | 22 | #Include ..\Utils\PluginHelper.ah2 23 | 24 | PluginHelper.addEntryFunc((*) => Plugin_文件选择对话框导航.main()) ; 添加入口函数等待执行 25 | 26 | class Plugin_文件选择对话框导航 { 27 | 28 | static main() { 29 | SplitPath(A_LineFile, &name) ; 获取插件id即文件名 30 | this.name := name 31 | 32 | this.addToIntelligentMode() 33 | } 34 | 35 | static addToIntelligentMode() { 36 | ; 仅匹配窗口 37 | matchHandler(obj, searchText, pastedContentType, pastedContent, workWinInfo, winInfoMatchFlag) { 38 | hwnd := workWinInfo.hwnd 39 | cName := workWinInfo.class 40 | ; 匹配文件选择对话框 winInfoMatchFlag开启,才能保证cName是最新的 41 | if (winInfoMatchFlag && cName == "#32770") { 42 | obj.matchData := { hwnd: hwnd } 43 | return PluginHelper.Utils.strStartWith("WJXZDHK", searchText) ? 3 : 2 ; 优先级 44 | } 45 | return 0 46 | } 47 | 48 | ; 插件模式初始化 49 | init(hwnd, that) { 50 | that.pluginSearchData := this.getExplorerPathList() 51 | that.pluginOtherData := { hwnd: hwnd } 52 | } 53 | 54 | ; 插件模式下搜索 55 | searchHandler(that, searchText) { 56 | that.pluginSearchResult := [] 57 | if (searchText) { 58 | ; 搜索结果 59 | for item in that.pluginSearchData { 60 | if (InStr(item.title, searchText) || InStr(item.titlePY, searchText)) ; 原文匹配或者拼音首字母部分匹配 61 | that.pluginSearchResult.Push(item) 62 | } 63 | } else ; 搜索内容为空时显示所有 64 | that.pluginSearchResult := that.pluginSearchData 65 | 66 | ; 搜索结果按标题排序 67 | PluginHelper.Utils.QuickSort(that.pluginSearchResult, (itemA, tiemB) => StrCompare(itemA.title, tiemB.title)) 68 | ;重置列表 69 | that.listView.Opt("-Redraw") ;禁用重绘 70 | that.listView.Delete() 71 | ; 添加搜索结果到列表 72 | for item in that.pluginSearchResult { 73 | that.listView.Add(, " " item.title) 74 | } 75 | 76 | that.resizeGui() ;根据搜索结果数量调整gui尺寸 并启用重绘 77 | } 78 | 79 | ;定义插件模式下回车、双击处理函数 80 | runHandler(that, rowNum) { 81 | if (rowNum) { 82 | ; hwnd在ini函数中赋值到了pluginOtherData.hwnd 83 | PluginHelper.hideSearchGui() 84 | this.jumpToPath(that.pluginOtherData.hwnd, that.pluginSearchResult[rowNum].path) 85 | } 86 | } 87 | 88 | ; 添加插件项到智能模式搜索界面 89 | PluginHelper.addPluginToIntelligentMode( 90 | this.name, 91 | "文件选择对话框路径导航", 92 | matchHandler, 93 | (obj, searchText) => ( 94 | PluginHelper.showPluginMode( ; 启动插件模式 95 | [], ; 在ini函数中中动态设定 96 | searchHandler, 97 | runHandler, { 98 | initHandler: init.Bind(obj.matchData.hwnd), 99 | placeholder: "选择导航路径", 100 | thumb: PluginHelper.getPluginHIcon(this.name) 101 | } 102 | ) 103 | ), , PluginHelper.getPluginHIcon(this.name) 104 | ) 105 | } 106 | 107 | 108 | ; 获取当前运行的资源管理器路径 109 | static getExplorerPathList() { 110 | pathList := Map() 111 | for item in ComObject("Shell.Application").Windows { 112 | try folder := item.Document.Folder.Self.Path 113 | if (!folder || pathList.Has(folder)) 114 | Continue 115 | pathList[folder] := true 116 | } 117 | out := [] 118 | out.Capacity := pathList.Count 119 | for k, v in pathList { 120 | title := PluginHelper.Utils.pathStrCompact(k, 50) 121 | out.Push({ title: title, path: k, titlePY: PluginHelper.Utils.chineseFirstChar(title) }) ; 压缩到50字符内 122 | } 123 | return out 124 | } 125 | 126 | ; 指定对话框导航到指定路径 127 | static jumpToPath(hwnd, path) { 128 | if (WinExist("ahk_id " hwnd)) { 129 | ctrls := WinGetControls("A") 130 | 131 | ControlSend("{F4}") 132 | ctrlNN := "" 133 | for i, ctrl in ctrls { 134 | if (InStr(ctrl, "Edit")) 135 | ctrlNN := ctrl 136 | } 137 | Sleep(150) 138 | ControlFocus(ctrlNN) 139 | ControlSetText(path, ctrlNN) 140 | ControlSend("{Enter}", ctrlNN) 141 | PluginHelper.Utils.tip("文件选择对话框路径导航", "路径已导航至:`n`n" path, 2000) 142 | } else 143 | PluginHelper.Utils.tip("文件选择对话框路径导航", "对话框窗口不存在,导航失败", 2000) 144 | 145 | } 146 | } -------------------------------------------------------------------------------- /docs/dev/plugin-mode/basics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 基础 3 | icon: square-caret-up 4 | author: AkiChase 5 | date: 2023-04-14 6 | order: 1 7 | --- 8 | 9 | **插件模式**类似于启动模式、智能模式,但更加自由、强大,**Starter** 仅仅提供一个搜索框,其他内容全部由插件定义。 10 | 11 | 可以定义的内容包括: 12 | 13 | 1. 待搜索数据 14 | 2. 搜索处理函数 15 | 3. 运行处理函数 16 | 4. (可选)双击Right处理函数 17 | 5. (可选)加载图标处理函数 18 | 6. (可选)触底处理函数 19 | 7. (可选)粘贴内容处理函数 20 | 8. (可选)拖入文件处理函数 21 | 9. (可选)初始化函数 22 | 10. (可选)初始搜索框文本 23 | 11. (可选)搜索框占位符 24 | 12. (可选)搜索框图标 25 | 26 | ## API 27 | 28 | 进入**插件模式**需要使用 `PluginHelper.showPluginMode()` API 29 | 30 | 参数及说明:[PluginHelper.showPluginMode](../../api/showPluginMode.md) 31 | 32 | 除此之外还需要了解一下 `PluginMode` 模块的几个成员。 33 | 34 | `PluginMode` 是使用插件模式时处理函数中 `that` 参数指向的类,我们一般只需要了解下面几个数据存储位置即可。 35 | 36 | - `that.pluginSearchData` 37 | 38 | `showPluginMode()`函数中第一个参数传递的数据会被储存在当前位置,意义是**可能**出现在插件模式**搜索结果**的全部静态内容 39 | 40 | 当然这个全部内容只是针对**静态内容**,[进阶](./advanced.md)中有动态获取数据的情况,就不依赖此静态内容。 41 | 42 | - `that.pluginSearchResult` 43 | 44 | 插件应当在 `searchHandler` 即搜索处理函数中,将要显示的搜索结果储存在当前位置,方便 `runHandler` 访问。 45 | 46 | - `that.pluginOtherData` 47 | 48 | 插件如果有额外的数据,可以存放到当前位置 49 | 50 | ::: danger 51 | 插件应用在使用**插件模式**时禁止将任何数据存储在 `PluginMode`或者说 `that` 的其他位置。 52 | 53 | 若数据较多,请嵌套至对象后存储。 54 | ::: 55 | 56 | - `that.listView` 57 | 58 | 指向插件模式的搜索框下方列表,可以通过相关的函数对其进行操作,参考[ListView (GUI) - 语法 & 使用](https://orz707.gitee.io/v2/docs/commands/ListView.htm) 59 | 60 | - `that.resizeGui()` 61 | 62 | 重新计算并调整列表、窗口大小,一般在修改`listView`的内容之后调用 63 | 64 | :::tip 65 | `resizeGui()` 方法中会调用 `that.listView.Opt("Redraw")`,所以无需再启用重绘 66 | ::: 67 | 68 | 69 | ## 最简示例 70 | 71 | 最简示例直接上**相关部分**代码,看注释 72 | 73 | ```ahk 74 | class Plugin_MyPlugin { 75 | static main() { 76 | SplitPath(A_LineFile, &name) 77 | this.name := name 78 | 79 | ; 添加 进入插件模式 菜单项 80 | m := Menu() 81 | m.Add("进入插件模式", (*) => this.showPluginMode()) 82 | PluginHelper.pluginMenu.Add(this.name, m) 83 | } 84 | 85 | ; 进入插件模式 86 | static showPluginMode() { 87 | ; 临时数据 88 | tmpData := [ 89 | ["明月几时有", "把酒问青天"], 90 | ["不知天上宫阙", "今夕是何年"], 91 | ["我欲乘风归去", "又恐琼楼玉宇"] 92 | ] 93 | ; 构造出我们真正需要的数据,方便在searchHandler中使用 94 | searchData := [] 95 | for data in tmpData { 96 | searchData.Push({ 97 | title: data[1], 98 | keywords: PluginHelper.Utils.chineseFirstChar(data[1]), ; 拼音首字母作为关键词 99 | answer: data[2] 100 | }) 101 | } 102 | ; 释放不必要的占用,虽然不多,但要有这个意识 103 | tmpData := 0 104 | 105 | ; 此处that指的是 pluginMode 模块,即插件模式 106 | searchHandler(that, searchText) { 107 | ; 如果有搜索内容则进行匹配 108 | if (StrLen(searchText)) { 109 | that.pluginSearchResult:=[] ; 重置 pluginSearchResult 110 | for data in that.pluginSearchData { 111 | ; 如果搜索内容是title或者keywords的一部分则添加到搜索结果 112 | if (InStr(data.title, searchText) || InStr(data.keywords, searchText)) 113 | that.pluginSearchResult.Push(data) 114 | } 115 | } else ; 否则显示全部 116 | that.pluginSearchResult := that.pluginSearchData 117 | 118 | ; 对搜索结果进行渲染,即添加到listview中 119 | ;重置列表 120 | that.listView.Opt("-Redraw") ;禁用重绘 121 | that.listView.Delete() 122 | for data in that.pluginSearchResult { 123 | ; 这里使用title作为内容 124 | that.listView.Add(, data.title) 125 | } 126 | ; 调整gui尺寸 并启用重绘,该函数包含了Opt("Redraw") 127 | that.resizeGui() 128 | } 129 | 130 | runHandler(that, rowNum) { 131 | ; 通过pluginSearchResult和当前启动的行号访问到当前数据 132 | curData := that.pluginSearchResult[rowNum] 133 | ; 显示当前项的诗词后半句 134 | ; 此处的this指的是当前插件,不要和that混淆 135 | PluginHelper.Utils.tip(this.name, curData.answer, 1000) 136 | } 137 | 138 | PluginHelper.showPluginMode( 139 | searchData, 140 | searchHandler, 141 | runHandler 142 | ) 143 | } 144 | } 145 | ``` 146 | 147 | 效果如图: 148 | 149 | ![最简示例:无搜索内容](../images/plugin-mode-1.jpg) 150 | 151 | ![最简示例:有搜索内容](../images/plugin-mode-2.jpg) 152 | 153 | ![最简示例:执行结果](../images/plugin-mode-3.jpg) 154 | 155 | ## 从启动项进入 156 | 157 | 虽然可以像最简示例中一样,通过菜单而进入**插件模式**。 158 | 159 | 但是最常用的方式还是通过**插件启动项**的运行处理函数进入。 160 | 161 | ```ahk 162 | ; 添加 进入MyPlugin插件模式 启动项 163 | PluginHelper.addPluginToStartupMode( 164 | this.name, 165 | "进入MyPlugin插件模式", 166 | ["进入MyPlugin插件模式", PluginHelper.Utils.chineseFirstChar("进入MyPlugin插件模式")], 167 | (obj, searchText) => this.showPluginMode(), ; 运行则进入插件模式 168 | ) 169 | ``` 170 | ![从启动项进入](../images/plugin-mode-4.jpg) 171 | 172 | ## 简易文件夹示例 173 | 174 | 这个示例中,可以通过**启动项**进入**插件模式**,显示软件图片资源文件夹内的所有文件。 175 | 176 | 可以通过文件名或者拼音首字母转换后的文件名进行**搜索筛选**,运行则**在文件夹中打开**指定项 177 | 178 | 具体如何上代码,看注释 179 | 180 | ```ahk 181 | class Plugin_MyPlugin { 182 | static main() { 183 | SplitPath(A_LineFile, &name) 184 | this.name := name 185 | 186 | ; 添加 进入MyPlugin插件模式 启动项 187 | PluginHelper.addPluginToStartupMode( 188 | this.name, 189 | "简易文件夹", 190 | ["简易文件夹", PluginHelper.Utils.chineseFirstChar("简易文件夹")], 191 | (obj, searchText) => this.showPluginMode(), ; 启动则进入插件模式 192 | ) 193 | } 194 | 195 | ; 进入插件模式 196 | static showPluginMode() { 197 | ; 构造文件夹相关数据 198 | searchData := [] 199 | ; PluginHelper.imgDir 为Starter存放图片资源的目录 200 | loop files PluginHelper.imgDir "\*.*" { 201 | searchData.Push({ 202 | title: A_LoopFileName, 203 | keywords: PluginHelper.Utils.chineseFirstChar(A_LoopFileName), 204 | path: A_LoopFileFullPath 205 | }) 206 | } 207 | 208 | ; 此处that指的是 pluginMode 模块,即插件模式 209 | searchHandler(that, searchText) { 210 | ; 如果有搜索内容则进行匹配 211 | if (StrLen(searchText)) { 212 | that.pluginSearchResult := [] ; 重置 pluginSearchResult 213 | for data in that.pluginSearchData { 214 | ; 如果搜索内容是title或者keywords的一部分则添加到搜索结果 215 | if (InStr(data.title, searchText) || InStr(data.keywords, searchText)) 216 | that.pluginSearchResult.Push(data) 217 | } 218 | } else ; 否则显示全部 219 | that.pluginSearchResult := that.pluginSearchData 220 | 221 | ;重置列表 222 | that.listView.Opt("-Redraw") ;禁用重绘 223 | that.listView.Delete() 224 | for data in that.pluginSearchResult 225 | that.listView.Add(, data.title) 226 | ; 调整gui尺寸 并启用重绘 227 | that.resizeGui() 228 | } 229 | 230 | runHandler(that, rowNum) { 231 | ; 通过pluginSearchResult和当前启动的行号访问到当前数据 232 | curData := that.pluginSearchResult[rowNum] 233 | ; 此处的this指的是当前插件,不要和that混淆 234 | PluginHelper.hideSearchGui() ; 隐藏搜索框 235 | PluginHelper.Utils.tip(this.name, "在文件夹中显示:`n" curData.path) 236 | PluginHelper.Utils.openFileInFolder(curData.path) ; 在文件夹中显示当前文件 237 | } 238 | 239 | PluginHelper.showPluginMode( 240 | searchData, 241 | searchHandler, 242 | runHandler, { 243 | thumb: PluginHelper.getPluginHIcon(this.name) ; 搜索框图标 244 | } 245 | ) 246 | } 247 | } 248 | ``` 249 | 250 | 效果如图: 251 | 252 | ![简易文件夹:插件启动项](../images/plugin-mode-5.jpg) 253 | 254 | ![简易文件夹:插件启动项](../images/plugin-mode-6.jpg) 255 | 256 | ![简易文件夹:插件启动项](../images/plugin-mode-7.jpg) 257 | 258 | ![简易文件夹:插件启动项](../images/plugin-mode-8.jpg) 259 | 260 | ![简易文件夹:插件启动项](../images/plugin-mode-9.jpg) 261 | -------------------------------------------------------------------------------- /docs/api/utils/ImagePutHelper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ImagePutHelper 3 | author: AkiChase 4 | order: 2 5 | date: 2023-04-14 6 | --- 7 | 8 | ::: warning 9 | 本文内容列举翻译API内容,仅供参考。 10 | ::: 11 | 12 | 具体使用请参考: 13 | 14 | - [简单、高效、实用的图片操作库 - AutoAHK](https://www.autoahk.com/archives/37246) 15 | - [ImagePut —— 裁剪、缩放 & 其他选项](../../dev/others/image-put-doc-1.md) 16 | - [ImagePut —— 输入类型 & 输出函数](../../dev/others/image-put-doc-2.md) 17 | 18 | ## ImagePutBase64(image, extension := "", quality := "") 19 | 20 | 将图像转换为指定格式的文件并且返回一个Base64编码的字符串。 21 | 22 | - 参数 23 | - \{Image\} `image`: 需要被转换的图像 24 | - \{String\} `extension`: 文件格式, 选项为: `bmp`, `gif`, `jpg`, `png`, `tiff` 25 | - \{String\} `quality`: `jpg`文件质量的等级,整数类型,0-100 26 | 27 | - 返回值 28 | - \{String\} Base64编码的字符串 29 | 30 | ## ImagePutBitmap(image) 31 | 32 | 将图像转换为一个GDI+位图并且返回一个指针。 33 | 34 | - 参数 35 | - \{Image\} `image`: 需要被转换的图像 36 | 37 | - 返回值 38 | - \{Pointer\} GDI+位图的指针 39 | 40 | ## ImagePutBuffer(image) 41 | 42 | 将图像转换为一个GDI+位图并且返回一个包含GDI+作用域的缓冲区对象。 43 | 44 | - 参数 45 | - \{Image\} `image`: 需要被转换的图像 46 | 47 | - 返回值 48 | - \{BitmapBuffer\} 包含GDI+作用域的缓冲区对象 49 | 50 | ## ImagePutClipboard(image) 51 | 52 | 将图像复制到剪贴板并且返回剪贴板的所有内容。 53 | 54 | - 参数 55 | - \{Image\} `image`: 需要被复制的图像 56 | 57 | - 返回值 58 | - \{String\} 包含剪贴板的所有内容的字符串 59 | 60 | ## ImagePutCursor(image, xHotspot := "", yHotspot := "") 61 | 62 | 将图像设为光标并且返回变量A_Cursor。 63 | 64 | - 参数 65 | - \{Image\} `image`: 需要被设为光标的图像 66 | - \{String\} `xHotspot`: X点击点,范围为0-width。 67 | - \{String\} `yHotspot`: Y点击点,范围为0-height。 68 | 69 | - 返回值 70 | - \{Variable\} 变量A_Cursor 71 | 72 | ## ImagePutDC(image, alpha := "") 73 | 74 | 将图像放到设备上下文并且返回句柄。 75 | 76 | - 参数 77 | - \{Image\} `image`: 需要被放到设备上下文的图像 78 | - \{String\} `alpha`: Alpha通道的颜色,RGB格式,例如 0xFFFFFF。 79 | 80 | - 返回值 81 | - \{Handle\} 句柄 82 | 83 | ## ImagePutDesktop(image) 84 | 85 | 将图像放在桌面图标后面并且返回字符串"desktop"。 86 | 87 | - 参数 88 | - \{Image\} `image`: 需要被放在桌面图标后面的图像 89 | 90 | - 返回值 91 | - \{String\} "desktop" 92 | 93 | ## ImagePutExplorer(image, default := "") 94 | 95 | 将图像保存在最近活动的文件浏览器窗口中。 96 | 97 | - 参数 98 | - \{Image\} `image`: 需要被保存最近活动的文件浏览器窗口中的图像 99 | - \{String\} `default`: 默认路径选项。 100 | 101 | ## ImagePutFile(image, filepath := "", quality := "") 102 | 103 | 将图像保存为文件并返回文件路径。 104 | 105 | - 参数 106 | - \{Object\} `image`: 要保存的图像对象。 107 | - \{String\} `filepath`: 文件路径和扩展名,默认为空字符串 | string -> *.bmp, *.gif, *.jpg, *.png, *.tiff 108 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串 | integer -> 0 - 100 109 | 110 | - 返回值 111 | - \{String\} 文件路径字符串。 112 | 113 | ## ImagePutFormData(image, boundary := "ImagePut-abcdef") 114 | 115 | 将图像以多部分表单数据的形式返回二进制 SafeArray COM 对象。 116 | 117 | - 参数 118 | - \{Object\} `image`: 图像对象。 119 | - \{String\} `boundary`: 内容类型,默认为 "ImagePut-abcdef"。 120 | 121 | - 返回值 122 | - \{Object\} 返回图像的二进制 SafeArray COM 对象。 123 | 124 | ## ImagePutHBitmap(image, alpha := "") 125 | 126 | 将图像保存为设备无关位图,并返回句柄。 127 | 128 | - 参数 129 | - \{Object\} `image`: 要保存的图像对象。 130 | - \{String\} `alpha`: 在 Alpha 通道中的颜色(rgb 值),默认为空字符串。 | RGB -> 0xFFFFFF 131 | 132 | - 返回值 133 | - \{Number\} 设备无关位图处理后的句柄。 134 | 135 | ## ImagePutHex(image, extension := "", quality := "") 136 | 137 | 将图像保存为文件格式,并返回十六进制编码字符串。 138 | 139 | - 参数 140 | - \{Object\} `image`: 要保存的图像对象。 141 | - \{String\} `extension`: 文件格式编码,默认为空字符串。 | string -> bmp, gif, jpg, png, tiff 142 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串。 | integer -> 0 - 100 143 | 144 | - 返回值 145 | - \{String\} 十六进制编码字符串。 146 | 147 | ## ImagePutHIcon(image) 148 | 149 | 将图像处理为图标,并返回句柄。 150 | 151 | - 参数 152 | - \{Object\} `image`: 要进行处理的图像对象。 153 | 154 | - 返回值 155 | - \{Number\} 图标处理成功后的句柄。 156 | 157 | ## ImagePutRandomAccessStream(image, extension := "", quality := "") 158 | 159 | 将图像保存为文件格式,并返回指向 RandomAccessStream 的指针。 160 | 161 | - 参数 162 | - \{Object\} `image`: 要进行处理的图像对象。 163 | - \{String\} `extension`: 文件格式编码,默认为空字符串。 | string -> bmp, gif, jpg, png, tiff 164 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串。 | integer -> 0 - 100 165 | 166 | - 返回值 167 | - \{Object\} RandomAccessStream 指针。 168 | 169 | ## ImagePutSafeArray(image, extension := "", quality := "") 170 | 171 | 将图像保存为文件格式,并返回 SafeArray COM 对象。 172 | 173 | - 参数 174 | - \{Object\} `image`: 要进行处理的图像对象。 175 | - \{String\} `extension`: 文件格式编码,默认为空字符串。 | string -> bmp, gif, jpg, png, tiff 176 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串。 | integer -> 0 - 100 177 | 178 | - 返回值 179 | - \{Object\} SafeArray COM 对象。 180 | 181 | ## ImagePutScreenshot(image, screenshot := "", alpha := "") 182 | 183 | 将图像放置在共享屏幕设备上,并返回坐标数组。 184 | 185 | - 参数 186 | - \{Object\} `image`: 要进行处理的图像对象。 187 | - \{String\} `screenshot`: 屏幕坐标数组,默认为空字符串。 | array -> [x,y,w,h] or [0,0] 188 | - \{String\} `alpha`: 在 Alpha 通道中的颜色(rgb 值),默认为空字符串。 | RGB -> 0xFFFFFF 189 | 190 | - 返回值 191 | - \{Array\} 坐标数组。 192 | 193 | ## ImagePutStream(image, extension := "", quality := "") 194 | 195 | 将图像保存为文件格式,并返回指向 stream 的指针。 196 | 197 | - 参数 198 | - \{Object\} `image`: 要进行处理的图像对象。 199 | - \{String\} `extension`: 文件格式编码,默认为空字符串。 | string -> bmp, gif, jpg, png, tiff 200 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串。 | integer -> 0 - 100 201 | 202 | - 返回值 203 | - \{Object\} stream 指针。 204 | 205 | ## ImagePutURI(image, extension := "", quality := "") 206 | 207 | 将图像保存为文件格式,并返回 URI 字符串。 208 | 209 | - 参数 210 | - \{Object\} `image`: 要进行处理的图像对象。 211 | - \{String\} `extension`: 文件格式编码,默认为空字符串。 | string -> bmp, gif, jpg, png, tiff 212 | - \{Number\} `quality`: JPEG 图像质量等级,默认为空字符串。 | integer -> 0 - 100 213 | 214 | - 返回值 215 | - \{String\} URI 字符串。 216 | 217 | ## ImagePutWallpaper(image) 218 | 219 | 将图像作为桌面壁纸,并返回字符串 "wallpaper"。 220 | 221 | - 参数 222 | - \{Object\} `image`: 要进行处理的图像对象。 223 | 224 | - 返回值 225 | - \{String\} "wallpaper" 字符串。 226 | 227 | ## ImagePutWICBitmap(image) 228 | 229 | 将图像处理为 WICBitmap,并返回接口指针。 230 | 231 | - 参数 232 | - \{Object\} `image`: 要进行处理的图像对象。 233 | 234 | - 返回值 235 | - \{Object\} WICBitmap 接口指针。 236 | 237 | ## ImagePutWindow(image, title := "", pos := "", style := 0x82C80000, styleEx := 0x9, parent := "") 238 | 239 | 将图像放置在窗口中并返回窗口句柄。 240 | 241 | - 参数 242 | - \{Object\} `image`: 要进行处理的图像对象。 243 | - \{String\} `title`: 窗口标题,默认为空字符串。 244 | - \{String\} `pos`: 窗口坐标数组,默认为空字符串。 | array -> [x,y,w,h] or [0,0] 245 | - \{Number\} `style`: 窗口样式,默认为 0x82C80000。 246 | - \{Number\} `styleEx`: 窗口附加样式,默认为 0x9. 247 | - \{Object\} `parent`: 窗口父对象,默认为空字符串。 248 | 249 | - 返回值 250 | - \{Number\} 窗口句柄。 251 | 252 | ## ImageShow(image, title := "", pos := "", style := 0x90000000, styleEx := 0x80088, parent := "") 253 | 254 | 在窗口中显示图像,并返回窗口句柄。 255 | 256 | - 参数 257 | - \{Object\} `image`: 要进行处理的图像对象。 258 | - \{String\} `title`: 窗口标题,默认为空字符串。 259 | - \{String\} `pos`: 窗口坐标数组,默认为空字符串。 | array -> [x,y,w,h] or [0,0] 260 | - \{Number\} `style`: 窗口样式,默认为 0x90000000。 261 | - \{Number\} `styleEx`: 窗口附加样式,默认为 0x80088. 262 | - \{Object\} `parent`: 窗口父对象,默认为空字符串。 263 | 264 | - 返回值 265 | - \{Number\} 窗口句柄。 266 | 267 | ## ImageDestroy(image) 268 | 269 | 销毁图像对象。 270 | 271 | - 参数 272 | - \{Object\} `image`: 要进行销毁的图像对象。 273 | 274 | - 返回值 275 | - 无返回值。 276 | 277 | ## ImageWidth(image) 278 | 279 | 获取图像的宽度。 280 | 281 | - 参数 282 | - \{Object\} `image`: 要获取宽度的图像对象。 283 | 284 | - 返回值 285 | - \{Number\} 图像的宽度。 286 | 287 | ## ImageHeight(image) 288 | 289 | 获取图像的高度。 290 | 291 | - 参数 292 | - \{Object\} `image`: 要获取高度的图像对象。 293 | 294 | - 返回值 295 | - \{Number\} 图像的高度。 -------------------------------------------------------------------------------- /src/Utils/GlobalData.ah2: -------------------------------------------------------------------------------- 1 | class GlobalData { 2 | ; static rootDir := unset 3 | ; static imgDir := unset 4 | ; static bootDir := unset 5 | ; static dataDir := unset 6 | ; static pluginDir := unset 7 | ; static customImgDir := unset 8 | 9 | ; static startupData := unset 10 | ; static startupDataPath := unset 11 | ; static config := unset 12 | ; static configPath := unset 13 | 14 | ; static intelligentData := unset 15 | ; static intelligentDataPath := unset 16 | 17 | static startupSearchResult := [] 18 | static intelligentSearchResult := [] 19 | static intelligentRegMap := Map() 20 | 21 | static intelligentGroups := ["run-with", "search"] 22 | static intelligentMatchPriority := Map("str", 1, "reg", 2) 23 | 24 | static hotkeyList := [] 25 | ; static onExitHandler := unset 26 | 27 | static init() { 28 | SplitPath(A_ScriptDir, , &tmp) 29 | this.rootDir := tmp 30 | 31 | this.imgDir := this.rootDir "\resource\img" 32 | this.pluginDir := this.rootDir "\src\plugin" 33 | this.dataDir := this.rootDir "\resource\data" 34 | this.customImgDir := this.dataDir "\customImg" 35 | this.bootDir := this.dataDir "\boot" 36 | 37 | 38 | for dirPath in [this.dataDir, this.bootDir, this.customImgDir, this.pluginDir] 39 | if (!FileExist(dirPath)) 40 | DirCreate(dirPath) 41 | 42 | this.startupDataPath := this.dataDir "\startupData.txt" 43 | this.configPath := this.dataDir "\config.json" 44 | this.intelligentDataPath := this.dataDir "\intelligentData.json" 45 | 46 | this.refreshStartupData() 47 | this.refreshConfig() 48 | this.refreshIntelligentData() 49 | 50 | ; 退出前执行 51 | onExitHandler(*) { 52 | this.storeStartupData() 53 | this.storeConfig() 54 | this.storeIntelligentData() 55 | } 56 | this.onExitHandler := onExitHandler 57 | } 58 | 59 | ; 重新读取智能模式数据 60 | static refreshIntelligentData() { 61 | static defaultData := " 62 | (Join 63 | { 64 | "run-with": [ 65 | { 66 | "enabled": 1, 67 | "group": "run-with", 68 | "match": { 69 | "exp": [ 70 | "[^\\s]*(\\.[a-zA-z]{2}[^\\s]*){2}", 71 | "https?:\/{2}(.+)" 72 | ], 73 | "mode": "reg", 74 | "replace": [ 75 | "$0", 76 | "$0" 77 | ] 78 | }, 79 | "notes": "使用默认浏览器打开网址链接", 80 | "removable": 0, 81 | "script": { 82 | "mode": "none" 83 | }, 84 | "thumb": "打开网址.png", 85 | "title": "打开网址" 86 | } 87 | ], 88 | "search": [ 89 | { 90 | "enabled": 1, 91 | "group": "search", 92 | "match": { 93 | "exp": [ 94 | "(bd|baidu|百度)\\s+(?.*)" 95 | ], 96 | "mode": "reg", 97 | "replace": [ 98 | "${query}" 99 | ] 100 | }, 101 | "notes": "使用默认浏览器百度搜索", 102 | "removable": 1, 103 | "thumb": "百度搜索.ico", 104 | "title": "百度搜索", 105 | "url": "https:\/\/www.baidu.com\/s?word={}" 106 | }, 107 | { 108 | "enabled": 1, 109 | "group": "search", 110 | "match": { 111 | "exp": [ 112 | "(gg|谷歌)\\s+(?.*)" 113 | ], 114 | "mode": "reg", 115 | "replace": [ 116 | "${query}" 117 | ] 118 | }, 119 | "notes": "使用默认浏览器谷歌搜索", 120 | "removable": 0, 121 | "thumb": "谷歌搜索.ico", 122 | "title": "谷歌搜索", 123 | "url": "https:\/\/www.google.com\/search?q={}" 124 | } 125 | ] 126 | } 127 | )" 128 | ; 加载默认值 129 | default := Jxon_Load(&defaultData) 130 | if (FileExist(this.intelligentDataPath)) { 131 | this.intelligentData := DataHelper.loadJSONFile(this.intelligentDataPath) 132 | for group, itemList in default { 133 | if !(this.intelligentData.Has(group) and (this.intelligentData[group] is Array) this.intelligentData[group].Length) 134 | this.intelligentData[group] := itemList ; 当前组不存在、非数组、为空则置默认值 135 | } 136 | } 137 | else 138 | this.intelligentData := default 139 | } 140 | 141 | ; 保存智能模式数据文件至本地 142 | static storeIntelligentData() { 143 | DataHelper.storeJSONFile(this.intelligentData, this.intelligentDataPath) 144 | } 145 | 146 | ; 重新读取startupData 147 | static refreshStartupData() { 148 | if (FileExist(this.startupDataPath)) 149 | this.startupData := DataHelper.loadData(this.startupDataPath) 150 | else 151 | { 152 | FileAppend("", this.startupDataPath) 153 | this.startupData := [] 154 | } 155 | } 156 | 157 | ; 保存startupDataPath至本地 158 | static storeStartupData() { 159 | DataHelper.storeData(this.startupData, this.startupDataPath) 160 | } 161 | 162 | ; 生成默认配置 163 | static newDefaultConfig(key?) { 164 | if (IsSet(key)) { 165 | switch key { 166 | case "plugin": 167 | return [] 168 | case "hotkey": 169 | return [Map( 170 | "index", 3, 171 | "key", "CapsLock", 172 | "block", false 173 | ), Map( 174 | "index", 4, 175 | "key", "CapsLock", 176 | "block", false 177 | )] 178 | case "keywordsHK": 179 | return [] 180 | } 181 | } else { 182 | config := Map() 183 | config["plugin"] := [] 184 | config["hotkey"] := [Map( 185 | "index", 3, 186 | "key", "CapsLock", 187 | "block", false 188 | ), Map( 189 | "index", 4, 190 | "key", "CapsLock", 191 | "block", false 192 | )] 193 | config["keywordsHK"] := [] 194 | return config 195 | } 196 | } 197 | 198 | ; 重新读取配置文件 199 | static refreshConfig() { 200 | if (FileExist(this.configPath)) { 201 | try { 202 | this.config := DataHelper.loadJSONFile(this.configPath) 203 | if (Type(this.config) != "Map") { 204 | this.config := this.newDefaultConfig() 205 | } 206 | 207 | for name in ["plugin", "hotkey", "keywordsHK"] { 208 | if (!this.config.Has(name)) 209 | this.config[name] := this.newDefaultConfig(name) 210 | } 211 | hotkeyDefault := this.newDefaultConfig("hotkey") 212 | ; 热键数量变化则重置为默认热键 213 | if (this.config["hotkey"].Length < hotkeyDefault.Length) 214 | this.config["hotkey"] := hotkeyDefault 215 | 216 | return 217 | } 218 | } 219 | 220 | ; 配置文件不存在或读取失败则重置为默认配置 221 | this.config := this.newDefaultConfig() 222 | this.storeConfig() 223 | } 224 | 225 | ; 保存配置文件至本地 226 | static storeConfig() { 227 | DataHelper.storeJSONFile(this.config, this.configPath) 228 | } 229 | } -------------------------------------------------------------------------------- /src/Utils/JXON.ah2: -------------------------------------------------------------------------------- 1 | ;;;; AHK v2 2 | ; Example =================================================================================== 3 | ; =========================================================================================== 4 | 5 | ; Msgbox "The idea here is to create several nested arrays, save to text with jxon_dump(), and then reload the array with jxon_load(). The resulting array should be the same.`r`n`r`nThis is what this example shows." 6 | ; a := Map(), b := Map(), c := Map(), d := Map(), e := Map(), f := Map() ; Object() is more technically correct than {} but both will work. 7 | 8 | ; d["g"] := 1, d["h"] := 2, d["i"] := ["purple","pink","pippy red"] 9 | ; e["g"] := 1, e["h"] := 2, e["i"] := Map("1","test1","2","test2","3","test3") 10 | ; f["g"] := 1, f["h"] := 2, f["i"] := [1,2,Map("a",1.0009,"b",2.0003,"c",3.0001)] 11 | 12 | ; a["test1"] := "test11", a["d"] := d 13 | ; b["test3"] := "test33", b["e"] := e 14 | ; c["test5"] := "test55", c["f"] := f 15 | 16 | ; myObj := Map() 17 | ; myObj["a"] := a, myObj["b"] := b, myObj["c"] := c, myObj["test7"] := "test77", myObj["test8"] := "test88" 18 | 19 | ; g := ["blue","green","red"], myObj["h"] := g ; add linear array for testing 20 | 21 | ; q := Chr(34) 22 | ; textData2 := Jxon_dump(myObj,4) ; ===> convert array to JSON 23 | ; msgbox "JSON output text:`r`n===========================================`r`n(Should match second output.)`r`n`r`n" textData2 24 | 25 | ; newObj := Jxon_load(&textData2) ; ===> convert json back to array 26 | 27 | ; textData3 := Jxon_dump(newObj,4) ; ===> break down array into 2D layout again, should be identical 28 | ; msgbox "Second output text:`r`n===========================================`r`n(should be identical to first output)`r`n`r`n" textData3 29 | 30 | ; msgbox "textData2 = textData3: " ((textData2=textData3) ? "true" : "false") 31 | 32 | ; =========================================================================================== 33 | ; End Example ; ============================================================================= 34 | ; =========================================================================================== 35 | 36 | ; originally posted by user coco on AutoHotkey.com 37 | ; https://github.com/cocobelgica/AutoHotkey-JSON 38 | 39 | Jxon_Load(&src, args*) { 40 | key := "", is_key := false 41 | stack := [ tree := [] ] 42 | next := '"{[01234567890-tfn' 43 | pos := 0 44 | 45 | while ( (ch := SubStr(src, ++pos, 1)) != "" ) { 46 | if InStr(" `t`n`r", ch) 47 | continue 48 | if !InStr(next, ch, true) { 49 | testArr := StrSplit(SubStr(src, 1, pos), "`n") 50 | 51 | ln := testArr.Length 52 | col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1)) 53 | 54 | msg := Format("{}: line {} col {} (char {})" 55 | , (next == "") ? ["Extra data", ch := SubStr(src, pos)][1] 56 | : (next == "'") ? "Unterminated string starting at" 57 | : (next == "\") ? "Invalid \escape" 58 | : (next == ":") ? "Expecting ':' delimiter" 59 | : (next == '"') ? "Expecting object key enclosed in double quotes" 60 | : (next == '"}') ? "Expecting object key enclosed in double quotes or object closing '}'" 61 | : (next == ",}") ? "Expecting ',' delimiter or object closing '}'" 62 | : (next == ",]") ? "Expecting ',' delimiter or array closing ']'" 63 | : [ "Expecting JSON value(string, number, [true, false, null], object or array)" 64 | , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1] 65 | , ln, col, pos) 66 | 67 | throw Error(msg, -1, ch) 68 | } 69 | 70 | obj := stack[1] 71 | is_array := (obj is Array) 72 | 73 | if i := InStr("{[", ch) { ; start new object / map? 74 | val := (i = 1) ? Map() : Array() ; ahk v2 75 | 76 | is_array ? obj.Push(val) : obj[key] := val 77 | stack.InsertAt(1,val) 78 | 79 | next := '"' ((is_key := (ch == "{")) ? "}" : "{[]0123456789-tfn") 80 | } else if InStr("}]", ch) { 81 | stack.RemoveAt(1) 82 | next := (stack[1]==tree) ? "" : (stack[1] is Array) ? ",]" : ",}" 83 | } else if InStr(",:", ch) { 84 | is_key := (!is_array && ch == ",") 85 | next := is_key ? '"' : '"{[0123456789-tfn' 86 | } else { ; string | number | true | false | null 87 | if (ch == '"') { ; string 88 | i := pos 89 | while i := InStr(src, '"',, i+1) { 90 | val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C") 91 | if (SubStr(val, -1) != "\") 92 | break 93 | } 94 | if !i ? (pos--, next := "'") : 0 95 | continue 96 | 97 | pos := i ; update pos 98 | 99 | val := StrReplace(val, "\/", "/") 100 | val := StrReplace(val, '\"', '"') 101 | , val := StrReplace(val, "\b", "`b") 102 | , val := StrReplace(val, "\f", "`f") 103 | , val := StrReplace(val, "\n", "`n") 104 | , val := StrReplace(val, "\r", "`r") 105 | , val := StrReplace(val, "\t", "`t") 106 | 107 | i := 0 108 | while i := InStr(val, "\",, i+1) { 109 | if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0 110 | continue 2 111 | 112 | xxxx := Abs("0x" . SubStr(val, i+2, 4)) ; \uXXXX - JSON unicode escape sequence 113 | if (xxxx < 0x100) 114 | val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6) 115 | } 116 | 117 | if is_key { 118 | key := val, next := ":" 119 | continue 120 | } 121 | } else { ; number | true | false | null 122 | val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos) 123 | 124 | if IsInteger(val) 125 | val += 0 126 | else if IsFloat(val) 127 | val += 0 128 | else if (val == "true" || val == "false") 129 | val := (val == "true") 130 | else if (val == "null") 131 | val := "" 132 | else if is_key { 133 | pos--, next := "#" 134 | continue 135 | } 136 | 137 | pos += i-1 138 | } 139 | 140 | is_array ? obj.Push(val) : obj[key] := val 141 | next := obj == tree ? "" : is_array ? ",]" : ",}" 142 | } 143 | } 144 | 145 | return tree[1] 146 | } 147 | 148 | Jxon_Dump(obj, indent:="", lvl:=1) { 149 | if IsObject(obj) { 150 | If !(obj is Array || obj is Map || obj is String || obj is Number) 151 | throw Error("Object type not supported.", -1, Format("", ObjPtr(obj))) 152 | 153 | if IsInteger(indent) 154 | { 155 | if (indent < 0) 156 | throw Error("Indent parameter must be a postive integer.", -1, indent) 157 | spaces := indent, indent := "" 158 | 159 | Loop spaces ; ===> changed 160 | indent .= " " 161 | } 162 | indt := "" 163 | 164 | Loop indent ? lvl : 0 165 | indt .= indent 166 | 167 | is_array := (obj is Array) 168 | 169 | lvl += 1, out := "" ; Make #Warn happy 170 | for k, v in obj { 171 | if IsObject(k) || (k == "") 172 | throw Error("Invalid object key.", -1, k ? Format("", ObjPtr(obj)) : "") 173 | 174 | if !is_array ;// key ; ObjGetCapacity([k], 1) 175 | out .= (ObjGetCapacity([k]) ? Jxon_Dump(k) : escape_str(k)) (indent ? ": " : ":") ; token + padding 176 | 177 | out .= Jxon_Dump(v, indent, lvl) ; value 178 | . ( indent ? ",`n" . indt : "," ) ; token + indent 179 | } 180 | 181 | if (out != "") { 182 | out := Trim(out, ",`n" . indent) 183 | if (indent != "") 184 | out := "`n" . indt . out . "`n" . SubStr(indt, StrLen(indent)+1) 185 | } 186 | 187 | return is_array ? "[" . out . "]" : "{" . out . "}" 188 | 189 | } Else If (obj is Number) 190 | return obj 191 | 192 | Else ; String 193 | return escape_str(obj) 194 | 195 | escape_str(obj) { 196 | obj := StrReplace(obj,"\","\\") 197 | obj := StrReplace(obj,"`t","\t") 198 | obj := StrReplace(obj,"`r","\r") 199 | obj := StrReplace(obj,"`n","\n") 200 | obj := StrReplace(obj,"`b","\b") 201 | obj := StrReplace(obj,"`f","\f") 202 | obj := StrReplace(obj,"/","\/") 203 | obj := StrReplace(obj,'"','\"') 204 | 205 | return '"' obj '"' 206 | } 207 | } 208 | 209 | -------------------------------------------------------------------------------- /src/Plugin/窗口切换.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: 窗口切换 3 | * @Version: 0.0.1 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-05-12 7 | * @Description: 窗口切换,支持使用拼音首字母检索窗口标题进行切换,类似Switcheroo 8 | */ 9 | 10 | /* 11 | ===Starter Plugin Info==> 12 | { 13 | "author": "AkiChase", 14 | "version": "0.0.1", 15 | "introduction": "窗口切换,支持使用拼音首字母检索窗口标题进行切换,类似Switcheroo", 16 | "icon": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA81JREFUWEftll9oW3UUx7/nd9NMKqZTVtT1bjrSrXeNw4cW0iGjLY048GUKLXN/UF+UbdJ2Ix2ID8sQlC3q2jqQKfqkuGXIRAYTmtmOPZhBYKDE3m2pqe3dyli3ka7t/nT3d+TepNrWpLnZXPegPwiXJN9zvh/OOb/7+xEe8qKH7I//CAAzeQ8PqKS4VHaXGAMbKoanK3/fFag5FC8Zf3yJyiRUlqYKIhWgCgDWUwVYBWB9/3sxH1bYtUffqJ53DFAZGSkHTTULoNpOzqyybYInZ6QeAWAAZIDYYImLYDYg2IBUDHhuGzzuahSkvA7gKX502XpHACuPDgXB/L6VnAhnbQMpL1omxMK44zKNlLnCQAuZToZ61ZHUBiZxDJBvFgTwRobXC8gTTLwl2fzsN04MnGhWHh3qZWmeKghgCcE8eaHlmZedJHaqKQ4A0C80L982N3lp+49PL1qkeK0hB5B98tKs7rwQfPD6vhd/zQXlGED99JdNd2/eWnrzSnoCyBjN+JQCuA4gBcLvYEoC5gAxJZnoPQABKZXVNz5u1OdCzAvgCZ58hwS/AUZNJpCHQZRixqAABi0zk0VSlsjkxAeBy/nKXtYRPQYQp8NNrzoG8ASjG4nwLTPvJcHHpSJSNz4MXHXa25m6xcGeBib6Mh0OWFWbtfJWoGx3NA6mnnS46d1cpr61nzxhQtFYkkZkLrc1RHH95/bjc/VZgN50OPCPYc8L4An2hBQF31Wc7p8070JjkhpIVDGxRgwNQHnG1O75ULZFDQR83R9r25qjAs4AtLoDIYDWALQKsI1cACYAWAOkE+OcFKwTFF0Zu3oukQjdmTarruv0S1BMYfIlzrT+Nv374o6f6hmyb94KVPq7PS7iE9awAGYURKPEQp9i6MkzrYbT3mt13b2AeUqP7QwVBaCt7XwJTF/psbbZB4ZT56zu3gHs0iv1eqy1sUjPWfKiASJ/MBPtoEzvFxag8kgqRCT2MOBbEIDyWu9eEkp95p3GDQAusSk3J19b0bcQAN+X13q7pvtFhEE3XD8kWpZds3fzg2xBWUf0CwBL0uHAK/nm64EBlHWcbAY4woQtY/sDee8R/zrAyLo1Vq8bwDzMEPvHPmo6ON/uotX+zu1M9LYea3v+frZhVV1Xkpg/u7zuuZ7bbveVydHSUXxeO1UoJ2n+rhoQ4gTe1R9rP1AoINf/mr/rLRAOzX0VO8lln1I+f3e1KRABs89JUA7NJUBu1mM7+4qN/+uYrHph32NkurMXkOLSKI+444m+HePFRWXUBS+l95K0mJj/Af4E/+8XBQoX0rkAAAAASUVORK5CYII=" 17 | } 18 | <==Starter Plugin Info=== 19 | */ 20 | 21 | 22 | #Include ..\Utils\PluginHelper.ah2 23 | PluginHelper.addEntryFunc((*) => Plugin_窗口切换.main()) ; 添加入口函数等待执行 24 | 25 | #DllLoad "dwmapi" ; 预先加载dwmapi.dll 26 | 27 | class Plugin_窗口切换 { 28 | 29 | static main() { 30 | SplitPath(A_LineFile, &name) ; 获取插件id即文件名 31 | this.name := name 32 | this.hwnd := PluginHelper.searchGuiHwnd 33 | 34 | this.addToStartupMode() 35 | } 36 | 37 | ; 获取所有窗口列表(结果和Alt+Tab一致) 38 | static getAllAltWinList() ; v0.21 by SKAN for ah2 on D51K/D51O @ autohotkey.com/r?t=99157 39 | { 40 | Static S_OK := 0 ; 非0时表示DwmGetWindowAttribute函数错误 41 | 42 | Local List := [] 43 | , Style := 0 44 | , ExStyle := 0 45 | , hwnd := 0 46 | 47 | For , hwnd in WinGetList() 48 | If IsVisible(hwnd) 49 | && StyledRight(hwnd) 50 | && IsAltTabWindow(hwnd) 51 | List.Push(hwnd) 52 | 53 | Return List 54 | 55 | IsVisible(hwnd, Cloaked := 0) 56 | { 57 | If S_OK = 0 58 | S_OK := DllCall("dwmapi\DwmGetWindowAttribute", "ptr", hwnd 59 | , "int", 14 ; DWMWA_CLOAKED 60 | , "uintp", &Cloaked 61 | , "int", 4 ; sizeof uint 62 | ) 63 | 64 | Style := WinGetStyle(hwnd) 65 | Return (Style & 0x10000000) && !Cloaked ; WS_VISIBLE 66 | } 67 | 68 | 69 | StyledRight(hwnd) 70 | { 71 | ExStyle := WinGetExStyle(hwnd) 72 | 73 | Return (ExStyle & 0x8000000) ? False ; WS_EX_NOACTIVATE 74 | : (ExStyle & 0x40000) ? True ; WS_EX_APPWINDOW 75 | : (ExStyle & 0x80) ? False ; WS_EX_TOOLWINDOW 76 | : True 77 | } 78 | 79 | 80 | IsAltTabWindow(Hwnd) 81 | { 82 | 83 | ExStyle := WinGetExStyle(hwnd) 84 | If (ExStyle & 0x40000) ; WS_EX_APPWINDOW 85 | Return True 86 | 87 | While hwnd := DllCall("GetParent", "ptr", hwnd, "ptr") 88 | { 89 | If IsVisible(Hwnd) 90 | Return False 91 | 92 | ExStyle := WinGetExStyle(hwnd) 93 | 94 | If (ExStyle & 0x80) ; WS_EX_TOOLWINDOW 95 | && !(ExStyle & 0x40000) ; WS_EX_APPWINDOW 96 | Return False 97 | } 98 | 99 | Return !Hwnd 100 | } 101 | } 102 | 103 | ; 获取匹配搜索的窗口信息列表 104 | static getWinList(searchText) { 105 | o := A_DetectHiddenWindows 106 | DetectHiddenWindows(false) 107 | 108 | out := [] 109 | idList := this.getAllAltWinList() 110 | for id in idList { 111 | ; 窗口不存在则跳过 112 | if (!WinExist("ahk_id " id)) 113 | continue 114 | 115 | title := WinGetTitle() 116 | if ( 117 | id == this.hwnd || ; 是主程序窗口则跳过 118 | ; 带有搜索内容 且 标题或转拼音首字母后 都不包含搜索文本则跳过 119 | searchText && 120 | !( 121 | InStr(title, searchText) || 122 | Instr(PluginHelper.Utils.chineseFirstChar(title), searchText) ; 标题中文 123 | ) 124 | ) 125 | continue 126 | 127 | ; 获取hIcon 128 | if !(hIcon := SendMessage(0x7F, 0, 0)) 129 | if !(hIcon := SendMessage(0x7F, 1, 0)) 130 | if (!(hIcon := SendMessage(0x7F, 2, 0))) 131 | if (!(hIcon := DllCall("GetClassLongPtr", "Ptr", id, "Int", -14, "Ptr"))) 132 | if (!(hIcon := DllCall("GetClassLongPtr", "Ptr", id, "Int", -34, "Ptr"))) 133 | hIcon := DllCall("LoadIcon", "uint", 0, "uint", 32512) ; 使用默认图标 134 | 135 | ; 添加到输出列表 136 | out.push({ hwnd: id, title: title, hIcon: hIcon }) 137 | } 138 | 139 | DetectHiddenWindows(o) 140 | 141 | if (out.Length >= 2) { 142 | ; 交换1,2元素位置 143 | tmp := out[1] 144 | out[1] := out[2] 145 | out[2] := tmp 146 | } 147 | return out 148 | } 149 | 150 | static addToStartupMode() { 151 | searchHandler(that, searchText) { 152 | that.pluginSearchResult := this.getWinList(searchText) 153 | 154 | ; 定义了loadImgsHandler,重载当前图像列表 155 | that.reloadLVIL(true) 156 | 157 | ; 重新加载列表 158 | that.listView.Opt("-Redraw") ;禁用重绘 159 | 160 | ; that.listView.Delete() ; reloadLVIL中已经删除了 161 | for item in that.pluginSearchResult 162 | that.listView.Add("icon" that.imgPathToImgListIndex[item.hwnd], " " item.title) 163 | 164 | that.resizeGui() ;根据搜索结果数量调整gui尺寸 并启用重绘 165 | } 166 | 167 | runHandler(that, rowNum) { 168 | ; 激活对应窗口然后隐藏搜索框 169 | hwnd := that.pluginSearchResult[rowNum].hwnd 170 | try WinActivate("ahk_id " hwnd) 171 | catch { 172 | ; 激活失败则重载搜索结果 173 | searchHandler(that, PluginHelper.searchText) 174 | return 175 | } 176 | PluginHelper.hideSearchGui() 177 | } 178 | 179 | loadImgsHandler(that) { 180 | static defaultHIcon := DllCall("LoadIcon", "uint", 0, "uint", 32512) ; 默认图标 181 | 182 | that.imgPathToImgListIndex["default"] := IL_Add(that.imgListID, "hIcon:*" defaultHIcon) 183 | for item in that.pluginSearchResult { 184 | ; hwnd作为路径 185 | index := IL_Add(that.imgListID, "hIcon:*" item.hIcon) 186 | ; 加载失败则使用默认图标 187 | that.imgPathToImgListIndex[item.hwnd] := index ? index : that.imgPathToImgListIndex["default"] 188 | } 189 | } 190 | 191 | PluginHelper.addPluginToStartupMode( 192 | this.name, 193 | "窗口切换", 194 | ["CKQH"], 195 | (*) => PluginHelper.showPluginMode( 196 | [], 197 | searchHandler, 198 | runHandler, { 199 | loadImgsHandler: loadImgsHandler, 200 | thumb: PluginHelper.getPluginHIcon(this.name) 201 | } 202 | ) 203 | ) 204 | } 205 | } -------------------------------------------------------------------------------- /docs/dev/plugin-mode/advanced.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 进阶 3 | icon: circle-right 4 | author: AkiChase 5 | date: 2023-04-14 6 | order: 2 7 | --- 8 | 9 | 在理解**插件模式**的基本应用之后,可以尝试进阶应用。 10 | 11 | ## 动态获取数据 12 | 13 | 动态获取数据一般是在 `searchHandler` 运行时,动态地从数据来源获取数据,然后将其添加、显示在列表中。 14 | 15 | 这个数据来源可以是**网络**、**其他程序**等等,只要包含了你需要列举的数据,都可以作为数据来源。 16 | 17 | 在此,以[网页搜索](../../plugin/web-search.md)插件源码中的`searchHandler`为例子,稍作修改并添加注释: 18 | 19 | ```ahk 20 | searchHandler(that, searchText) { 21 | if (searchText) { 22 | ; 如果有搜索内容 23 | ; 进行网络请求,获取当前searchText的搜索联想词 24 | res := this.outer.request(this.lxUrl PluginHelper.Utils.UrlEncode(searchText)) 25 | ; 重置搜索结果数据 26 | that.pluginSearchResult := [] 27 | ; 对网络请求结果进行解析、转换 28 | if (res && res := PluginHelper.Utils.globalMatch(res, '\')) { 29 | ; 有联想词 30 | ; 提前对搜索结果数组进行扩容 31 | that.pluginSearchResult.Capacity := res.length + 1 32 | ; 添加至搜索结果数组 33 | for item in res 34 | that.pluginSearchResult.Push(item[1]) 35 | ; 如果联想词中不包括当前搜索内容,则把当前搜索内容插入在第一位 36 | (that.pluginSearchResult.Has(1) && that.pluginSearchResult[1] = searchText) 37 | ? 0 : that.pluginSearchResult.InsertAt(1, searchText) 38 | } else ; 无联想词,仅插入当前搜索内容 39 | that.pluginSearchResult.Push(searchText) 40 | } else ; 搜索内容为空时什么都不显示 41 | that.pluginSearchResult := [] 42 | 43 | ;重置列表 44 | that.listView.Opt("-Redraw") ;禁用重绘 45 | that.listView.Delete() 46 | ; 添加搜索结果到列表 47 | for item in that.pluginSearchResult { 48 | that.listView.Add(, item) 49 | } 50 | that.resizeGui() ;根据搜索结果数量调整gui尺寸 并启用重绘 51 | } 52 | ``` 53 | 54 | 如此就实现了**网页搜索**插件中的联想词效果。 55 | 56 | ## 图标相关 57 | 58 | ### 相关概念 59 | 60 | `listView` 是可以使用图标的,在插件模式中当然也可以为每个列表项添加图标。 61 | 62 | 不过需要首先了解一下 `PluginMode` 模块的几个成员。 63 | 64 | - `that.imgPathToImgListIndex` 65 | 66 | `imgPathToImgListIndex` 是 `Map` 对象,用于存储**图片路径关键字**到[ImageList](https://orz707.gitee.io/v2/docs/commands/ListView.htm)中图标序号的映射关系,方便添加图标到`listView`中。 67 | 68 | - `that.imgListID` 69 | 70 | 当前 `listView` 绑定的 `ImageList` 的id,一般用于添加图标到 `ImageList` 中,即[IL_Add()](https://orz707.gitee.io/v2/docs/commands/ListView.htm#IL_Add) 71 | 72 | - `that.reloadLVIL(ILFlag)` 73 | 74 | `reloadLVIL(ILFlag)` 方法将清空 `listView` , 75 | 76 | 若 `ILFlag` 为真,还会重置 `imgPathToImgListIndex`,销毁旧的 `ImageList`,创建新 `ImageList` 并绑定到 `listView` 上 77 | 78 | 简言之,**重置列表和图标资源**。 79 | 80 | ### 简易文件夹示例 Plus 81 | 82 | 接下来我们升级一下基础篇的[简易文件夹示例](./basics.md#简易文件夹示例),让这个示例的列表同时支持图标。 83 | 84 | ```ahk 85 | static showPluginMode() { 86 | ; 构造文件夹相关数据 87 | searchData := [] 88 | ; PluginHelper.imgDir 为Starter存放图片资源的目录 89 | loop files PluginHelper.imgDir "\*.*" { 90 | searchData.Push({ 91 | title: A_LoopFileName, 92 | keywords: PluginHelper.Utils.chineseFirstChar(A_LoopFileName), 93 | path: A_LoopFileFullPath 94 | }) 95 | } 96 | 97 | ; 此处that指的是 pluginMode 模块,即插件模式 98 | searchHandler(that, searchText) { 99 | ; 如果有搜索内容则进行匹配 100 | if (StrLen(searchText)) { 101 | that.pluginSearchResult := [] ; 重置 pluginSearchResult 102 | for data in that.pluginSearchData { 103 | ; 如果搜索内容是title或者keywords的一部分则添加到搜索结果 104 | if (InStr(data.title, searchText) || InStr(data.keywords, searchText)) 105 | that.pluginSearchResult.Push(data) 106 | } 107 | } else ; 否则显示全部 108 | that.pluginSearchResult := that.pluginSearchData 109 | 110 | ;重置列表 111 | that.listView.Opt("-Redraw") ;禁用重绘 112 | that.listView.Delete() 113 | for data in that.pluginSearchResult { 114 | SplitPath(data.path, , , &ext) 115 | ; 实际操作时建议在数据加载阶段就把ext计算并储存,避免在此重复计算ext的值 116 | if (ext = 'ico') { 117 | ; ico文件使用全路径作为imgPath 118 | that.listView.Add("icon" that.imgPathToImgListIndex[data.path], data.title) 119 | } else { 120 | ; 其他文件使用后缀作为imgPath 121 | that.listView.Add("icon" that.imgPathToImgListIndex[ext], data.title) 122 | } 123 | } 124 | ; 调整gui尺寸 并启用重绘 125 | that.resizeGui() 126 | } 127 | 128 | runHandler(that, rowNum) { 129 | ; 通过pluginSearchResult和当前启动的行号访问到当前数据 130 | curData := that.pluginSearchResult[rowNum] 131 | ; 此处的this指的是当前插件,不要和that混淆 132 | PluginHelper.hideSearchGui() ; 隐藏搜索框 133 | PluginHelper.Utils.tip(this.name, "在文件夹中显示:`n" curData.path) 134 | PluginHelper.Utils.openFileInFolder(curData.path) ; 在文件夹中显示当前文件 135 | } 136 | 137 | ; 定义预加载图标处理函数 138 | ; 该函数会在进入插件模式和reloadLVIL(true)时被执行 139 | loadImgsHandler(that) { 140 | for data in that.pluginSearchData { 141 | SplitPath(data.path, , , &ext) 142 | if (ext = 'ico') { 143 | ; 由于ico图片资源的图标是不同的,每个都要加载 144 | ; 所以使用文件全路径作为imgPath 145 | ; 而作为图标的资源文件自然就是ico文件本身 146 | that.imgPathToImgListIndex[data.path] := IL_Add(that.imgListID, data.path) 147 | ; 此处仅仅是示例,实际上还需要考虑到图标是否会加载失败即 IL_Add 返回 0 148 | ; 失败则应当使用一个默认图标,避免出现空白 149 | 150 | } else { 151 | ; 而其他文件类型比如png图片资源的图标使用相同的,不重复加载 152 | ; 所以使用文件后缀作为imgPath 153 | ; 我们可以通过工具函数获取指定文件路径关联的图标的hIcon 154 | if (!that.imgPathToImgListIndex.Has(ext)) { 155 | hIcon := PluginHelper.Utils.associatedHIcon(data.path) 156 | that.imgPathToImgListIndex[ext] := IL_Add(that.imgListID, "HICON:*" hIcon) 157 | ; 和ico中一样,实际操作时应当加载失败的判断 158 | } 159 | } 160 | } 161 | } 162 | 163 | PluginHelper.showPluginMode( 164 | searchData, 165 | searchHandler, 166 | runHandler, { 167 | loadImgsHandler: loadImgsHandler, 168 | thumb: PluginHelper.getPluginHIcon(this.name) ; 搜索框图标 169 | } 170 | ) 171 | } 172 | ``` 173 | 174 | 效果如图: 175 | 176 | ![带图标的简易文件夹:全部](../images/plugin-mode-10.jpg) 177 | 178 | ![带图标的简易文件夹:ico](../images/plugin-mode-11.jpg) 179 | 180 | 181 | :::tip 182 | 如果有动态获取数据或者异步加载数据的情况下,可能会出现图标无法提前全部预加载的情况 183 | 184 | 那么就要把图标加载也放在`searchHandler`中,在**获取新数据后**根据情况进行图标加载 185 | ::: 186 | 187 | ## 粘贴非文本内容、拖入文件 188 | 189 | 请细看API文档[showPluginMode](../../api/showPluginMode.md)中 `pasteContentHandler`, `dropFilesHandler` 的相关解释,在此仅仅给两个代码片段,作为参考 190 | 191 | ```ahk 192 | ; 粘贴文件/位图处理函数 193 | pasteContentHandler(that, typeName, content?) { 194 | if (typeName != "file") ;非文件类型都不允许 195 | return 0 196 | if (!IsSet(content)) { 197 | content := A_Clipboard 198 | if (InStr(content, "`r`n")) ; 不允许多文件 199 | return false 200 | return InStr(FileExist(content), "D") ;仅允许单文件夹 201 | } 202 | else { ;粘贴完成后的触发 203 | PluginHelper.placeholder := "在文件夹内搜索" 204 | t := PluginHelper.searchText 205 | PluginHelper.searchText := t ; 直接触发搜索 206 | PluginHelper.setSearchTextSel(StrLen(t)) ; 游标移动到最后 207 | } 208 | } 209 | ``` 210 | 211 | ``` ahk 212 | ; 拖入文件处理函数 213 | dropFilesHandler(that, fileList, pre) { 214 | if (pre) { 215 | if (fileList.Length > 1) 216 | return false 217 | return InStr(FileExist(fileList[1]), "D") ;仅允许单文件夹 218 | } else { ;拖入生效后的触发 219 | PluginHelper.placeholder := "在文件夹内搜索" 220 | t := PluginHelper.searchText 221 | PluginHelper.searchText := t ; 直接触发搜索 222 | PluginHelper.setSearchTextSel(StrLen(t)) ; 游标移动到最后 223 | } 224 | } 225 | ``` 226 | 227 | ## 触底回调 228 | 229 | 在[文件搜索](../../plugin/file-find.md)插件中就使用了**触底回调**,实现异步加载数据,而不是直接加载全部数据。 230 | 231 | 由于代码比较复杂,只提取关键信息作为**逻辑代码**示例: 232 | 233 | ```ahk 234 | ; 异步加载列表 即toBottomHandler参数填入的函数 235 | asyncLoading(that) { 236 | ; totalNum, offset是储存在that.pluginOtherData中的其他数据 237 | ; this.pageSize属于闭包的访问到this,即文件搜索插件类,pageSize代表一次加载多少项 238 | 239 | if (that.pluginOtherData.totalNum > that.pluginOtherData.offset + this.pageSize) { 240 | ; 如果未加载完成 241 | ; 设置Everything搜索条件偏移量 242 | that.pluginOtherData.offset += this.pageSize 243 | Everything.setOffset(that.pluginOtherData.offset) 244 | ; 进行搜索 245 | if (Everything.query()) { 246 | ;... 247 | ; 将搜索结果添加到 pluginSearchResult 中 248 | ;... 249 | 250 | that.listView.Opt("-Redraw") ;禁用重绘 251 | 252 | ; 将结果添加到listView中,包括了图标资源的动态加载 253 | this.addItemsToLV(that, tempList) 254 | 255 | ; 此处不使用resizeGui()是因为在搜索处理函数中一定调用了,应当避免重复调用 256 | 257 | that.listView.Opt("Redraw") ;启用重绘 258 | } else 259 | PluginHelper.Utils.tip(this.name, "Everything搜索失败, 错误码:" Everything.getLastError()) 260 | } 261 | } 262 | ``` 263 | 264 | 265 | -------------------------------------------------------------------------------- /src/Plugin/demo.ahk: -------------------------------------------------------------------------------- 1 | /** 2 | * @Name: demo.ahk 3 | * @Version: 0.0.5 4 | * @Author: AkiChase 5 | * @LastEditors: AkiChase 6 | * @LastEditTime: 2023-04-12 7 | * @Description: 插件示例 8 | */ 9 | 10 | /* 11 | =============================================== 12 | Starter通过解析 Starter Plugin Info 和 13 | Starter Plugin Info 之间的内容来加载插件 14 | 信息,无此内容的文件将被忽略,格式见下方。 15 | 16 | icon: 插件列表中的图标,请填入无头部的base64图片 17 | =============================================== 18 | 19 | ===Starter Plugin Info==> 20 | { 21 | "author": "AkiChase", 22 | "version": "0.0.5", 23 | "introduction": "An example of Starter Plugin", 24 | "icon": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAgNJREFUWEft1UuozVEUx/HPHRmaMFBMTDwmYiJSQimUMqAoAwNRQoSSR3kVxfWIIjISojwGiiLKgBkTYSADZGAkE2VAS3vXdvr/z/2fc27nls6a/fde+7e+e62113/IGNvQGMc3APivMnALE1NPLWraW6OVgZl4k4Jew/p+A+zBiRR0LW72G+A5FuArpuFHPwHm4mUKeAUbmwYPvyY9sAlTsL9G+Eixtwr3kt9RfMKldkAjAZzFNhxrA/Aas/Axpf9XAbAP57C9DqIOIOp4AUvSwbjlwQqRqHvUP+w8thY+h3EgfT/BFrxv1agCiDRG8EnJ+TM240EFQM5QbC3Dw8JnBS5iclqLBg2Iu6VOK0DUOW6b7XZqqu81KfyAqXiLmAWtNh6XsbrYiKxEf/y1EuA64g1n24nTNYFjeSGepf2T2N3GdweGi/0bWNcrwFVsSKIxejNMFUcjgDjYSQm+YQJeYU7N7TsqQdZo0oTxOh6nA1HP3O0lR1dNmAVan+Fx7C3U48+XG2teMQlLgEPF0+3oGZYi+ZmdQdQx20+MwwvMr0l/ngNdDaJSM0bx9AJgKR4lh+iZmJJVFgBfeh3FVcJ3EH0SNhsxiru2kf4FVcK/0+JTLO46cjrYKcDyYiTvwql+A9zHyhR0Bt71GyDSHhZDaE2vweN8pyUYjZj/aAwABhn4A2ymYyH5RzvQAAAAAElFTkSuQmCC" 25 | } 26 | <==Starter Plugin Info=== 27 | */ 28 | 29 | 30 | #Include ..\Utils\PluginHelper.ah2 31 | PluginHelper.addEntryFunc((*) => Plugin_Demo.main()) ; 添加入口函数等待执行 32 | 33 | ; 使用类,避免变量名、函数名污染 34 | class Plugin_Demo { 35 | static menu := Menu() 36 | ; static gui:=unset 37 | ; static edit:=unset 38 | 39 | static guiInit() { 40 | g := Gui(, "插件基本示例1") 41 | g.BackColor := "FFFFFF" 42 | g.AddText(, "一个简单的Gui示例") 43 | 44 | SplitPath(A_LineFile, &pluginName) 45 | ; MsgBox(PluginHelper.getPluginHIcon(pluginName)) 46 | g.AddPicture("w50 h50", "hIcon:*" . PluginHelper.getPluginHIcon(pluginName)) 47 | this.edit := g.AddEdit("w200 h100", "输入文本~") 48 | g.Show("Hide") 49 | this.gui := g 50 | } 51 | 52 | static showGui() { 53 | this.gui.Show() 54 | } 55 | 56 | static main() { 57 | SplitPath(A_LineFile, &name) ; 获取插件id即文件名 58 | this.name := name 59 | 60 | ; 创建Gui的简单示例 61 | this.guiInit() 62 | 63 | ; 创建托盘菜单项的示例 64 | this.menu.Add("显示面板", (*) => this.showGui()) 65 | this.menu.Add("弹出编辑框文本", (*) => PluginHelper.Utils.tip("弹出消息", this.edit.Value, 1500)) 66 | this.menu.Add("其他", (*) => PluginHelper.Utils.tip("提示", "自定义其他的功能吧!", 1500)) 67 | ; PluginHelper是方便插件规范调用Starter API的静态类 68 | ; 插件原则上都只能通过PluginHelper暴露的API对Staarter APi进行合理的调用 69 | ; 插件相关菜单最好加入pluginMenu菜单项,更加规范 70 | PluginHelper.pluginMenu.Add("demo.ahk", this.menu) 71 | 72 | 73 | ; 定义菜单,用于插件模式右键显示 74 | pModeMenu := Menu() 75 | that := PluginHelper.getPluginMode() ; 为了定义下两行的匿名函数,需要提前访问到that即pluginMode(担心混淆可以改成别的名字), 76 | pModeMenu.Add("显示当前行号", (*) => PluginHelper.Utils.tip("demo.ahk", that.focusedRow, 1500)) 77 | pModeMenu.Add("显示当前标题", (*) => PluginHelper.Utils.tip("demo.ahk", that.pluginSearchResult[that.focusedRow].title, 1500)) 78 | 79 | ; 搜索处理函数,用于插件模式搜索 80 | search(that, searchText) { 81 | that.pluginSearchResult := [] 82 | if (searchText) { 83 | ; 搜索结果 84 | for item in that.pluginSearchData { 85 | if (InStr(item.title, searchText)) 86 | that.pluginSearchResult.Push(item) 87 | } 88 | } else ; 搜索内容为空时显示所有 89 | that.pluginSearchResult := that.pluginSearchData 90 | 91 | ; 搜索结果按标题排序 92 | PluginHelper.Utils.QuickSort(that.pluginSearchResult, (itemA, tiemB) => StrCompare(itemA.title, tiemB.title)) 93 | ;重置列表 94 | that.listView.Opt("-Redraw") ;禁用重绘 95 | that.listView.Delete() 96 | ; 添加搜索结果到列表(带图标), 若不带图标则不需要loadImgs函数 97 | for item in that.pluginSearchResult { 98 | that.listView.Add("Icon" that.imgPathToImgListIndex[item.iconPath], " " item.title) 99 | } 100 | 101 | that.resizeGui() ;根据搜索结果数量调整gui尺寸 并启用重绘 102 | } 103 | 104 | ; 加载图标的函数,用于插件模式搜索结果中 that.listView.Add("Icon" n,...) 105 | loadImgs(that) { 106 | defaultHIcon := LoadPicture(A_AhkPath, "Icon1", &_) 107 | that.imgPathToImgListIndex["default"] := IL_Add(that.imgListID, "HICON:" defaultHIcon) 108 | 109 | for item in that.pluginSearchData { 110 | if (!that.imgPathToImgListIndex.Has(item.iconPath)) { 111 | index := IL_Add(that.imgListID, "HICON:*" hIcon) 112 | ; 加载失败则使用默认图标 113 | that.imgPathToImgListIndex[item.iconPath] := index ? index : that.imgPathToImgListIndex["default"] 114 | } 115 | } 116 | } 117 | 118 | ; 进入插件模式的初始化处理函数(此处应当尽量少执行内容,因为每次打开插件模式前都需要执行) 119 | initHandler(that) { 120 | that.menu := pModeMenu 121 | } 122 | 123 | ; 创建一些固定的示例数据 124 | hIcon := PluginHelper.getPluginHIcon("demo.ahk") 125 | data := [] 126 | loop 15 { 127 | data.Push({ title: Format("{:03}", A_Index), iconPath: hIcon }) 128 | } 129 | 130 | ; 定义插件项被双击或者回车时执行的处理函数: 启动插件模式 131 | ; 注意函数接受两个参数 obj: 指向的是插件项对象{name, title, keywords, startHandler, doubleRightHandler?, menu?, hIcon} 132 | ; searchText: 搜索框搜索的内容 133 | fn(obj, searchText) { 134 | PluginHelper.showPluginMode( 135 | data, 136 | search, 137 | (that, rowNum) => rowNum > 0 ? PluginHelper.Utils.tip(this.name, that.pluginSearchResult[rowNum].title, 1500) : 0 138 | , { 139 | doubleRightHandler: (that, rowNum) => rowNum > 0 ? 140 | PluginHelper.Utils.tip(this.name, "double Right" that.pluginSearchResult[rowNum].title, 1500) : 0, 141 | loadImgsHandler: loadImgs, 142 | toBottomHandler: (that) => PluginHelper.Utils.tip(this.name, "列表触底通知:可以通过这种方式异步加载数量过大的列表", 1000, true), 143 | initHandler: initHandler, 144 | placeholder: "输入搜索内容吧!" 145 | } 146 | ) 147 | } 148 | 149 | m := Menu() 150 | curObj := {} 151 | m.Add("显示标题", (*) => PluginHelper.Utils.tip(curObj.name, "标题:" curObj.title, 1000)) 152 | contextHandler(obj) { 153 | curObj := obj 154 | m.Show() 155 | } 156 | 157 | ; 添加这个插件项到启动模式搜索界面,更详细的用法见PluginHelper.ahk 158 | PluginHelper.addPluginToStartupMode( 159 | this.name, 160 | "插件示例-启动插件模式", 161 | ["plugin demo", "CJSL"], 162 | fn, , ; 使用显式定义的函数 163 | contextHandler 164 | ) 165 | ; 可以添加多个插件项,但每个插件的插件项应当具有相同的name,不同的title 166 | PluginHelper.addPluginToStartupMode( 167 | this.name, 168 | "插件示例-项目2", 169 | ["plugin demo", "CJSL"], 170 | (obj, searchText) => (PluginHelper.Utils.tip(obj.title, "搜索框内容:" searchText, 1000), PluginHelper.hideSearchGui()), 171 | (obj, searchText) => PluginHelper.Utils.tip(obj.title, "搜索框内容:" searchText, 1000), ; 使用匿名函数作为双击Right的处理函数 172 | contextHandler, 173 | ; 定义该插件项显示的图标HICON,此处用base64ToHICON方法将base64图片载入得到HICON 174 | PluginHelper.Utils.base64ToHICON("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAnBJREFUWEfVlj9oFEEUxt+bLa6wF0whQcRCENOo2EUQYy/xZmb3sDKCoK0RhURQSFoFwdhcuNuZPY+AXKU22gmKiI2FAREbwUrwrI6dJxN2wv3Z21vZDWe2291vv/fbNx9vBmHKF065Pvy/AEKI60R0EhGPFekSEX1BxE9a68dpPqkdEEI0AcAvUjjl21BrHQw/HwGoVqsXGGMvE+E3ItosAoKIVwBg1noYYxZardarfr8RACHEDQB4aEWe5800m80fRQCCIDgax/F24nFTa/0oE4BzvoqIK1aktS4lpEIIsn5EdC+KotX9CyClXDLGzGQtCWNs2/O8541G44/TldIBKeUcEX3MkwfG2OkwDN+XCsA5n0XECADOTID4UKlUztfr9V+lAuT583GaUpZg6gC1Wu1Ar9dbdENlHJANYbfb7XQ6nd+lLoHv+6eMMe/ydIKIzkVR9KZUgCAIDsVxfBsATmRB2M0niqJr/Zr9mwHbymRP+BmG4ec87Xca3/ePx3F80N4j4uuJo1hKeYSI1gDABm3kQsR1pdRyHggp5RoR3RqjbSPislLq6w6cE3HOnyDi0oQCT7XWmRohxAYAXJ2Qkw2Xk12A/qAAwG6Ckxbav7mYmA68Syk0nzx7QUTrQ+/nh3faVIDhLdOaCCHuA8CdPEsAAA+01neHtWlbfW4AayalXDDGnM2CYIy9VUq5E9WAtDBAzr8fK+OcryDizoHEHXb+qQMlAIyctnKFsGjhvu8zQ/hs3AwoEcBZtbXWlwfmgJRykYiqAHBpDwr2W24hYksp1R4AcAo7EY0xh/cCgjH23U1A51/KsbsI7NQB/gIoe4UwRdVGRAAAAABJRU5ErkJggg==") 175 | ) 176 | 177 | ; 自定义智能模式匹配函数,可以根据多种条件判断是否匹配当前插件 178 | matchHandler(obj, searchText, *) { 179 | ; 比如根据粘贴内容是否为文件等等,可以参考"文件搜索插件" 180 | ; 此处简单的使用搜索词是否在"CJSL"文本的开头 181 | return PluginHelper.Utils.strStartWith("CJSL", searchText) 182 | } 183 | 184 | ; 添加这个插件项到智能模式搜索界面,更详细的用法见PluginHelper.ahk 185 | PluginHelper.addPluginToIntelligentMode( 186 | this.name, 187 | "插件示例-智能模式项目", 188 | matchHandler, ; 自定义匹配函数 189 | (obj, searchText) => PluginHelper.Utils.tip(obj.title, "传递内容:" searchText, 1000), 190 | contextHandler 191 | ) 192 | 193 | ; PluginHelper.Utils.tip("插件已加载", "demo.ahk") 194 | ; 该插件入口函数执行完毕 195 | } 196 | } --------------------------------------------------------------------------------