├── src ├── version.txt ├── version-zip.txt ├── AutoHotkey │ └── AutoHotkey64.exe ├── InputTipIcon │ └── default │ │ ├── app.ico │ │ ├── app.png │ │ └── app-paused.png ├── InputTipCursor │ └── default │ │ ├── oreo-blue │ │ ├── No.cur │ │ ├── Pen.cur │ │ ├── Arrow.cur │ │ ├── Cross.cur │ │ ├── Hand.cur │ │ ├── Help.cur │ │ ├── IBeam.cur │ │ ├── Wait.ani │ │ ├── SizeAll.cur │ │ ├── SizeNESW.cur │ │ ├── SizeNS.cur │ │ ├── SizeNWSE.cur │ │ ├── SizeWE.cur │ │ ├── UpArrow.cur │ │ └── AppStarting.ani │ │ ├── oreo-green │ │ ├── No.cur │ │ ├── Hand.cur │ │ ├── Help.cur │ │ ├── Pen.cur │ │ ├── Wait.ani │ │ ├── Arrow.cur │ │ ├── Cross.cur │ │ ├── IBeam.cur │ │ ├── SizeAll.cur │ │ ├── SizeNS.cur │ │ ├── SizeWE.cur │ │ ├── UpArrow.cur │ │ ├── SizeNESW.cur │ │ ├── SizeNWSE.cur │ │ └── AppStarting.ani │ │ └── oreo-red │ │ ├── Hand.cur │ │ ├── Help.cur │ │ ├── No.cur │ │ ├── Pen.cur │ │ ├── Wait.ani │ │ ├── Arrow.cur │ │ ├── Cross.cur │ │ ├── IBeam.cur │ │ ├── SizeNS.cur │ │ ├── SizeWE.cur │ │ ├── SizeAll.cur │ │ ├── SizeNESW.cur │ │ ├── SizeNWSE.cur │ │ ├── UpArrow.cur │ │ └── AppStarting.ani ├── InputTipSymbol │ └── default │ │ ├── triangle-blue.png │ │ ├── triangle-red.png │ │ └── triangle-green.png ├── utils │ ├── app-update │ │ ├── Cargo.toml │ │ ├── Cargo.lock │ │ └── src │ │ │ └── main.rs │ ├── options.ahk │ ├── app-list.ahk │ ├── ini.ahk │ ├── create-gui.ahk │ ├── tools.ahk │ ├── hotkey-gui.ahk │ ├── show.ahk │ ├── IME.ahk │ └── verify-file.ahk ├── plugins │ └── InputTip.plugin.ahk ├── files.ini ├── menu │ ├── bw-list.ahk │ ├── check-update.ahk │ ├── symbol-pos.ahk │ ├── scheme-cursor.ahk │ ├── about.ahk │ ├── startup.ahk │ ├── cursor-mode.ahk │ ├── other-config.ahk │ ├── auto-exit.ahk │ ├── switch-window.ahk │ ├── input-method.ahk │ └── app-offset.ahk └── InputTip.JAB.JetBrains.ahk ├── .gitignore ├── InputTip.bat ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.yml │ └── bug-report.yml └── workflows │ └── publish-to-winget.yml ├── .vscode ├── extensions.json └── settings.json ├── scripts ├── handle-cursor-style.ps1 └── create-winget-manifest.ps1 └── README.md /src/version.txt: -------------------------------------------------------------------------------- 1 | 2025.10.09 2 | -------------------------------------------------------------------------------- /src/version-zip.txt: -------------------------------------------------------------------------------- 1 | 2025.10.09.1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/*.exe 2 | src/InputTip.ini 3 | test/ 4 | todo/ 5 | 6 | src/utils/app-update/target/* 7 | -------------------------------------------------------------------------------- /src/AutoHotkey/AutoHotkey64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/AutoHotkey/AutoHotkey64.exe -------------------------------------------------------------------------------- /src/InputTipIcon/default/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipIcon/default/app.ico -------------------------------------------------------------------------------- /src/InputTipIcon/default/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipIcon/default/app.png -------------------------------------------------------------------------------- /InputTip.bat: -------------------------------------------------------------------------------- 1 | REM InputTip.bat 2 | start "InputTip" /min "%~dp0\src\AutoHotkey\AutoHotkey64.exe" "%~dp0\src\InputTip.ahk" 3 | -------------------------------------------------------------------------------- /src/InputTipIcon/default/app-paused.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipIcon/default/app-paused.png -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/No.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/No.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Pen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Pen.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/No.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/No.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Hand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Hand.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Help.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Help.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/No.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/No.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Pen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Pen.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Wait.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Wait.ani -------------------------------------------------------------------------------- /src/InputTipSymbol/default/triangle-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipSymbol/default/triangle-blue.png -------------------------------------------------------------------------------- /src/InputTipSymbol/default/triangle-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipSymbol/default/triangle-red.png -------------------------------------------------------------------------------- /src/utils/app-update/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app-update" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Arrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Arrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Cross.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Cross.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Hand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Hand.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Help.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Help.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/IBeam.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/IBeam.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/Wait.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/Wait.ani -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Hand.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Hand.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Help.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Help.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Pen.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Pen.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Wait.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Wait.ani -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Arrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Arrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/Cross.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/Cross.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/IBeam.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/IBeam.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/SizeNS.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/SizeNS.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/SizeWE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/SizeWE.cur -------------------------------------------------------------------------------- /src/InputTipSymbol/default/triangle-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipSymbol/default/triangle-green.png -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/SizeAll.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/SizeAll.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/SizeNESW.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/SizeNESW.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/SizeNS.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/SizeNS.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/SizeNWSE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/SizeNWSE.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/SizeWE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/SizeWE.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/UpArrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/UpArrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Arrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Arrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/Cross.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/Cross.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/IBeam.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/IBeam.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/SizeAll.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/SizeAll.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/SizeNS.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/SizeNS.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/SizeWE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/SizeWE.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/UpArrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/UpArrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/SizeAll.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/SizeAll.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/SizeNESW.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/SizeNESW.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/SizeNWSE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/SizeNWSE.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/UpArrow.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/UpArrow.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/SizeNESW.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/SizeNESW.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/SizeNWSE.cur: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/SizeNWSE.cur -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-red/AppStarting.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-red/AppStarting.ani -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-blue/AppStarting.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-blue/AppStarting.ani -------------------------------------------------------------------------------- /src/InputTipCursor/default/oreo-green/AppStarting.ani: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abgox/InputTip/HEAD/src/InputTipCursor/default/oreo-green/AppStarting.ani -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 官网 4 | url: https://inputtip.abgox.com/ 5 | about: 访问它获取更多信息 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "thqby.vscode-autohotkey2-lsp", 4 | "zero-plusplus.vscode-autohotkey-debug", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/app-update/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "app-update" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[ahk]": { 3 | "editor.defaultFormatter": "thqby.vscode-autohotkey2-lsp" 4 | }, 5 | "AutoHotkey2.InterpreterPath": "src\\AutoHotkey\\AutoHotkey64.exe", 6 | "editor.defaultFormatter": "esbenp.prettier-vscode", 7 | "editor.formatOnSave": true 8 | } 9 | -------------------------------------------------------------------------------- /src/plugins/InputTip.plugin.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | /* 4 | 5 | - 你可以在这里自定义想要的功能,例如: 6 | - 自定义快捷键 7 | - 自定义热字串 8 | - ... 9 | 10 | - 你也可以在 plugins 目录中新建一个或多个 .ahk 文件,然后在此文件中引入,例如: 11 | - 在 plugins 目录中新建一个文件名为 custom.ahk 的文件 12 | - 将自定义功能写入 custom.ahk 文件中 13 | - 在 InputTip.plugin.ahk 文件中引入 custom.ahk 文件: #Include custom.ahk 14 | 15 | - 需要注意: 不能存在死循环 16 | 17 | - 详情参考: 18 | - 官方文档: https://inputtip.abgox.com/faq/plugin 19 | - Github: https://github.com/abgox/InputTip#自定义功能 20 | - Gitee: https://gitee.com/abgox/InputTip#自定义功能 21 | 22 | */ 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 💡 功能请求 2 | description: 请求一个新功能或功能增强 3 | title: "[Request]: " 4 | labels: ["enhancement"] 5 | assignees: [abgox] 6 | body: 7 | - type: textarea 8 | id: description 9 | attributes: 10 | label: 描述这个功能请求 11 | description: | 12 | - 目前 InputTip 存在哪些不足? 13 | - 详细描述你需要的功能需求或想法。 14 | - 你认为有哪些可行的实现方案作为参考? 15 | validations: 16 | required: true 17 | - type: dropdown 18 | id: windows-version 19 | attributes: 20 | label: 你正在使用什么 Windows 系统版本? 21 | description: 如果是 Windows 10 以前的系统,请选择 `其他`, 并在最后的补充信息中说明具体系统版本。 22 | options: 23 | - Windows 11 24 | - Windows 10 25 | - 其他 26 | multiple: true 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: additional-info 31 | attributes: 32 | label: 还有其他你想要补充的信息吗? 33 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish winget-pkgs on release 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | create-pr-branch: 10 | runs-on: windows-latest 11 | env: 12 | REPO: ${{ github.repository }} 13 | USER_NAME: "abgox" 14 | USER_EMAIL: "abgohxf@outlook.com" 15 | steps: 16 | - name: Configure Git 17 | shell: pwsh 18 | run: | 19 | git config --global user.name $env:USER_NAME 20 | git config --global user.email $env:USER_EMAIL 21 | 22 | - name: Clone from GitHub and Create PR branch 23 | shell: pwsh 24 | run: | 25 | git clone --depth 1 https://${{ secrets.PAT_TOKEN }}@github.com/abgox/InputTip.git InputTip 26 | git clone --depth 1 https://${{ secrets.PAT_TOKEN }}@github.com/abgox/winget-pkgs.git winget-pkgs 27 | cd winget-pkgs 28 | $info = ..\InputTip\scripts\create-winget-manifest.ps1 "manifests\a\abgox\InputTip" 29 | $new_branch = $info.new_branch 30 | git checkout -b $new_branch 31 | git add . 32 | git commit -m $info.commit_msg 33 | git push origin $new_branch 34 | -------------------------------------------------------------------------------- /scripts/handle-cursor-style.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | # 待处理的鼠标样式目录 3 | [string]$dir, 4 | # 处理后的鼠标样式目录 5 | [string]$out = "out" 6 | ) 7 | 8 | $cursorNameMap = @{ 9 | AppStarting = @("Work") 10 | Arrow = @("Pointer", "Normal") 11 | Cross = @("Crosshair", "Precision") 12 | Hand = @("Link", "HandPointer", "PointingHand") 13 | Help = @("Question", "QuestionArrow") 14 | IBeam = @("Text", "Input") 15 | No = @("Unavailable", "NotAllowed", "Forbidden") 16 | Pen = @("Handwriting") 17 | Person = @("People") 18 | Pin = @("Place") 19 | SizeAll = @("Move", "AllScroll") 20 | SizeNESW = @("Dgn2", "Diagonal2") 21 | SizeNS = @("Vert", "Vertical") 22 | SizeNWSE = @("Dgn1", "Diagonal1") 23 | SizeWE = @("Horz", "Horizontal") 24 | UpArrow = @("Alternate", "Up") 25 | Wait = @("Busy") 26 | } 27 | 28 | # 输出目录 29 | $out_dir = Join-Path $dir $out 30 | New-Item -ItemType Directory $out_dir -Force 31 | 32 | Get-ChildItem $dir -Recurse -File | ForEach-Object { 33 | foreach ($style in $cursorNameMap.Keys) { 34 | if ($_.BaseName -eq $style -or $_.BaseName -in $cursorNameMap.$style) { 35 | Move-Item $_ (Join-Path $out_dir "$($style)$($_.Extension)") -Force 36 | Continue 37 | } 38 | } 39 | } 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/utils/options.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | #Requires AutoHotkey v2.0 4 | ;@AHK2Exe-SetLanguage 0x0804 5 | ;@Ahk2Exe-SetMainIcon InputTipIcon/default/app.ico 6 | ;@Ahk2Exe-SetCopyright Copyright (c) 2023-present abgox 7 | #SingleInstance Force 8 | #Warn All, Off 9 | 10 | Persistent 11 | ListLines 0 12 | KeyHistory 5 13 | DetectHiddenWindows 1 14 | InstallKeybdHook 15 | CoordMode 'Mouse', 'Screen' 16 | SetStoreCapsLockMode 0 17 | 18 | OnError LogError 19 | 20 | LogError(exception, mode) { 21 | if (InStr(exception.Message, "Invalid memory read/write")) { 22 | MsgBox("InputTip 内存读写错误,请尝试重启 InputTip", "InputTip 内存读写错误", "0x10") 23 | return true 24 | } 25 | return false 26 | } 27 | 28 | OnMessage(0x20A, WM_MOUSEWHEEL_Handler) ; 0x20A = WM_MOUSEWHEEL 29 | WM_MOUSEWHEEL_Handler(wParam, lParam, msg, hwnd) { 30 | buf := Buffer(256, 0) ; 创建缓冲区 31 | DllCall("GetClassName", "ptr", hwnd, "ptr", buf.ptr, "int", buf.size) 32 | controlName := StrGet(buf) ; 获取控件类名 33 | if (controlName == "ComboBox") { 34 | return 0 ; 禁用 ComboBox 控件的鼠标滚轮以避免误切换 35 | } 36 | } 37 | 38 | OnMessage(0x20E, (*) => 0) ; 0x20E = WM_MOUSEHWHEEL 39 | 40 | ;@AHK2Exe-SetVersion 2025.10.09 41 | 42 | if (A_IsCompiled) { 43 | ; exe 版本 44 | currentVersion := "2025.10.09" 45 | 46 | versionType := "exe" 47 | versionKey := "version" 48 | } else { 49 | ; zip 版本 50 | currentVersion := "2025.10.09.1" 51 | 52 | versionType := "zip" 53 | versionKey := "version-zip" 54 | } 55 | -------------------------------------------------------------------------------- /src/files.ini: -------------------------------------------------------------------------------- 1 | # InputTip: url=path 2 | [Files] 3 | src/menu/about.ahk=menu/about.ahk 4 | src/menu/app-offset.ahk=menu/app-offset.ahk 5 | src/menu/auto-exit.ahk=menu/auto-exit.ahk 6 | src/menu/bw-list.ahk=menu/bw-list.ahk 7 | src/menu/check-update.ahk=menu/check-update.ahk 8 | src/menu/cursor-mode.ahk=menu/cursor-mode.ahk 9 | src/menu/input-method.ahk=menu/input-method.ahk 10 | src/menu/other-config.ahk=menu/other-config.ahk 11 | src/menu/scheme-cursor.ahk=menu/scheme-cursor.ahk 12 | src/menu/scheme-symbol.ahk=menu/scheme-symbol.ahk 13 | src/menu/startup.ahk=menu/startup.ahk 14 | src/menu/switch-window.ahk=menu/switch-window.ahk 15 | src/menu/symbol-pos.ahk=menu/symbol-pos.ahk 16 | src/menu/tray-menu.ahk=menu/tray-menu.ahk 17 | src/utils/app-list.ahk=utils/app-list.ahk 18 | src/utils/check-version.ahk=utils/check-version.ahk 19 | src/utils/create-gui.ahk=utils/create-gui.ahk 20 | src/utils/hotkey-gui.ahk=utils/hotkey-gui.ahk 21 | src/utils/IME.ahk=utils/IME.ahk 22 | src/utils/ini.ahk=utils/ini.ahk 23 | src/utils/show.ahk=utils/show.ahk 24 | src/utils/tools.ahk=utils/tools.ahk 25 | src/utils/var.ahk=utils/var.ahk 26 | src/utils/verify-file.ahk=utils/verify-file.ahk 27 | src/CHANGELOG.md=CHANGELOG.md 28 | src/InputTip.ahk=InputTip.ahk 29 | src/InputTip.JAB.JetBrains.ahk=InputTip.JAB.JetBrains.ahk 30 | .gitignore=../.gitignore 31 | InputTip.bat=../InputTip.bat 32 | README.md=../README.md 33 | LICENSE=../LICENSE 34 | src/version.txt=version.txt 35 | src/version-zip.txt=version-zip.txt 36 | src/utils/options.ahk=utils/options.ahk 37 | -------------------------------------------------------------------------------- /src/utils/app-update/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::process::Command; 2 | use std::{env, fs}; 3 | 4 | fn main() { 5 | let args: Vec = std::env::args().collect(); 6 | 7 | if args.len() < 4 { 8 | eprintln!( 9 | "Usage: {} ", 10 | args[0] 11 | ); 12 | std::process::exit(1); 13 | } 14 | 15 | let process_name = &args[1]; 16 | let target_exe_path = &args[2]; 17 | let key_count = &args[3]; 18 | let dir = env::current_dir() 19 | .expect("Failed to get current directory") 20 | .join("InputTipSymbol") 21 | .join("default"); 22 | 23 | // 终止当前正在运行的 InputTip 进程 24 | Command::new("taskkill") 25 | .args(&["/f", "/im", process_name]) 26 | .status() 27 | .expect("Failed to execute taskkill"); 28 | 29 | // 将新版本的 exe 文件复制到目标路径 30 | let new_version_path = dir.join("abgox-InputTip-new-version.exe"); 31 | fs::copy(&new_version_path, target_exe_path).expect("Failed to copy new version"); 32 | 33 | // 删除源文件 34 | fs::remove_file(&new_version_path).expect("Failed to delete source file"); 35 | 36 | // 创建一个 txt 文件,用于标记更新完成 37 | let done_file_path = dir.join("abgox-InputTip-update-version-done.txt"); 38 | fs::write(done_file_path, "").expect("Failed to create done file"); 39 | 40 | // 运行 InputTip.exe 41 | Command::new(target_exe_path) 42 | .arg(key_count) 43 | .spawn() 44 | .expect("Failed to start target executable"); 45 | } 46 | -------------------------------------------------------------------------------- /src/utils/app-list.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | modeNameList := ["HOOK", "UIA", "GUI_UIA", "MSAA", "HOOK_DLL", "WPF", "ACC", "JAB"] 4 | 5 | ; 内部的默认模式应用列表,优先级最低,会被【光标获取模式】设置的内容覆盖 6 | defaultModeList := { 7 | HOOK: [], 8 | UIA: [ 9 | ; Word 10 | "WINWORD.EXE", 11 | ; 终端 12 | "WindowsTerminal.exe", 13 | "wt.exe", 14 | ; 一个文件管理器 15 | "OneCommander.exe", 16 | ; 有道词典 17 | "YoudaoDict.exe", 18 | ; 19 | "Mempad.exe", 20 | ; 任务管理器 21 | "Taskmgr.exe", 22 | ], 23 | ; 先后调用 GUI 和 UIA 24 | GUI_UIA: [ 25 | ; PowerPoint(PPT) 26 | "POWERPNT.EXE", 27 | ; 28 | "Notepad++.exe", 29 | ; Visual Studio 30 | "devenv.exe", 31 | ; 腾讯会议 32 | "WeMeetApp.exe", 33 | ; 微信 34 | "Weixin.exe", 35 | ], 36 | MSAA: [ 37 | ; 火狐浏览器 38 | "firefox.exe", 39 | ; Excel 40 | "EXCEL.EXE", 41 | ; 钉钉 42 | "DingTalk.exe", 43 | ; 记事本 44 | "Notepad.exe", 45 | "Notepad3.exe", 46 | ; 快速启动 47 | "Quicker.exe", 48 | ; 49 | "skylark.exe", 50 | ; 字幕编辑器 51 | "aegisub32.exe", 52 | "aegisub64.exe", 53 | "aegisub.exe", 54 | ; 图片文字识别 55 | "PandaOCR.exe", 56 | "PandaOCR.Pro.exe", 57 | ; 音速启动 58 | "VStart6.exe", 59 | ; Tim 60 | "TIM.exe", 61 | ; PowerToys 快速启动器 62 | "PowerToys.PowerLauncher.exe", 63 | ; 邮箱 64 | "Foxmail.exe", 65 | ], 66 | ; 需要调用有兼容性问题(32位)的 dll 67 | HOOK_DLL: [ 68 | ; 微信 69 | "WeChat.exe", 70 | ], 71 | WPF: [ 72 | ; Windows PowerShell ISE 73 | "powershell_ise.exe", 74 | ], 75 | ACC: [], 76 | ; 需要使用 Java Access Bridge 77 | JAB: [] 78 | } 79 | -------------------------------------------------------------------------------- /src/menu/bw-list.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_bw_list(*) { 4 | createUniqueGui(bwListGui).Show() 5 | bwListGui(info) { 6 | g := createGuiOpt("InputTip - 设置符号的黑/白名单") 7 | 8 | tab := g.AddTab3("-Wrap", ["符号的黑/白名单", "关于"]) 9 | tab.UseTab(1) 10 | g.AddText("Section cRed", gui_help_tip) 11 | 12 | 13 | if (info.i) { 14 | g.AddText(, gui_help_tip) 15 | return g 16 | } 17 | w := info.w 18 | bw := w - g.MarginX * 2 19 | 20 | g.AddText(, "-------------------------------------------------------------------------") 21 | 22 | _c := g.AddButton("xs w" bw, "白名单") 23 | _c.OnEvent("Click", set_white_list) 24 | set_white_list(*) { 25 | g.Destroy() 26 | fn_white_list() 27 | } 28 | g.AddButton("xs w" bw, "黑名单").OnEvent("Click", set_black_list) 29 | set_black_list(*) { 30 | g.Destroy() 31 | fn_common({ 32 | title: "设置符号的黑名单", 33 | tab: "符号的黑名单", 34 | config: "App-HideSymbol", 35 | link: '相关链接: 符号的名单机制' 36 | }, fn) 37 | fn() { 38 | global app_HideSymbol := StrSplit(readIniSection("App-HideSymbol"), "`n") 39 | restartJAB() 40 | } 41 | } 42 | 43 | tab.UseTab(2) 44 | g.AddText("Section cRed", "- 白名单是【符号方案】的核心,黑名单仅作为它的补充`n- 如果你想要使用【符号方案】,就必须设置白名单").Focus() 45 | g.AddLink("Section", '- 关于【符号方案】,请参考: 官网 Github Gitee`n- 关于【黑白名单机制】,请参考: 符号的名单机制') 46 | return g 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/utils/ini.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | /** 4 | * 读取配置文件 5 | * @param {String}key 配置文件中的键名 6 | * @param default 默认值 7 | * @param {"InputMethod" | "Config-v2"} section 配置文件中的分区名 8 | * @param {String} path 配置文件的路径 9 | * @returns {String} 配置文件中的值 10 | */ 11 | readIni(key, default, section := "Config-v2", path := "InputTip.ini") { 12 | try { 13 | return IniRead(path, section, key) 14 | } catch { 15 | writeIni(key, default, section, path) 16 | return default 17 | } 18 | } 19 | /** 20 | * 写入配置文件 21 | * @param key 配置文件中的键名 22 | * @param value 要写入的值 23 | * @param {"InputMethod" | "Config-v2"} section 配置文件中的分区名 24 | * @param {String} path 配置文件的路径 25 | */ 26 | writeIni(key, value, section := "Config-v2", path := "InputTip.ini") { 27 | try { 28 | IniWrite(value, path, section, key) 29 | } catch { 30 | createTipGui([{ 31 | opt: "cRed", 32 | text: "InputTip.ini 配置文件写入失败`n- 尝试重启 InputTip 解决问题`n- 检查 InputTip.ini 配置文件是否存在系统读写权限限制`n- 移除配置文件(自行备份),并重启以重新初始化配置文件", 33 | }], "InputTip - 配置文件写入失败").Show() 34 | } 35 | } 36 | 37 | /** 38 | * 读取配置文件(Section) 39 | * @param section 要读取的 Section 40 | * @param default 默认值 41 | * @param {String} path 配置文件的路径 42 | * @returns {String} 配置文件中的值 43 | */ 44 | readIniSection(section, default := "", path := "InputTip.ini") { 45 | try { 46 | return IniRead(path, section) 47 | } catch { 48 | writeIniSection(default, section, path) 49 | return default 50 | } 51 | } 52 | /** 53 | * 写入配置文件(Section) 54 | * @param value 要写入的值 55 | * @param section 要写入的 Section 56 | * @param {String} path 配置文件的路径 57 | */ 58 | writeIniSection(value, section, path := "InputTip.ini") { 59 | try { 60 | IniWrite(value, path, section) 61 | } catch { 62 | createTipGui([{ 63 | opt: "cRed", 64 | text: "InputTip.ini 配置文件写入失败`n- 尝试重启 InputTip 解决问题`n- 检查 InputTip.ini 配置文件是否存在系统读写权限限制`n- 移除配置文件(自行备份),并重启以重新初始化配置文件", 65 | }], "InputTip - 配置文件写入失败").Show() 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/menu/check-update.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_check_update(*) { 4 | gc.checkUpdateDelay := checkUpdateDelay 5 | 6 | createUniqueGui(checkUpdateGui).Show() 7 | checkUpdateGui(info) { 8 | g := createGuiOpt("InputTip - 更新检查") 9 | line := "-------------------------------------------------------------------------" 10 | tab := g.AddTab3("-Wrap", ["更新检查", "关于"]) 11 | tab.UseTab(1) 12 | g.AddText("Section cRed", gui_help_tip) 13 | 14 | if (info.i) { 15 | return g 16 | } 17 | w := info.w 18 | bw := w - g.MarginX * 2 19 | 20 | g.AddText("xs", line) 21 | g.AddText(, "更新检查频率: ") 22 | 23 | _ := g.AddEdit("yp Number Limit5 vcheckUpdateDelay") 24 | _.Value := readIni("checkUpdateDelay", 1440) 25 | _.OnEvent("Change", e_setIntervalTime) 26 | e_setIntervalTime(item, *) { 27 | value := item.value 28 | if (value != "") { 29 | if (value > 50000) { 30 | value := 50000 31 | } 32 | global checkUpdateDelay := value 33 | } 34 | } 35 | g.AddText("xs", "自动静默更新: ") 36 | _ := g.AddDropDownList("yp AltSubmit vsilentUpdate", [" 禁用", " 启用"]) 37 | _.Value := readIni("silentUpdate", 0) + 1 38 | _.OnEvent("Change", e_setSilentUpdate) 39 | e_setSilentUpdate(item, *) { 40 | global silentUpdate := item.value - 1 41 | } 42 | g.AddText("xs", line) 43 | g.AddButton("xs w" bw, "立即检查版本更新").OnEvent("Click", e_check_update) 44 | e_check_update(*) { 45 | g.Destroy() 46 | checkUpdate(1, 1, 1, 0) 47 | } 48 | aboutText := '1. 配置项 —— 更新检查频率`n - 设置每隔多长时间检查一次更新`n - 单位: 分钟,默认 1440 分钟(1 天)`n - 如果为 0,则表示不检查版本更新`n - 如果不为 0,在 InputTip 启动时,会立即检查一次`n`n2. 配置项 —— 自动静默更新`n - 启用后,不再弹出更新弹框,而是利用空闲时间自动更新`n - 空闲时间: 3 分钟内没有鼠标或键盘操作`n - 如果【更新检查频率】的值为 0,则此配置项无效`n`n3. 按钮 —— 立即检查版本更新`n - 点击这个按钮后,会立即检查一次版本更新`n - 如果没有更新弹窗且不是网络问题,则表示当前就是最新版本' 49 | lineN := "7" 50 | if (!A_IsCompiled) { 51 | g.AddButton("xs w" bw, "与源代码仓库同步").OnEvent("Click", e_get_update) 52 | e_get_update(*) { 53 | g.Destroy() 54 | getRepoCode(0, 0) 55 | } 56 | aboutText .= '`n`n4. 按钮 —— 与源代码仓库同步`n - 点击这个按钮后,会从源代码仓库中下载最新的源代码文件`n - 不管是否有版本更新,都会下载最新的源代码文件' 57 | lineN := "10" 58 | } 59 | tab.UseTab(2) 60 | g.AddEdit("ReadOnly w" bw " r" lineN, aboutText) 61 | g.AddLink(, '相关链接: 更新检查') 62 | tab.UseTab(0) 63 | g.OnEvent("Close", e_close) 64 | e_close(*) { 65 | data := g.Submit() 66 | writeIni("checkUpdateDelay", data.checkUpdateDelay) 67 | writeIni("silentUpdate", data.silentUpdate - 1) 68 | 69 | if (gc.checkUpdateDelay = 0 && checkUpdateDelay != 0) { 70 | checkUpdate(1, 1) 71 | } 72 | } 73 | return g 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "🐞 Bug 报告" 2 | description: 提交一个 Bug 报告 3 | title: "[Bug]: " 4 | labels: ["bug"] 5 | assignees: [abgox] 6 | body: 7 | - type: checkboxes 8 | id: checks 9 | attributes: 10 | label: 你应该确保已经完成了下面这些事情。 11 | options: 12 | - label: 你正在使用 InputTip 最新版本。 13 | required: true 14 | - label: 你已经阅读了项目的 README 文件。 15 | required: true 16 | - label: 你已经在 [Issues](https://github.com/abgox/InputTip/issues) 中搜索了你遇到的问题,但没有找到相关 issue。 17 | required: true 18 | - label: 你已经查看了 [一些常见的使用问题](https://inputtip.abgox.com/faq/), 其中没有你遇到的问题。 19 | required: true 20 | - label: 你已经观看了 [InputTip 使用教程](https://www.bilibili.com/video/BV15oYKz5EQ8), 仍未解决你的问题。 21 | required: true 22 | - type: input 23 | id: version 24 | attributes: 25 | label: 当前使用的 InputTip 版本 26 | description: | 27 | - 在软件的托盘菜单中,点击 **关于** 进行查看。 28 | - 如果不是最新版本,请先更新到最新版本。 29 | - exe 版本可能落后,请直接使用 zip 版本。 30 | - 更新后如果问题依然存在,再提交 issue。 31 | - 不要使用 **最新版本** 这样的描述,必须是具体的版本号。 32 | placeholder: v2025.10.01 33 | validations: 34 | required: true 35 | - type: dropdown 36 | id: InputTip-type 37 | attributes: 38 | label: 你正在使用哪种类型的安装包? 39 | description: | 40 | - 如果你正在使用 exe 版本,建议切换到 zip 版本。 41 | - [关于 zip 版本和 exe 版本的区别](https://inputtip.abgox.com/faq/zip-vs-exe) 42 | options: 43 | - zip 44 | - exe 45 | multiple: true 46 | validations: 47 | required: true 48 | - type: dropdown 49 | id: windows-version 50 | attributes: 51 | label: 你正在使用什么 Windows 系统版本? 52 | description: 如果是 Windows 10 以下的系统,请选择 `其他`, 并在最后的补充信息中说明具体系统版本。 53 | options: 54 | - Windows 11 55 | - Windows 10 56 | - 其他 57 | multiple: true 58 | validations: 59 | required: true 60 | - type: input 61 | id: exe 62 | attributes: 63 | label: 哪一个软件出现了问题?软件进程名叫什么? 64 | description: | 65 | - 使用 `托盘菜单` => `获取窗口信息` 66 | - 或者在 `任务管理器` 中找到出现问题的软件进程,右键点击进程名,点击 `打开文件所在的位置` 找到软件的运行进程 exe 文件。 67 | placeholder: code.exe 68 | validations: 69 | required: true 70 | - type: textarea 71 | id: steps-to-reproduce 72 | attributes: 73 | label: bug 复现步骤 74 | placeholder: | 75 | 如何复现这个 bug? 76 | 描述尽量简单易懂。 77 | 如果可以,请提供截图、动图、录屏等来帮助理解这个问题发生的全过程。 78 | validations: 79 | required: true 80 | - type: textarea 81 | id: expected 82 | attributes: 83 | label: 期望结果 84 | placeholder: | 85 | 你认为它应该是什么样的? 86 | - type: textarea 87 | id: actually-happening 88 | attributes: 89 | label: 实际发生了什么? 90 | placeholder: | 91 | 描述尽量简单易懂。 92 | 如果可以,请提供截图、gif、录屏等来帮助理解这个问题发生的全过程。 93 | validations: 94 | required: true 95 | - type: textarea 96 | id: additional-info 97 | attributes: 98 | label: 还有其他你想要补充的信息吗? 99 | placeholder: | 100 | 提供有问题的应用的下载地址以方便测试。 101 | 你对这个问题有什么看法? 102 | ... 103 | -------------------------------------------------------------------------------- /src/menu/symbol-pos.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_symbol_pos(*) { 4 | createUniqueGui(symbolPos).Show() 5 | symbolPos(info) { 6 | g := createGuiOpt("InputTip - 设置符号应该显示在鼠标附近的应用") 7 | tab := g.AddTab3("-Wrap", ["在鼠标附近显示符号", "关于"]) 8 | tab.UseTab(1) 9 | g.AddText("Section cRed", gui_help_tip) 10 | 11 | if (info.i) { 12 | return g 13 | } 14 | w := info.w 15 | bw := w - g.MarginX * 2 16 | 17 | g.AddText(, "在鼠标附近显示符号的应用窗口: ") 18 | g.AddDropDownList("yp AltSubmit Choose" showCursorPos + 1, [" 指定窗口", " 所有窗口"]).OnEvent("Change", e_change) 19 | e_change(item, *) { 20 | value := item.value - 1 21 | writeIni("showCursorPos", value) 22 | global showCursorPos := value 23 | restartJAB() 24 | } 25 | g.AddText("xs", "在鼠标附近显示时的水平偏移量: ") 26 | _ := g.AddEdit("yp") 27 | _.Value := readIni("showCursorPos_x", 0) 28 | _._config := "showCursorPos_x" 29 | _.OnEvent("Change", fn_offset_x) 30 | fn_offset_x(item, *) { 31 | static db := debounce((config, value) => ( 32 | writeIni(config, value) 33 | )) 34 | 35 | value := returnNumber(item.value) 36 | global showCursorPos_x := value 37 | 38 | db(item._config, value) 39 | } 40 | g.AddText("xs", "在鼠标附近显示时的垂直偏移量: ") 41 | _ := g.AddEdit("yp") 42 | _.Value := readIni("showCursorPos_y", -20) 43 | _._config := "showCursorPos_y" 44 | _.OnEvent("Change", fn_offset_y) 45 | fn_offset_y(item, *) { 46 | static db := debounce((config, value) => ( 47 | writeIni(config, value) 48 | )) 49 | 50 | value := returnNumber(item.value) 51 | global showCursorPos_y := value 52 | 53 | db(item._config, value) 54 | } 55 | 56 | _c := g.AddButton("xs w" w, "指定在鼠标附近显示符号的应用窗口") 57 | _c.OnEvent("Click", set_app_list) 58 | set_app_list(*) { 59 | g.Destroy() 60 | fn_common({ 61 | title: "指定在鼠标附近显示符号的应用窗口", 62 | tab: "在鼠标附近显示符号的应用窗口", 63 | config: "ShowNearCursor", 64 | link: '相关链接: 在鼠标附近显示符号' 65 | }, fn) 66 | fn() { 67 | global ShowNearCursor := StrSplit(readIniSection("ShowNearCursor"), "`n") 68 | restartJAB() 69 | } 70 | } 71 | tab.UseTab(2) 72 | g.AddEdit("ReadOnly VScroll r7 w" w, "1. 简要说明`n - 在 v1 版本中,通过在鼠标的附近显示符号,没有发现兼容性问题`n - 而 v2 版本中,部分窗口无法获取到输入光标位置,就可以使用它`n - 它会让符号显示在鼠标附近,并跟随鼠标移动`n - 详情参考下方的相关链接`n`n2. 配置项 —— 在鼠标附近显示符号的应用窗口`n - 【指定窗口】: 还需要通过下方的按钮去指定窗口`n - 【所有窗口】: 它就实现了 v1 版本中同样的效果`n`n3. 配置项 —— 在鼠标附近显示时的水平/垂直偏移量`n - 它指的是符号在鼠标附近显示时的偏移量`n - 注意,符号最终的偏移量为 o1 + o4`n - o1 指的是符号配置页面中的偏移量`n - o4 指的是这里的偏移量`n`n4. 按钮 —— 指定在鼠标附近显示符号的应用窗口`n - 如果你使用了【指定窗口】,就需要使用它`n - 通过它去指定需要在鼠标附近显示符号的应用窗口`n`n5. 建议`n - 减小【其他设置】的【轮询响应间隔时间】,保持在 50 毫秒以内`n - 因为显示在鼠标附近时,符号位置会随鼠标移动实时更新`n - 如果此值过高,符号位置更新不及时,会出现符号显示卡顿的现象") 73 | g.AddLink(, '相关链接: 应用窗口兼容情况 v1 版本') 74 | return g 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/utils/create-gui.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | try { 4 | gui_font_size := IniRead("InputTip.ini", "Config-v2", "gui_font_size") 5 | } catch { 6 | try { 7 | IniWrite("12", "InputTip.ini", "Config-v2", "gui_font_size") 8 | } 9 | gui_font_size := "12" 10 | } 11 | 12 | ; g.SetFont(fontOpt*) 13 | fontOpt := ["s" gui_font_size, "Microsoft YaHei"] 14 | 15 | 16 | /** 17 | * - 创建 Gui 对象。 18 | * - 能够获取窗口最终的坐标和宽高,方便配置控件(如按钮宽度)。 19 | * - 原理: 通过先执行一次隐藏显示来获取信息,相当于实际会运行两次 20 | * @param {Func} callback 21 | * - 回调函数接受形参 `info` 22 | * - `info.x`,`info.y`,`info.w`,`info.h`: 最终计算得到的窗口坐标和宽高。 23 | * - 当执行隐藏显示时,`info.i` 为 `1`,否则为 `0` 24 | * @returns {Gui} 返回 Gui 对象 25 | * @example 26 | * createGui(helloGui).Show() 27 | * helloGui(info) { 28 | * g := createGuiOpt("") 29 | * g.AddText(, "xxxxxxxxxxxxxxxxxxx") 30 | * ; 第一次隐藏显示,可以通过它在合适的地方直接返回,减少多余的执行 31 | * if (info.i) { 32 | * return g 33 | * } 34 | * w := info.w 35 | * bw := w - g.MarginX * 2 36 | * ; 其他控件... 37 | * g.AddButton("w" bw, "确定") 38 | * return g 39 | * } 40 | */ 41 | createGui(callback) { 42 | g := callback({ x: 0, y: 0, w: 0, h: 0, i: 1 }) 43 | g.Show("Hide") 44 | g.GetPos(&x, &y, &w, &h) 45 | g.Destroy() 46 | return callback({ x: x, y: y, w: w, h: h, i: 0 }) 47 | } 48 | 49 | /** 50 | * 创建一个唯一的 Gui 51 | * @param {Func} callback 52 | * - 回调函数接受形参 `info` 53 | * - `info.x`,`info.y`,`info.w`,`info.h`: 最终计算得到的窗口坐标和宽高。 54 | * - 当执行隐藏显示时,`info.i` 为 `1`,否则为 `0` 55 | * @returns {Gui} 返回 Gui 对象 56 | * @example 57 | * createUniqueGui(helloGui).Show() 58 | * helloGui(info) { 59 | * g := createGuiOpt("") 60 | * g.AddText(, "xxxxxxxxxxxxxxxxxxx") 61 | * ; 第一次隐藏显示,可以通过它在合适的地方直接返回,减少多余的执行 62 | * if (info.i) { 63 | * return g 64 | * } 65 | * w := info.w 66 | * bw := w - g.MarginX * 2 67 | * ; 其他控件... 68 | * g.AddButton("w" bw, "确定") 69 | * return g 70 | * } 71 | */ 72 | createUniqueGui(callback) { 73 | static w := Map() 74 | g := createGui(callback) 75 | g.GetPos(, , &width, &height) 76 | ; 基本确保唯一性 77 | id := g.Title "_" width "_" height 78 | if (w.Has(id)) { 79 | try { 80 | w.Get(id).Destroy() 81 | w.Delete(id) 82 | } 83 | } 84 | w.Set(id, g) 85 | return g 86 | } 87 | 88 | /** 89 | * @param title Gui 标题 90 | * @param {Array} fontOption 字体配置(如: ["s12", "Microsoft YaHei"]) 91 | * - 这里为了方便 InputTip 使用,默认值使用了外部的 fontOpt 变量 92 | * @param {String} guiOption Gui 初始化配置 93 | * @returns {Gui} 返回 Gui 对象 94 | */ 95 | createGuiOpt(title, fontOption := fontOpt, guiOption := "") { 96 | g := Gui(guiOption, title) 97 | g.SetFont(fontOption*) 98 | return g 99 | } 100 | 101 | 102 | /** 103 | * 创建一个提示 Gui 104 | * @param {Array} Tips 显示的提示信息 105 | * @param {String} title Gui 标题 106 | * @param {String} btnText 按钮文本 107 | * @returns {Gui} 返回 Gui 对象 108 | */ 109 | createTipGui(Tips, title := "InputTip - 提示", btnText := "我知道了") { 110 | tipGui(info) { 111 | g := createGuiOpt(title) 112 | 113 | for v in Tips { 114 | g.AddLink(v.opt, v.text) 115 | } 116 | 117 | if (info.i) { 118 | return g 119 | } 120 | 121 | btn := g.AddButton("xs w" info.w, btnText) 122 | btn.Focus() 123 | btn.OnEvent("Click", (*) => g.Destroy()) 124 | return g 125 | } 126 | return createUniqueGui(tipGui) 127 | } 128 | -------------------------------------------------------------------------------- /src/menu/scheme-cursor.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_scheme_cursor(*) { 4 | createUniqueGui(cursorStyleGui).Show() 5 | cursorStyleGui(info) { 6 | g := createGuiOpt("InputTip - 状态提示 - 鼠标方案") 7 | tab := g.AddTab3("-Wrap", ["鼠标方案", "关于"]) 8 | tab.UseTab(1) 9 | g.AddText("Section cRed", gui_help_tip) 10 | 11 | if (info.i) { 12 | g.AddText(, gui_width_line) 13 | return g 14 | } 15 | w := info.w 16 | bw := w - g.MarginX * 2 17 | line := gui_width_line "------" 18 | 19 | g.AddText("xs", line) 20 | 21 | g.AddText("xs", "加载鼠标样式:") 22 | _ := g.AddDropDownList("w" bw / 2 " yp AltSubmit Choose" changeCursor + 1, ["【否】保持原本的鼠标样式", "【是】随输入法状态而变化"]) 23 | _.OnEvent("Change", e_change_cursor) 24 | e_change_cursor(item, *) { 25 | static last := changeCursor + 1 26 | if (last = item.value) { 27 | return 28 | } 29 | last := item.value 30 | 31 | if (item.value = 1) { 32 | writeIni("changeCursor", 0) 33 | global changeCursor := 0 34 | revertCursor(cursorInfo) 35 | createTipGui([{ 36 | opt: "", 37 | text: "正在尝试恢复到启动 InputTip 之前的鼠标样式" 38 | }, { 39 | opt: "cRed", 40 | text: "可能无法完全恢复,这时就需要重启系统以完全移除动态加载的鼠标样式", 41 | }, { 42 | opt: "cGray", 43 | text: "如果你不想重启系统,也可以通过系统设置,重新应用之前的鼠标样式" 44 | }], "InputTip - 尝试恢复鼠标样式").Show() 45 | } else { 46 | writeIni("changeCursor", 1) 47 | global changeCursor := 1 48 | reloadCursor() 49 | } 50 | restartJAB() 51 | } 52 | 53 | g.AddText("xs", line) 54 | createGuiOpt("").AddText(, "中文状态").GetPos(, , &__w) 55 | dirList := StrSplit(cursorDir, ":") 56 | if (dirList.Length = 0) { 57 | dirList := getCursorDir() 58 | } 59 | for i, v in ["CN", "EN", "Caps"] { 60 | g.AddText("xs", stateMap.%v% ":") 61 | _ := g.AddDropDownList("yp r9 w" bw - __w - g.MarginX, dirList) 62 | _._config := v "_cursor" 63 | _.OnEvent("Change", e_cursor_dir) 64 | try { 65 | _.Text := %v "_cursor"% 66 | } catch { 67 | _.Text := "" 68 | } 69 | } 70 | e_cursor_dir(item, *) { 71 | writeIni(item._config, item.Text) 72 | updateCursor() 73 | reloadCursor() 74 | } 75 | 76 | _w := bw / 3 - g.MarginX / 3 77 | g.AddButton("xs w" _w, "打开鼠标样式目录").OnEvent("Click", (*) => (Run("explorer.exe InputTipCursor"))) 78 | g.AddButton("yp w" _w, "刷新下拉列表").OnEvent("Click", (*) => ( 79 | getPathList() 80 | fn_scheme_cursor() 81 | )) 82 | g.AddButton("yp w" _w, "下载其他鼠标样式").OnEvent("Click", (*) => (Run("https://inputtip.abgox.com/download/extra"))) 83 | 84 | tab.UseTab(2) 85 | g.AddEdit("ReadOnly r11 w" bw, "1. 配置 —— 加载鼠标样式`n - 根据不同的输入法状态加载下方选择的对应的鼠标样式`n`n2. 配置 —— 中文状态/英文状态/大写锁定`n - 通过下拉列表为不同状态指定鼠标样式`n - InputTip 会根据当前输入法状态切换鼠标样式`n`n3. 按钮 —— 打开鼠标样式目录`n - 点击它,会自动打开存放鼠标样式的目录`n - 在这个目录下添加鼠标样式文件夹后,会显示在下拉列表中`n`n4. 按钮 —— 刷新下拉列表`n - 当修改了鼠标样式目录,可能需要点击它去刷新下拉列表`n`n5. 按钮 —— 下载其他鼠标样式`n - 点击它,前往官网查看已经适配的其他鼠标样式`n - 下载后,解压到鼠标样式目录下即可使用") 86 | g.AddLink(, '相关链接: 自定义鼠标样式') 87 | return g 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/menu/about.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_about(*) { 4 | createUniqueGui(aboutGui).Show() 5 | aboutGui(info) { 6 | g := createGuiOpt("InputTip - " versionType " 版本 - " (A_IsAdmin ? "以管理员权限启动" : "以当前用户权限启动")) 7 | 8 | g.AddText("Center w" info.w, "InputTip - 一个输入法状态管理工具") 9 | g.SetFont("s" readIni("gui_font_size", "12") / 1.2) 10 | g.AddText("Center w" info.w, "实时提示(鼠标方案/符号方案) + 状态切换(窗口触发/热键触发)") 11 | g.SetFont(fontOpt*) 12 | 13 | tab := g.AddTab3("-Wrap", ["关于项目", "赞赏支持", "参考项目"]) 14 | tab.UseTab(1) 15 | g.AddText("Section", '版本号: ') 16 | g.AddEdit("yp ReadOnly cRed", currentVersion) 17 | g.AddText("xs", '开发者: ') 18 | g.AddLink("yp", 'abgox') 19 | 20 | if (info.i) { 21 | g.AddText("xs", "-------------------------------------------------------------------------------") 22 | return g 23 | } 24 | w := info.w 25 | bw := w - g.MarginX * 2 26 | line := "-------------------------------------------------------------------------------------" 27 | 28 | g.AddText("xs", line) 29 | ; g.AddLink("xs", 'QQ 群: ') 30 | ; g.AddEdit("yp ReadOnly", '451860327') 31 | ; g.AddLink("xs", '点击加入【QQ 群】交流反馈') 32 | g.AddLink("xs", '点击前往 Github 提交 Issue') 33 | g.AddLink("xs", '点击加入【腾讯频道】交流反馈') 34 | 35 | g.AddText("xs", line) 36 | g.AddLink("xs", '1. 官网: inputtip.abgox.com') 37 | g.AddLink("xs", '2. Github: github.com/abgox/InputTip') 38 | g.AddLink("xs", '3. Gitee: gitee.com/abgox/InputTip') 39 | tab.UseTab(2) 40 | g.AddLink("Section", '- InputTip 作为一个开源项目,是免费使用的') 41 | g.AddLink("xs", '- 但它的开发和维护工作,消耗了我的许多时间与精力') 42 | g.AddLink("xs", '- 如果你喜欢 InputTip,请为它的项目仓库点亮一个 Star') 43 | g.AddLink("xs", '- 或者前往 abgox.com/donate 进行赞赏以支持我的付出') 44 | 45 | g.AddLink("xs", ' - 建议赞赏时进行必要的留言: 赞赏的项目、目的、想法等内容') 46 | g.AddLink("xs", ' - 它们会显示在 abgox.com/donate-list 这个赞赏名单中') 47 | g.AddLink("xs", ' - 如果不想显示在其中,也可以留言说明,会进行匿名处理') 48 | g.AddLink("xs", '- abgox.com 还有更多信息、其他项目等等,或许它们刚好也能帮助到你') 49 | g.AddLink("xs", '- 总之,非常感谢你的支持,能够通过软件的方式相遇,何尝不是一种缘分') 50 | tab.UseTab(3) 51 | g.AddLink("Section", '1. ImTip - aardio') 52 | g.AddLink("xs", '2. KBLAutoSwitch - flyinclouds') 53 | g.AddLink("xs", '3. AutoHotkeyScripts - Tebayaki') 54 | g.AddLink("xs", '4. RedDot - Autumn-one') 55 | g.AddLink("xs", '5. language-indicator - yakunins') 56 | g.AddText("xs", '- InputTip v1 是在鼠标附近显示带文字的方块符号') 57 | g.AddText("xs", '- InputTip v2 默认通过不同颜色的鼠标样式来区分') 58 | g.AddText("xs", '- 后来参照了 RedDot 和 language-indicator 的设计') 59 | g.AddText("xs", '- 因为实现简单直接: 去掉 v1 中方块符号的文字,加上不同的背景颜色') 60 | 61 | tab.UseTab(0) 62 | btn := g.AddButton("Section w" w, "关闭") 63 | btn.OnEvent("Click", e_close) 64 | e_close(*) { 65 | g.Destroy() 66 | } 67 | return g 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/InputTip.JAB.JetBrains.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | #Warn All, Off 4 | 5 | #Include utils/options.ahk 6 | 7 | #NoTrayIcon 8 | ;@AHK2Exe-SetName InputTip.JAB 9 | ;@Ahk2Exe-SetOrigFilename InputTip.JAB.JetBrains.ahk 10 | ;@AHK2Exe-SetDescription InputTip.JAB - 一个输入法状态管理工具(提示/切换) 11 | 12 | isJAB := 1 13 | 14 | #Include utils/tools.ahk 15 | #Include utils/create-gui.ahk 16 | #Include utils/ini.ahk 17 | #Include utils/IME.ahk 18 | #Include utils/app-list.ahk 19 | #Include utils/var.ahk 20 | 21 | /** 22 | * 跳过非 JAB/JetBrains IDE 程序,交由 InputTip 处理 23 | * @param exe_str 进程字符串,如 ":webstorm64.exe:" 24 | * @returns {1 | 0} 是否需要跳过 25 | */ 26 | needSkip(exe_str) { 27 | return showCursorPos || !InStr(modeList.JAB, exe_str) 28 | } 29 | 30 | returnCanShowSymbol(&left, &top, &right, &bottom) { 31 | try { 32 | GetCaretPosFromJAB(&left, &top, &right, &bottom) 33 | } catch { 34 | left := 0, top := 0, right := 0, bottom := 0 35 | return 0 36 | } 37 | 38 | s := isWhichScreen(screenList) 39 | if (s.num) { 40 | try { 41 | offset := app_offset_screen.%s.num% 42 | left += offset.x 43 | top += offset.y 44 | } 45 | try { 46 | offset := app_offset.%exe_name exe_title%.%s.num% 47 | left += offset.x 48 | top += offset.y 49 | } catch { 50 | try { 51 | left += app_offset.%exe_name%.%s.num%.x 52 | top += app_offset.%exe_name%.%s.num%.y 53 | } 54 | } 55 | return left 56 | } 57 | 58 | return 0 59 | } 60 | 61 | /** 62 | * Gets the position of the caret with UIA, Acc or CaretGetPos. 63 | * @link https://www.reddit.com/r/AutoHotkey/comments/ysuawq/get_the_caret_location_in_any_program/ 64 | * @link https://www.autohotkey.com/boards/viewtopic.php?t=130941#p576439 65 | * @param X Value is set to the screen X-coordinate of the caret 66 | * @param Y Value is set to the screen Y-coordinate of the caret 67 | * @param W Value is set to the width of the caret 68 | * @param H Value is set to the height of the caret 69 | */ 70 | GetCaretPosFromJAB(&X?, &Y?, &W?, &H?) { 71 | static JAB := InitJAB() ; Source: https://github.com/Elgin1/Java-Access-Bridge-for-AHK 72 | if JAB && (hWnd := WinExist("A")) && DllCall(JAB.module "\isJavaWindow", "ptr", hWnd, "CDecl Int") { 73 | if JAB.firstRun 74 | Sleep(200), JAB.firstRun := 0 75 | prevThreadDpiAwarenessContext := DllCall("SetThreadDpiAwarenessContext", "ptr", -2, "ptr") 76 | DllCall(JAB.module "\getAccessibleContextWithFocus", "ptr", hWnd, "Int*", &vmID := 0, JAB.acType "*", &ac := 0, "Cdecl Int") "`n" 77 | DllCall(JAB.module "\getCaretLocation", "Int", vmID, JAB.acType, ac, "Ptr", Info := Buffer(16, 0), "Int", 0, "Cdecl Int") 78 | DllCall(JAB.module "\releaseJavaObject", "Int", vmID, JAB.acType, ac, "CDecl") 79 | DllCall("SetThreadDpiAwarenessContext", "ptr", prevThreadDpiAwarenessContext, "ptr") 80 | X := NumGet(Info, 0, "Int"), Y := NumGet(Info, 4, "Int"), W := NumGet(Info, 8, "Int"), H := NumGet(Info, 12, "Int") 81 | hMonitor := DllCall("MonitorFromWindow", "ptr", hWnd, "int", 2, "ptr") ; MONITOR_DEFAULTTONEAREST 82 | DllCall("Shcore.dll\GetDpiForMonitor", "ptr", hMonitor, "int", 0, "uint*", &dpiX := 0, "uint*", &dpiY := 0) 83 | if dpiX 84 | X := DllCall("MulDiv", "int", X, "int", dpiX, "int", 96, "int"), Y := DllCall("MulDiv", "int", Y, "int", dpiX, "int", 96, "int"), W := DllCall("MulDiv", "int", W, "int", dpiX, "int", 96, "int"), H := DllCall("MulDiv", "int", H, "int", dpiX, "int", 96, "int") 85 | if X || Y || W || H 86 | return 87 | } 88 | InitJAB() { 89 | ret := {}, ret.firstRun := 1, ret.module := A_PtrSize = 8 ? "WindowsAccessBridge-64.dll" : "WindowsAccessBridge-32.dll", ret.acType := "Int64" 90 | ret.DefineProp("__Delete", { call: (this) => DllCall("FreeLibrary", "ptr", this) }) 91 | if !(ret.ptr := DllCall("LoadLibrary", "Str", ret.module, "ptr")) && A_PtrSize = 4 { 92 | ; try legacy, available only for 32-bit 93 | ret.acType := "Int", ret.module := "WindowsAccessBridge.dll", ret.ptr := DllCall("LoadLibrary", "Str", ret.module, "ptr") 94 | } 95 | if !ret.ptr 96 | return ; Failed to load library. Make sure you are running the script in the correct bitness and/or Java for the architecture is installed. 97 | DllCall(ret.module "\Windows_run", "Cdecl Int") 98 | return ret 99 | } 100 | } 101 | 102 | #Include utils/show.ahk 103 | -------------------------------------------------------------------------------- /src/utils/tools.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | /** 4 | * 防抖函数 5 | * @param {Func} fn 要执行的函数 6 | * @param {Number} delay 延迟时间(ms) 7 | * @returns {Func} 函数 8 | */ 9 | debounce(fn, delay := 1000) { 10 | params := [] 11 | timerFunc := (*) => fn.Call(params*) 12 | 13 | return (args*) => ( 14 | params := args, 15 | SetTimer(timerFunc, 0), 16 | SetTimer(timerFunc, -delay) 17 | ) 18 | } 19 | 20 | /** 21 | * 将数组转换成字符串,并用指定分隔符连接 22 | * @param {Array} arr 数组 23 | * @param {String} separator 分隔符 24 | * @returns {String} 连接后的字符串 25 | */ 26 | arrJoin(arr, separator := ",") { 27 | res := "" 28 | for i, v in arr { 29 | res .= i = arr.Length ? v : v separator 30 | } 31 | return res 32 | } 33 | 34 | /** 35 | * 检查是否存在子目录 36 | * @param {String} path 目录路径 37 | * @returns 是否存在子目录 38 | */ 39 | hasChildDir(path) { 40 | Loop Files path "\*", "D" { 41 | return A_Index 42 | } 43 | } 44 | 45 | /** 46 | * 鼠标是否悬停在指定窗口上 47 | * @param WinTitle 窗口标题 48 | * @returns 是否悬停在窗口上 49 | */ 50 | isMouseOver(WinTitle) { 51 | try { 52 | MouseGetPos(, , &Win) 53 | return WinExist(WinTitle " ahk_id " Win) 54 | } 55 | return 0 56 | } 57 | 58 | /** 59 | * 替换环境变量中的变量 60 | * @param {String} str 环境变量 61 | * @returns {String} 替换后的字符串 62 | */ 63 | replaceEnvVariables(str) { 64 | while RegExMatch(str, "%\w+%", &match) { 65 | env := match[] 66 | envValue := EnvGet(StrReplace(env, "%", "")) 67 | str := StrReplace(str, env, envValue) 68 | } 69 | return str 70 | } 71 | 72 | /** 73 | * 获取到屏幕信息 74 | * @returns {Array} 一个数组 [{num:1,left:0,top:0,right:0,bottom:0}] 75 | */ 76 | getScreenInfo() { 77 | list := [] 78 | MonitorCount := MonitorGetCount() 79 | MonitorPrimary := MonitorGetPrimary() 80 | Loop MonitorCount 81 | { 82 | MonitorGet A_Index, &L, &T, &R, &B 83 | MonitorGetWorkArea A_Index, &WL, &WT, &WR, &WB 84 | list.Push({ 85 | main: MonitorPrimary, 86 | count: MonitorCount, 87 | num: A_Index, 88 | left: L, 89 | top: T, 90 | right: R, 91 | bottom: B 92 | }) 93 | } 94 | return list 95 | } 96 | 97 | /** 98 | * 聚焦的窗口在哪个屏幕 99 | */ 100 | isWhichScreen(screenList) { 101 | try { 102 | WinGetClientPos(&x, &y, &w, &h, "A") 103 | 104 | ; 窗口的中心坐标 105 | cx := x + w / 2 106 | cy := y + h / 2 107 | } catch { 108 | return { main: 0, count: 0, num: 0, left: 0, top: 0, right: 0, bottom: 0 } 109 | } 110 | 111 | for v in screenList { 112 | if (cx >= v.left && cx <= v.right && cy >= v.top && cy <= v.bottom) { 113 | return v 114 | } 115 | } 116 | } 117 | 118 | ; 从字符串中提取出数字,支持负数和小数 119 | returnNumber(value) { 120 | if (value = "" || !(value ~= "\d")) { 121 | return 0 122 | } 123 | RegExMatch(value, "(-?\d+\.?\d*)", &numbers) 124 | return numbers[1] 125 | } 126 | 127 | /** 128 | * 比对版本号 129 | * @param new 新版本号 130 | * @param old 旧版本号 131 | * @returns {1 | -1 | 0} 132 | * - new > old : 1 133 | * - new < old : -1 134 | * - new = old : 0 135 | */ 136 | compareVersion(new, old) { 137 | newParts := StrSplit(new, ".") 138 | oldParts := StrSplit(old, ".") 139 | for i, part1 in newParts { 140 | try { 141 | part2 := oldParts[i] 142 | } catch { 143 | part2 := 0 144 | } 145 | if (part1 > part2) { 146 | return 1 ; new > old 147 | } else if (part1 < part2) { 148 | return -1 ; new < old 149 | } 150 | } 151 | return 0 ; new = old 152 | } 153 | 154 | ; 设置托盘图标 155 | setTrayIcon(path, isPaused := A_IsPaused) { 156 | if isJAB 157 | return 158 | 159 | try { 160 | TraySetIcon(path, , 1) 161 | } catch { 162 | if (isPaused) { 163 | path := "InputTipIcon\default\app-paused.png" 164 | global iconPaused := path 165 | writeIni("iconPaused", path) 166 | } else { 167 | path := "InputTipIcon\default\app.png" 168 | global iconRunning := path 169 | writeIni("iconRunning", path) 170 | } 171 | TraySetIcon(path, , 1) 172 | } 173 | } 174 | 175 | ; 返回当前的时间,作为唯一标识符 176 | returnId() { 177 | return FormatTime(A_Now, "yyyy-MM-dd-HH:mm:ss") "." A_MSec 178 | } 179 | 180 | ; 还原鼠标样式 181 | revertCursor(cursorInfo) { 182 | try { 183 | for v in cursorInfo { 184 | if (v.origin) { 185 | DllCall("SetSystemCursor", "Ptr", DllCall("LoadCursorFromFile", "Str", v.origin, "Ptr"), "Int", v.value) 186 | } else { 187 | ; 如果没有获取到原始的工形光标样式文件,则直接加载一个默认的样式 188 | if (v.type == "IBEAM") { 189 | DllCall("SetSystemCursor", "Ptr", DllCall("LoadCursorFromFile", "Str", "C:\Windows\Cursors\beam_m.cur", "Ptr"), "Int", v.value) 190 | } 191 | } 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/menu/startup.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_startup(item, *) { 4 | ; 注册表: 开机自启动 5 | HKEY_startup := "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run" 6 | ; 注册表: 值的名称 7 | regName := "abgox - " A_ScriptName 8 | 9 | global isStartUp 10 | 11 | if (isStartUp) { 12 | if (isStartUp != 2 && !A_IsAdmin) { 13 | createTipGui([{ 14 | opt: "cRed", 15 | text: "你需要以管理员权限运行来取消【开机自启动】`n设置: 【托盘菜单】=>【以管理员权限启动】", 16 | }], "InputTip - 错误").Show() 17 | return 18 | } 19 | 20 | try { 21 | FileDelete(A_Startup "/" fileLnk) 22 | } 23 | try { 24 | RegDelete(HKEY_startup, regName) 25 | } 26 | try { 27 | Run('schtasks /delete /tn "abgox.InputTip.noUAC" /f', , "Hide") 28 | } 29 | 30 | A_TrayMenu.Uncheck(item) 31 | isStartUp := 0 32 | writeIni("isStartUp", isStartUp) 33 | 34 | createTipGui([{ 35 | opt: "", 36 | text: "InputTip 的【开机自启动】已经取消了`n可通过【托盘菜单】=>【开机自启动】再次设置", 37 | }], "InputTip - 取消开机自启动").Show() 38 | } else { 39 | createUniqueGui(startupGui).Show() 40 | startupGui(info) { 41 | g := createGuiOpt("InputTip - 设置开机自启动") 42 | tab := g.AddTab3("-Wrap", ["设置开机自启动", "关于"]) 43 | tab.UseTab(1) 44 | g.AddText("Section cRed", gui_help_tip) 45 | 46 | if (info.i) { 47 | return g 48 | } 49 | w := info.w 50 | bw := w - g.MarginX * 2 51 | 52 | g.AddText(, "-------------------------------------------------------------------------") 53 | 54 | if (A_IsAdmin) { 55 | btnOpt := '' 56 | pad := "" 57 | tip := "建议使用: 【任务计划程序】 > 【注册表】 > 【应用快捷方式】`n由于权限或系统限制等环境因素,【应用快捷方式】可能无效" 58 | } else { 59 | btnOpt := ' Disabled ' 60 | pad := " (不可用)" 61 | tip := "当前不是以管理员权限启动,【任务计划程序】和【注册表】不可用`n你可以通过点击【托盘菜单】中的【以管理员权限启动】使它们可用" 62 | } 63 | 64 | btn := g.AddButton("Section w" bw btnOpt, "任务计划程序" pad) 65 | 66 | btn.OnEvent("Click", e_useTask) 67 | e_useTask(*) { 68 | if (A_IsCompiled) { 69 | done := createScheduleTask(A_ScriptFullPath, "abgox.InputTip.noUAC", [0], , , 1) 70 | } else { 71 | done := createScheduleTask(A_AhkPath, "abgox.InputTip.noUAC", [A_ScriptFullPath, 0], , , 1) 72 | } 73 | 74 | if (done) { 75 | fn_update_user(A_UserName) 76 | isStartUp := 1 77 | ; FileCreateShortcut("C:\WINDOWS\system32\schtasks.exe", A_Startup "\" fileLnk, , "/run /tn `"abgox.InputTip.noUAC`"", fileDesc, favicon, , , 7) 78 | fn_handle() 79 | } else { 80 | fn_err_msg("添加任务计划程序失败!`n请检查系统中是否存在 powershell.exe 或 pwsh.exe") 81 | } 82 | } 83 | g.AddButton("xs w" bw btnOpt, "注册表" pad).OnEvent("Click", e_useReg) 84 | e_useReg(*) { 85 | isStartUp := 3 86 | try { 87 | v := A_IsCompiled ? A_ScriptFullPath : '"' A_AhkPath '" "' A_ScriptFullPath '"' 88 | RegWrite(v, "REG_SZ", HKEY_startup, regName) 89 | fn_handle() 90 | } catch { 91 | fn_err_msg("添加注册表失败!") 92 | } 93 | } 94 | btn := g.AddButton("xs w" bw, "应用快捷方式") 95 | btn.OnEvent("Click", e_useLnk) 96 | e_useLnk(*) { 97 | isStartUp := 2 98 | if (A_IsCompiled) { 99 | FileCreateShortcut(A_ScriptFullPath, A_Startup "\" fileLnk, , , fileDesc, favicon, , , 7) 100 | } else { 101 | FileCreateShortcut(A_AhkPath, A_Startup "\" fileLnk, , '"' A_ScriptFullPath '"', fileDesc, favicon, , , 7) 102 | } 103 | fn_handle() 104 | } 105 | 106 | g.AddText("cGray", tip) 107 | 108 | fn_handle(*) { 109 | g.Destroy() 110 | A_TrayMenu.Check(item) 111 | writeIni("isStartUp", isStartUp) 112 | } 113 | fn_err_msg(msg, *) { 114 | createTipGui([{ 115 | opt: "cRed", 116 | text: msg, 117 | }, { 118 | opt: "cRed", 119 | text: "你需要考虑使用其他方式设置开机自启动" 120 | }], "InputTip - 错误").Show() 121 | } 122 | tab.UseTab(2) 123 | g.AddEdit("ReadOnly r10 w" bw, "1. 简要说明`n - 这个菜单用来设置 InputTip 的开机自启动`n - 你可以从以下三种方式中,选择有效的方式`n - 需要注意: 如果移动了软件所在位置,需要重新设置才有效`n`n2. 按钮 —— 任务计划程序`n - 点击它,会创建一个任务计划程序 abgox.InputTip.noUAC`n - 系统启动后,会通过它自动运行 InputTip`n - 它可以直接避免每次启动都弹出管理员授权(UAC)窗口`n - 当系统更新后,可能会失效,需要重新设置`n`n3. 按钮 —— 注册表`n - 点击它,会将程序路径写入开机自启动的注册表`n - 系统启动后,会通过它自动运行 InputTip`n`n4. 按钮 —— 应用快捷方式`n - 点击它,会在 shell:startup 目录下创建一个普通的快捷方式`n - 系统启动后,会通过它自动运行 InputTip`n - 注意: 由于权限或系统电源计划限制等因素,它可能无效`n`n5. 关于管理员授权(UAC)窗口`n - 只有【任务计划程序】能直接避免此窗口弹出`n - 使用【注册表】或【应用快捷方式】需要修改系统设置`n - 系统设置 =>【更改用户账户控制设置】=>【从不通知】") 124 | g.AddLink(, '相关链接: 开机自启动 用户账户控制设置(微软帮助)') 125 | return g 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /scripts/create-winget-manifest.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $outPath = "winget-manifest" 3 | ) 4 | 5 | $template_yaml = '# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json 6 | 7 | PackageIdentifier: abgox.InputTip 8 | PackageVersion: {{ version }} 9 | DefaultLocale: zh-CN 10 | ManifestType: version 11 | ManifestVersion: 1.9.0 12 | ' 13 | 14 | $template_installer_yaml = '# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json 15 | 16 | PackageIdentifier: abgox.InputTip 17 | PackageVersion: {{ version }} 18 | UpgradeBehavior: uninstallPrevious 19 | ReleaseDate: {{ release_date }} 20 | InstallerType: portable 21 | Installers: 22 | # From GitHub repository 23 | - Architecture: x64 24 | InstallerUrl: https://github.com/abgox/InputTip/releases/download/v{{ version }}/InputTip.exe 25 | InstallerSha256: {{ sha256 }} 26 | # From Gitee repository 27 | - Architecture: x64 28 | InstallerUrl: https://gitee.com/abgox/InputTip/releases/download/v{{ version }}/InputTip.exe 29 | InstallerSha256: {{ sha256 }} 30 | InstallerLocale: zh-CN 31 | ManifestType: installer 32 | ManifestVersion: 1.9.0 33 | ' 34 | 35 | $template_locale_en_yaml = '# yaml-language-server: $schema=https://aka.ms/winget-manifest.locale.1.9.0.schema.json 36 | 37 | PackageIdentifier: abgox.InputTip 38 | PackageVersion: {{ version }} 39 | PackageLocale: en-US 40 | Publisher: abgox 41 | PublisherUrl: https://github.com/abgox 42 | PublisherSupportUrl: https://github.com/abgox/InputTip/issues 43 | Author: abgox 44 | PackageName: InputTip 45 | PackageUrl: https://inputtip.abgox.com/ 46 | License: AGPL-3.0-only 47 | LicenseUrl: https://github.com/abgox/InputTip/blob/main/LICENSE 48 | Copyright: Copyright (c) 2023-present abgox 49 | CopyrightUrl: https://github.com/abgox/InputTip/blob/main/LICENSE 50 | ShortDescription: "An input method state management tool: real-time tip(mouse scheme/symbol scheme) + state switching (window trigger/hotkey trigger)" 51 | Tags: 52 | - autohotkey 53 | - cursor 54 | - mouse 55 | - mouse pointer 56 | - ime 57 | - inputmethod 58 | - input 59 | - tip 60 | - state 61 | - switch 62 | - hotkey 63 | ReleaseNotesUrl: https://github.com/abgox/InputTip/releases/tag/v{{ version }} 64 | Documentations: 65 | - DocumentLabel: README (GitHub) 66 | DocumentUrl: https://github.com/abgox/InputTip/blob/main/README.md 67 | - DocumentLabel: README (Gitee) 68 | DocumentUrl: https://gitee.com/abgox/InputTip/blob/main/README.md 69 | - DocumentLabel: Some Frequently Asked Questions & Tips (FAQ) 70 | DocumentUrl: https://inputtip.abgox.com/faq/ 71 | - DocumentLabel: README 72 | DocumentUrl: https://inputtip.abgox.com/v2/ 73 | ManifestType: locale 74 | ManifestVersion: 1.9.0 75 | ' 76 | 77 | 78 | $template_locale_zh_yaml = '# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json 79 | 80 | PackageIdentifier: abgox.InputTip 81 | PackageVersion: {{ version }} 82 | PackageLocale: zh-CN 83 | Publisher: abgox 84 | PublisherUrl: https://gitee.com/abgox 85 | PublisherSupportUrl: https://pd.qq.com/s/gyers18g6?businessType=5 86 | Author: abgox 87 | PackageName: InputTip 88 | PackageUrl: https://inputtip.abgox.com/ 89 | License: AGPL-3.0-only 90 | LicenseUrl: https://gitee.com/abgox/InputTip/blob/main/LICENSE 91 | Copyright: Copyright (c) 2023-present abgox 92 | CopyrightUrl: https://gitee.com/abgox/InputTip/blob/main/LICENSE 93 | ShortDescription: "一个输入法状态管理工具: 实时提示(鼠标方案/符号方案) + 状态切换(窗口触发/热键触发)" 94 | Tags: 95 | - autohotkey 96 | - 光标 97 | - 鼠标 98 | - 鼠标指针 99 | - 输入 100 | - 输入法 101 | - 状态 102 | - 提示 103 | - 状态切换 104 | - 快捷键 105 | - 实时 106 | - 工具 107 | ReleaseNotesUrl: https://gitee.com/abgox/InputTip/releases/tag/v{{ version }} 108 | Documentations: 109 | - DocumentLabel: README (GitHub) 110 | DocumentUrl: https://github.com/abgox/InputTip/blob/main/README.md 111 | - DocumentLabel: README (Gitee) 112 | DocumentUrl: https://gitee.com/abgox/InputTip/blob/main/README.md 113 | - DocumentLabel: 一些常见问题及使用技巧 (FAQ) 114 | DocumentUrl: https://inputtip.abgox.com/faq/ 115 | - DocumentLabel: README 116 | DocumentUrl: https://inputtip.abgox.com/v2/ 117 | ManifestType: defaultLocale 118 | ManifestVersion: 1.9.0 119 | ' 120 | 121 | $version = (Invoke-RestMethod -uri "https://raw.githubusercontent.com/abgox/InputTip/refs/heads/main/src/version.txt").Trim() 122 | 123 | function replaceContent($content, [System.Collections.Generic.List[array]]$replaceList) { 124 | foreach ($replace in $replaceList) { 125 | $content = $content -replace $replace[0], $replace[1] 126 | } 127 | return $content 128 | } 129 | 130 | Invoke-WebRequest -uri "https://github.com/abgox/InputTip/releases/download/v$version/InputTip.exe" -OutFile "abgox.InputTip.exe" 131 | 132 | $sha256 = Get-FileHash "abgox.InputTip.exe" 133 | 134 | Remove-Item "abgox.InputTip.exe" -ErrorAction SilentlyContinue 135 | 136 | $replaceMap = @( 137 | @("{{ version }}", $version), 138 | @("{{ release_date }}", (Get-Date).ToUniversalTime().AddHours(8).ToString("yyyy-MM-dd")), 139 | @("{{ sha256 }}", $sha256.Hash) 140 | ) 141 | 142 | $tempate = @( 143 | @{ 144 | fileName = "abgox.InputTip.yaml" 145 | content = replaceContent $template_yaml $replaceMap 146 | }, 147 | @{ 148 | fileName = "abgox.InputTip.installer.yaml" 149 | content = replaceContent $template_installer_yaml $replaceMap 150 | }, 151 | @{ 152 | fileName = "abgox.InputTip.locale.en-US.yaml" 153 | content = replaceContent $template_locale_en_yaml $replaceMap 154 | }, 155 | @{ 156 | fileName = "abgox.InputTip.locale.zh-CN.yaml" 157 | content = replaceContent $template_locale_zh_yaml $replaceMap 158 | } 159 | ) 160 | 161 | $out = Join-Path $outPath $version 162 | New-Item -ItemType Directory -Force -Path $out | Out-Null 163 | foreach ($item in $tempate) { 164 | $filePath = Join-Path $out $item.fileName 165 | $item.content | Out-File $filePath -Force -Encoding utf8 166 | } 167 | 168 | @{ 169 | new_branch = "abgox.InputTip-$version" 170 | commit_msg = "New version: abgox.InputTip version $version" 171 | pr_title = "New version: abgox.InputTip version $version" 172 | pr_body = "Pull Request created by the publish action of [InputTip](https://github.com/abgox/InputTip)." 173 | } 174 | 175 | # $envList | ConvertTo-Json | Out-File "$PSScriptRoot\..\out-winget-env.json" 176 | -------------------------------------------------------------------------------- /src/utils/hotkey-gui.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | /** 4 | * 设置快捷键 5 | * @param keyConfigList 配置项 6 | * @param label 窗口标题 7 | * @example 8 | * setHotKeyGui([{ 9 | * config: "hotkey_Pause", ; 要写入的配置项 10 | * tip: "暂停/运行" ; 快捷键功能描述 11 | * }], "软件暂停/运行") 12 | */ 13 | setHotKeyGui(keyConfigList, label := "") { 14 | createUniqueGui(hotKeyGui).Show() 15 | hotKeyGui(info) { 16 | g := createGuiOpt("InputTip - 设置快捷键" (label ? " —— " label : "")) 17 | tab := g.AddTab3("-Wrap", ["设置单键", "设置组合快捷键", "手动输入快捷键"]) 18 | tab.UseTab(1) 19 | g.AddText("Section", "1.") 20 | g.AddText("yp cRed", "快捷键设置不会实时生效,需要点击下方的【确定】后生效") 21 | g.AddText("xs", "2. LShift 指的是左边的 Shift 键,RShift 指的是右边的 Shift 键,其他按键以此类推") 22 | g.AddText("xs", "3. 使用单键作为快捷键不会覆盖原本的按键功能,因为是在按键抬起时才会触发") 23 | 24 | if (info.i) { 25 | return g 26 | } 27 | w := info.w 28 | bw := w - g.MarginX * 2 29 | 30 | line := gui_width_line "--" 31 | 32 | g.AddLink("xs", '4. 如果要移除快捷键,请选择【无】。点击查看完整的按键名称对应表`n' line) 33 | 34 | keyList := [] 35 | keyList.Push(["无", "Esc", "Shift", "LShift", "RShift", "Ctrl", "LCtrl", "RCtrl", "Alt", "LAlt", "RAlt"]*) 36 | keyList.Push(["MButton", "LButton", "RButton"]*) 37 | keyList.Push(["Space", "Tab", "Enter", "Backspace", "Delete", "Insert", "Home", "End", "PgUp", "PgDn", "Up", "Down", "Left", "Right"]*) 38 | 39 | i := 0 40 | while (i <= 9) { 41 | keyList.Push("Numpad" i) 42 | i++ 43 | } 44 | keyList.Push(["NumpadDot", "NumLock", "NumpadDiv", "NumpadMult", "NumpadAdd", "NumpadSub", "NumpadEnter"]*) 45 | 46 | i := 1 47 | while (i <= 24) { 48 | keyList.Push("F" i) 49 | i++ 50 | } 51 | 52 | for v in keyConfigList { 53 | g.AddText("xs", v.tip ":") 54 | 55 | _ := gc.%v.config% := g.AddDropDownList("yp r9", keyList) 56 | _._config := v.config 57 | _._with := v.config "_win" 58 | _.OnEvent("Change", e_change_hotkey) 59 | 60 | config := readIni(v.config, "") 61 | if (config ~= "^~\w+\sUp$") { 62 | try { 63 | _.Text := Trim(StrReplace(StrReplace(config, "~", ""), "Up", "")) 64 | if (!_.Value) { 65 | _.Value := 1 66 | } 67 | } catch { 68 | _.Text := "无" 69 | } 70 | } else { 71 | _.Text := "无" 72 | } 73 | } 74 | e_change_hotkey(item, *) { 75 | ; 同步修改到【设置组合快捷键】和【手动输入快捷键】 76 | if (item.Text = "无") { 77 | key := "" 78 | } else { 79 | key := "~" item.Text " Up" 80 | } 81 | gc.%item._config "2"%.Value := "" 82 | gc.%item._config "3"%.Value := key 83 | gc.%item._with%.Value := 0 84 | } 85 | tab.UseTab(2) 86 | g.AddText("Section", "1.") 87 | g.AddText("yp cRed", "快捷键设置不会实时生效,需要点击下方的【确定】后生效") 88 | g.AddText("xs", "2. 直接按下快捷键即可设置,除非快捷键被占用,需要使用【手动输入快捷键】") 89 | g.AddText("xs", "3. 使用 Backspace(退格键) 或 Delete(删除键) 可以清除快捷键") 90 | g.AddText("xs", "4. 通过勾选右边的 Win 键来表示快捷键中需要加入 Win 修饰键`n" line) 91 | 92 | for v in keyConfigList { 93 | g.AddText("xs", v.tip ":") 94 | value := readIni(v.config, '') 95 | _ := gc.%v.config "2"% := g.AddHotkey("yp", StrReplace(value, "#", "")) 96 | _._config := v.config 97 | _._with := v.config "_win" 98 | _.OnEvent("Change", e_change_hotkey1) 99 | gc.%_._with% := g.AddCheckbox("yp", "Win 键") 100 | gc.%_._with%._config := v.config 101 | gc.%_._with%.OnEvent("Click", e_win_key) 102 | gc.%_._with%.Value := InStr(value, "#") ? 1 : 0 103 | } 104 | e_change_hotkey1(item, *) { 105 | ; 同步修改到【设置单键】和【手动输入快捷键】 106 | gc.%item._config%.Text := "无" 107 | v := item.value 108 | if (gc.%item._with%.Value) { 109 | v := "#" v 110 | } 111 | gc.%item._config "3"%.Value := v 112 | } 113 | e_win_key(item, *) { 114 | ; 同步修改到【设置单键】和【手动输入快捷键】 115 | gc.%item._config%.Text := "无" 116 | v := gc.%item._config "2"%.Value 117 | gc.%item._config "3"%.Value := item.value ? "#" v : v 118 | } 119 | tab.UseTab(3) 120 | g.AddText("Section", "1.") 121 | g.AddText("yp cRed", "快捷键设置不会实时生效,需要点击下方的【确定】后生效") 122 | g.AddText("xs", "2.") 123 | g.AddText("yp cRed", "优先使用【设置单键】或【设置组合快捷键】设置,除非因为快捷键占用无法设置") 124 | g.AddText("xs", '3. 这里会回显它们的设置,建议先使用它们,然后回到此处适当修改') 125 | g.AddLink("xs", '4. 你需要首先查看 如何手动输入快捷键`n' line) 126 | for v in keyConfigList { 127 | g.AddText("xs", v.tip ":") 128 | _ := gc.%v.config "3"% := g.AddEdit("yp") 129 | _._config := v.config 130 | _._with := v.config "_win" 131 | _.Value := readIni(v.config, '') 132 | _.OnEvent("Change", e_change_hotkey2) 133 | } 134 | e_change_hotkey2(item, *) { 135 | gc.%item._with%.Value := InStr(item.value, "#") ? 1 : 0 136 | ; 当输入的快捷键符合单键时,同步修改 137 | if (item.value ~= "^~\w+\sUp$") { 138 | try { 139 | gc.%item._config%.Text := Trim(StrReplace(StrReplace(item.value, "~", ""), "Up", "")) 140 | } catch { 141 | gc.%item._config%.Text := "无" 142 | } 143 | gc.%item._config "2"%.Value := "" 144 | } else { 145 | gc.%item._config%.Text := "无" 146 | ; 当输入的快捷键符合组合快捷键时,同步修改 147 | try { 148 | gc.%item._config "2"%.Value := StrReplace(item.value, "#", "") 149 | } catch { 150 | gc.%item._config "2"%.Value := "" 151 | } 152 | } 153 | } 154 | tab.UseTab(0) 155 | g.AddButton("Section w" bw, "确定").OnEvent("Click", e_yes) 156 | e_yes(*) { 157 | for v in keyConfigList { 158 | key := gc.%v.config "3"%.Value 159 | writeIni(v.config, key) 160 | } 161 | fn_restart() 162 | } 163 | g.OnEvent("Close", e_close) 164 | e_close(*) { 165 | g.Destroy() 166 | } 167 | return g 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/utils/show.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | updateDelay() 4 | 5 | ; 鼠标悬停在符号上 6 | isOverSymbol := 0 7 | hasWindowChange := 1 8 | 9 | lastInputState := "" 10 | inputStateChanged := 0 11 | 12 | loop { 13 | Sleep(delay) 14 | ; 正在使用鼠标或有键盘操作 15 | if (A_TimeIdle < leaveDelay) { 16 | needShow := symbolType 17 | 18 | try { 19 | exe_name := ProcessGetName(WinGetPID("A")) 20 | exe_title := WinGetTitle("A") 21 | exe_str := ":" exe_name ":" 22 | 23 | if (symbolType && needSkip(exe_str)) { 24 | hideSymbol() 25 | lastSymbol := "" 26 | lastCursor := "" 27 | lastWindow := "" 28 | continue 29 | } 30 | 31 | ; 进程有变化(包含标题变化) 32 | hasWindowChange := lastWindow != exe_name ":" exe_title 33 | 34 | if (hasWindowChange) { 35 | if (validateMatch(exe_name, exe_title, app_AutoExit)) { 36 | fn_exit() 37 | } 38 | if (symbolType) { 39 | if (!showCursorPos && !WinActive("ahk_class AutoHotkeyGUI") && (validateMatch(exe_name, exe_title, app_HideSymbol) || !validateMatch(exe_name, exe_title, app_ShowSymbol))) { 40 | hideSymbol() 41 | needShow := 0 42 | } 43 | } 44 | 45 | ; 等待窗口完成聚焦,防止切换状态冲突,导致状态切换失败 46 | WinWaitActive(exe_title " ahk_exe " exe_name, , 5) 47 | 48 | lastSymbol := "" 49 | lastCursor := "" 50 | 51 | toState := switchState(exe_name, exe_title) 52 | if (toState == "CN") { 53 | switch_CN() 54 | } else if (toState == "EN") { 55 | switch_EN() 56 | } else if (toState == "Caps") { 57 | switch_Caps() 58 | } 59 | } 60 | } catch { 61 | hideSymbol() 62 | needShow := 0 63 | } 64 | 65 | if (symbolType && hoverHide) { 66 | if (isMouseOver("abgox-InputTip-Symbol-Window")) { 67 | hideSymbol() 68 | isOverSymbol := 1 69 | continue 70 | } 71 | } 72 | 73 | if (GetKeyState("CapsLock", "T")) { 74 | currentState := "Caps" 75 | } else { 76 | try { 77 | currentState := isCN() ? "CN" : "EN" 78 | } catch { 79 | hideSymbol() 80 | continue 81 | } 82 | } 83 | 84 | loadCursor(currentState) 85 | 86 | if (symbolShowMode == 1) { 87 | ShowSymbolEx(currentState) 88 | } else { 89 | inputStateChanged := currentState != lastInputState 90 | lastInputState := currentState 91 | if (inputStateChanged || GetKeyState("LButton", "P")) { 92 | ShowSymbolEx(currentState) 93 | } 94 | } 95 | } 96 | } 97 | 98 | /** 99 | * 匹配指定的状态,并返回是否需要切换 100 | * @param {String} exe_name 程序名 101 | * @param {String} exe_title 程序标题 102 | * @returns {"CN" | "EN" | "Caps" | 0} 需要切换到的状态或0 103 | */ 104 | switchState(exe_name, exe_title) { 105 | all := false 106 | for value in ["CN", "EN", "Caps"] { 107 | app_state := %"app_" value% 108 | for v in app_state { 109 | kv := StrSplit(v, "=", , 2) 110 | part := StrSplit(kv[2], ":", , 4) 111 | if (part.Length >= 2) { 112 | ; 进程名称 113 | name := part[1] 114 | ; 是否全局的,全局的只有当切换到此窗口时才会触发 115 | isGlobal := part[2] 116 | 117 | if (isGlobal) { 118 | if (exe_name == name && !InStr(lastWindow, name ":")) { 119 | all := value 120 | } 121 | continue 122 | } 123 | } 124 | 125 | if (part.Length < 4) { 126 | continue 127 | } 128 | 129 | ; 是否正则匹配 130 | isRegex := part[3] 131 | ; 标题 132 | title := part[4] 133 | if (name == exe_name) { 134 | isMatch := isRegex ? RegExMatch(exe_title, title) : exe_title == title 135 | if (isMatch) { 136 | return value 137 | } 138 | } 139 | } 140 | } 141 | if (all) { 142 | return all 143 | } 144 | return 0 145 | } 146 | 147 | /** 148 | * 是否需要显示在鼠标附近 149 | * @param {String} exe_name 程序名 150 | * @param {String} exe_title 程序标题 151 | * @returns {1 | 0} 152 | */ 153 | showBesideCursor(exe_name, exe_title) { 154 | if (showCursorPos) { 155 | return 1 156 | } 157 | return validateMatch(exe_name, exe_title, ShowNearCursor) 158 | } 159 | 160 | /** 161 | * 验证匹配是否成功(通用) 162 | * @param {String} exe_name 程序名 163 | * @param {String} exe_title 程序标题 164 | * @returns {1 | 0} 165 | */ 166 | validateMatch(exe_name, exe_title, configValue) { 167 | for v in configValue { 168 | kv := StrSplit(v, "=", , 2) 169 | part := StrSplit(kv[2], ":", , 4) 170 | if (part.Length >= 2) { 171 | ; 进程名称 172 | name := part[1] 173 | if (exe_name == name) { 174 | ; 如果是进程级 175 | if (part[2]) { 176 | return 1 177 | } 178 | } else { 179 | continue 180 | } 181 | } 182 | 183 | if (part.Length < 4) { 184 | continue 185 | } 186 | 187 | if (name == exe_name) { 188 | ; 是否正则匹配 189 | isRegex := part[3] 190 | ; 标题 191 | title := part[4] 192 | 193 | isMatch := isRegex ? RegExMatch(exe_title, title) : exe_title == title 194 | if (isMatch) { 195 | return 1 196 | } 197 | } 198 | } 199 | return 0 200 | } 201 | 202 | ShowSymbolEx(state) { 203 | static lastNeedShow := 0 204 | global canShowSymbol, lastWindow 205 | 206 | if (needHide) { 207 | hideSymbol() 208 | return 209 | } 210 | 211 | if (hasWindowChange) { 212 | lastWindow := exe_name ":" exe_title 213 | lastNeedShow := needShow 214 | } 215 | 216 | if (lastNeedShow) { 217 | select := showBesideCursor(exe_name, exe_title) 218 | 219 | if (select) { 220 | try { 221 | MouseGetPos(&left, &top) 222 | canShowSymbol := 1 223 | loadSymbol(state, left, top, left, top, 1) 224 | } catch { 225 | hideSymbol() 226 | } 227 | return 228 | } 229 | try { 230 | canShowSymbol := returnCanShowSymbol(&left, &top, &right, &bottom) 231 | 232 | WinGetPos(&x, &y, &w, &h, "A") 233 | if (top < y || top > y + h) { 234 | hideSymbol() 235 | return 236 | } 237 | loadSymbol(state, left, top, right, bottom) 238 | } catch { 239 | hideSymbol() 240 | } 241 | } 242 | } 243 | 244 | updateDelay() { 245 | if (hideSymbolDelay) { 246 | SetTimer(updateDelayTimer, 25) 247 | updateDelayTimer() { 248 | static timer := 0 249 | global needHide, isWait 250 | if (GetKeyState("LButton", "P")) { 251 | needHide := 0 252 | isWait := 1 253 | SetTimer(_timer, -hideSymbolDelay) 254 | _timer() { 255 | isWait := 0 256 | } 257 | } 258 | if (A_TimeIdleKeyboard <= leaveDelay) { 259 | timer := 0 260 | } 261 | if (!isWait) { 262 | if (A_TimeIdleKeyboard >= hideSymbolDelay - delay || timer >= hideSymbolDelay) { 263 | needHide := 1 264 | hideSymbol() 265 | timer := 0 266 | } else { 267 | needHide := 0 268 | } 269 | } 270 | if (hideSymbolDelay = 0) { 271 | SetTimer(updateDelayTimer, 0) 272 | needHide := 0 273 | isWait := 0 274 | } 275 | timer += 25 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/utils/IME.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | /** 4 | * @link https://github.com/Tebayaki/AutoHotkeyScripts/blob/main/lib/IME.ahk 5 | * @Tip 有所修改,外部必须提供变量 checkTimeout,modeRule,baseStatus 6 | * @example 7 | * checkTimeout := 1000 ; 超时时间(单位:毫秒) 8 | * modeRules := [] ; 状态规则 9 | * baseStatus := 0 ; 默认状态为英文 10 | * IME.GetInputMode() ; 获取当前输入法输入模式 11 | * IME.SetInputMode(!IME.GetInputMode()) ; 切换当前输入法输入模式 12 | */ 13 | class IME { 14 | /** 15 | * 获取当前输入法输入模式 16 | * @param hwnd 17 | * @returns {1 | 0} 1:中文,0:英文 18 | */ 19 | static GetInputMode(hwnd := this.GetFocusedWindow()) { 20 | if (mode = 1) { 21 | if (!this.GetOpenStatus(hwnd)) { 22 | return 0 23 | } 24 | return this.GetConversionMode(hwnd) & 1 25 | } 26 | 27 | ; 存储默认状态,如果都不匹配,就返回预先指定的默认状态 28 | status := baseStatus 29 | 30 | ; 系统返回的状态码 31 | statusMode := this.GetOpenStatus(hwnd) 32 | ; 系统返回的切换码 33 | conversionMode := this.GetConversionMode(hwnd) 34 | 35 | for v in modeRules { 36 | r := StrSplit(v, "*") 37 | 38 | ; 状态码规则 39 | sm := r[1] 40 | ; 切换码规则 41 | cm := r[2] 42 | ; 匹配状态 43 | s := r[3] 44 | 45 | if (matchRule(statusMode, sm) && matchRule(conversionMode, cm)) { 46 | ; 匹配成功 47 | status := s 48 | break 49 | } 50 | } 51 | 52 | /** 53 | * 匹配规则 54 | * @param value 系统返回的状态值 55 | * @param ruleValue 规则定义的状态值 56 | * @returns {1 | 0} 是否匹配成功 57 | */ 58 | matchRule(value, ruleValue) { 59 | ; 规则为空,默认匹配成功 60 | if (ruleValue == "") { 61 | return 1 62 | } 63 | 64 | if (ruleValue == "evenNum") { ; 如果值是偶数 65 | isMatch := !(value & 1) 66 | } else if (ruleValue == "oddNum") { ; 如果值是奇数 67 | isMatch := value & 1 68 | } else { 69 | isMatch := InStr("/" ruleValue "/", "/" value "/") 70 | } 71 | return isMatch 72 | } 73 | return status 74 | } 75 | 76 | /** 77 | * 系统返回的状态码和切换码 78 | * @param hwnd 79 | * @returns {Object} 系统返回的状态码和切换码 80 | */ 81 | static CheckInputMode(hwnd := this.GetFocusedWindow()) { 82 | return { 83 | statusMode: this.GetOpenStatus(hwnd), 84 | conversionMode: this.GetConversionMode(hwnd) 85 | } 86 | } 87 | 88 | /** 89 | * 切换到指定的输入法状态 90 | * @param mode 要切换的指定输入法状态(1:中文,0:英文) 91 | * @param hwnd 92 | */ 93 | static SetInputMode(mode, hwnd := this.GetFocusedWindow()) { 94 | if mode { 95 | this.SetOpenStatus(true, hwnd) 96 | switch this.GetKeyboardLayout(hwnd) { 97 | case 0x08040804: 98 | this.SetConversionMode(1025, hwnd) 99 | case 0x04110411: 100 | this.SetConversionMode(9, hwnd) 101 | } 102 | } 103 | else { 104 | this.SetOpenStatus(false, hwnd) 105 | } 106 | } 107 | 108 | static ToggleInputMode(hwnd := this.GetFocusedWindow()) { 109 | this.SetInputMode(!this.GetInputMode(hwnd), hwnd) 110 | } 111 | 112 | static GetOpenStatus(hwnd := this.GetFocusedWindow()) { 113 | try { 114 | DllCall("SendMessageTimeoutW", "ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "ptr", hwnd, "ptr"), "uint", 0x283, "ptr", 0x5, "ptr", 0, "uint", 0, "uint", checkTimeout, "ptr*", &status := 0) 115 | return status 116 | } catch { 117 | return 0 118 | } 119 | } 120 | 121 | static SetOpenStatus(status, hwnd := this.GetFocusedWindow()) { 122 | try { 123 | DllCall("SendMessageTimeoutW", "ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "ptr", hwnd, "ptr"), "uint", 0x283, "ptr", 0x6, "ptr", status, "uint", 0, "uint", checkTimeout, "ptr*", 0) 124 | } 125 | } 126 | 127 | static GetConversionMode(hwnd := this.GetFocusedWindow()) { 128 | try { 129 | DllCall("SendMessageTimeoutW", "ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "ptr", hwnd, "ptr"), "uint", 0x283, "ptr", 0x1, "ptr", 0, "uint", 0, "uint", checkTimeout, "ptr*", &mode := 0) 130 | return mode 131 | } catch { 132 | return 0 133 | } 134 | } 135 | 136 | static SetConversionMode(mode, hwnd := this.GetFocusedWindow()) { 137 | try { 138 | DllCall("SendMessageTimeoutW", "ptr", DllCall("imm32\ImmGetDefaultIMEWnd", "ptr", hwnd, "ptr"), "uint", 0x283, "ptr", 0x2, "ptr", mode, "uint", 0, "uint", checkTimeout, "ptr*", 0) 139 | } 140 | } 141 | 142 | static GetKeyboardLayout(hwnd := this.GetFocusedWindow()) { 143 | return DllCall("GetKeyboardLayout", "uint", DllCall("GetWindowThreadProcessId", "ptr", hwnd, "ptr", 0, "uint"), "ptr") 144 | } 145 | 146 | static SetKeyboardLayout(hkl, hwnd := this.GetFocusedWindow()) { 147 | SendMessage(0x50, 1, hkl, hwnd) 148 | } 149 | 150 | static GetKeyboardLayoutList() { 151 | if cnt := DllCall("GetKeyboardLayoutList", "int", 0, "ptr", 0) { 152 | list := [] 153 | buf := Buffer(cnt * A_PtrSize) 154 | loop DllCall("GetKeyboardLayoutList", "int", cnt, "ptr", buf) { 155 | list.Push(NumGet(buf, (A_Index - 1) * A_PtrSize, "ptr")) 156 | } 157 | return list 158 | } 159 | } 160 | 161 | static LoadKeyboardLayout(hkl) { 162 | return DllCall("LoadKeyboardLayoutW", "str", Format("{:08x}", hkl), "uint", 0x101) 163 | } 164 | 165 | static UnloadKeyboardLayout(hkl) { 166 | return DllCall("UnloadKeyboardLayout", "ptr", hkl) 167 | } 168 | 169 | static GetFocusedWindow() { 170 | if foreHwnd := WinExist("A") { 171 | guiThreadInfo := Buffer(A_PtrSize == 8 ? 72 : 48) 172 | NumPut("uint", guiThreadInfo.Size, guiThreadInfo) 173 | DllCall("GetGUIThreadInfo", "uint", DllCall("GetWindowThreadProcessId", "ptr", foreHwnd, "ptr", 0, "uint"), "ptr", guiThreadInfo) 174 | if focusedHwnd := NumGet(guiThreadInfo, A_PtrSize == 8 ? 16 : 12, "ptr") { 175 | return focusedHwnd 176 | } 177 | return foreHwnd 178 | } 179 | return 0 180 | } 181 | } 182 | 183 | /** 184 | * 判断当前输入法状态是否为中文 185 | * @returns {1 | 0} 输入法是否为中文 186 | * @example 187 | * DetectHiddenWindows 1 ; 前置条件(不为1,可能判断有误) 188 | * ;... 189 | * MsgBox isCN() 190 | */ 191 | isCN() { 192 | return IME.GetInputMode() 193 | } 194 | 195 | /** 196 | * 将输入法状态切换为中文 197 | * @param pressKey 触发此函数的按键,如果非按键触发,则为空 198 | * @example 199 | * SetStoreCapsLockMode 0 ; 前置条件,确保大写锁定可切换 200 | * ; ... 201 | * switch_CN() 202 | */ 203 | switch_CN(pressKey := "", *) { 204 | ; 当按下 shift + 任意键,取消切换 205 | if (pressKey && InStr(hotkey_CN, "shift") && A_TimeIdleKeyboard < 200 && !InStr(A_PriorKey, "shift")) { 206 | return 207 | } 208 | if (GetKeyState("CapsLock", "T")) { 209 | if (keepCapsLock) { 210 | return 211 | } 212 | SendInput("{CapsLock}") 213 | } 214 | Sleep(50) 215 | if (switchStatus) { 216 | if (!isCN()) { 217 | SendInput(switchStatusList[switchStatus]) 218 | } 219 | } else { 220 | IME.SetInputMode(1) 221 | } 222 | } 223 | /** 224 | * 将输入法状态切换为英文 225 | * @param pressKey 触发此函数的按键,如果非按键触发,则为空 226 | * @example 227 | * SetStoreCapsLockMode 0 ; 前置条件,确保大写锁定可切换 228 | * ; ... 229 | * switch_EN() 230 | */ 231 | switch_EN(pressKey := "", *) { 232 | ; 当按下 shift + 任意键,取消切换 233 | if (pressKey && InStr(hotkey_EN, "shift") && A_TimeIdleKeyboard < 200 && !InStr(A_PriorKey, "shift")) { 234 | return 235 | } 236 | if (GetKeyState("CapsLock", "T")) { 237 | if (keepCapsLock) { 238 | return 239 | } 240 | SendInput("{CapsLock}") 241 | } 242 | Sleep(50) 243 | if (switchStatus) { 244 | if (isCN()) { 245 | SendInput(switchStatusList[switchStatus]) 246 | } 247 | } else { 248 | IME.SetInputMode(0) 249 | } 250 | } 251 | /** 252 | * 将输入法状态切换为大写锁定 253 | * @param pressKey 触发此函数的按键,如果非按键触发,则为空 254 | * @example 255 | * SetStoreCapsLockMode 0 ; 前置条件,确保大写锁定可切换 256 | * ; ... 257 | * switch_Caps() 258 | */ 259 | switch_Caps(pressKey := "", *) { 260 | ; 当按下 shift + 任意键,取消切换 261 | if (pressKey && InStr(hotkey_Caps, "shift") && A_TimeIdleKeyboard < 200 && !InStr(A_PriorKey, "shift")) { 262 | return 263 | } 264 | if (!GetKeyState("CapsLock", "T")) { 265 | SendInput("{CapsLock}") 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/menu/cursor-mode.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_cursor_mode(*) { 4 | showGui() 5 | showGui(deep := 0) { 6 | createUniqueGui(modeGui).Show() 7 | modeGui(info) { 8 | g := createGuiOpt("InputTip - 光标获取模式") 9 | tab := g.AddTab3("-Wrap", ["光标获取模式", "关于"]) 10 | tab.UseTab(1) 11 | g.AddText("Section cRed", gui_help_tip) 12 | 13 | if (info.i) { 14 | g.AddText(, gui_width_line) 15 | return g 16 | } 17 | w := info.w 18 | bw := w - g.MarginX * 2 19 | 20 | 21 | LV := "LV_" A_Now 22 | gc.%LV% := g.AddListView("xs -LV0x10 -Multi r9 NoSortHdr Sort Grid w" w, ["进程名称", "光标获取模式", "创建时间"]) 23 | 24 | gc.%LV%.Opt("-Redraw") 25 | valueArr := StrSplit(readIniSection("InputCursorMode"), "`n") 26 | for v in valueArr { 27 | kv := StrSplit(v, "=", , 2) 28 | part := StrSplit(kv[2], ":", , 4) 29 | try { 30 | name := part[1] 31 | mode := part[2] 32 | gc.%LV%.Add(, name, mode, kv[1]) 33 | } 34 | } 35 | gc.%LV%.Opt("+Redraw") 36 | autoHdrLV(gc.%LV%) 37 | 38 | 39 | gc.%LV%.OnEvent("DoubleClick", handleClick) 40 | gc.%LV%._LV := LV 41 | _ := g.AddButton("xs w" w / 2, "快捷添加") 42 | _.OnEvent("Click", e_add) 43 | _._LV := LV 44 | e_add(item, *) { 45 | fn_add(item._LV) 46 | } 47 | 48 | _ := g.AddButton("yp w" w / 2, "手动添加") 49 | _.OnEvent("Click", e_add_manually) 50 | _._LV := LV 51 | e_add_manually(item, *) { 52 | itemValue := { 53 | exe_name: "", 54 | mode: "HOOK", 55 | id: returnId() 56 | } 57 | fn_edit(gc.%item._LV%, 1, "add", itemValue).Show() 58 | } 59 | 60 | handleClick(LV, RowNumber) { 61 | if (!RowNumber) { 62 | return 63 | } 64 | exe_name := LV.GetText(RowNumber) 65 | if (gc.w.subGui) { 66 | gc.w.subGui.Destroy() 67 | gc.w.subGui := "" 68 | } 69 | 70 | itemValue := { 71 | exe_name: LV.GetText(RowNumber, 1), 72 | mode: LV.GetText(RowNumber, 2), 73 | id: LV.GetText(RowNumber, 3) 74 | } 75 | createGui(editGui).Show() 76 | editGui(info) { 77 | return fn_edit(gc.%LV._LV%, RowNumber, "edit", itemValue) 78 | } 79 | } 80 | 81 | fn_edit(LV, RowNumber, action, itemValue) { 82 | ; 是否自动添加到符号的白名单中 83 | needAddWhiteList := 1 84 | 85 | if (action == "edit") { 86 | actionText := "编辑" 87 | } else { 88 | actionText := "添加" 89 | } 90 | 91 | label := "正在" actionText "应用的光标获取模式" 92 | 93 | g := createGuiOpt("InputTip - " label) 94 | 95 | if (info.i) { 96 | g.AddText(, gui_width_line) 97 | return g 98 | } 99 | w := info.w 100 | bw := w - g.MarginX * 2 101 | 102 | if (action != "edit") { 103 | g.AddText("cRed", "是否添加到【符号的白名单】中: ") 104 | _ := g.AddDropDownList("yp", ["【否】不添加", "【是】自动添加"]) 105 | _.Value := needAddWhiteList + 1 106 | _.OnEvent("Change", e_change) 107 | e_change(item, *) { 108 | needAddWhiteList := item.value - 1 109 | } 110 | g.AddText("xs cGray", "如果选择【是】,且它在白名单中不存在,将以【进程级】自动添加") 111 | } 112 | 113 | scaleWidth := bw / 1.6 114 | 115 | g.AddText(, "1. 应用进程名称: ") 116 | _ := g.AddEdit("yp w" scaleWidth, "") 117 | _.Text := itemValue.exe_name 118 | _.OnEvent("Change", e_changeName) 119 | e_changeName(item, *) { 120 | v := item.Text 121 | itemValue.exe_name := v 122 | } 123 | 124 | g.AddText("xs", "2. 光标获取模式: ") 125 | _ := g.AddDropDownList("yp w" scaleWidth, modeNameList) 126 | _.Text := itemValue.mode 127 | _.OnEvent("Change", e_changeLevel) 128 | e_changeLevel(item, *) { 129 | v := item.Text 130 | itemValue.mode := v 131 | } 132 | 133 | g.AddButton("xs w" bw / 1.2, "完成" actionText).OnEvent("Click", e_set) 134 | e_set(*) { 135 | fn_set(action, 0) 136 | } 137 | if (action == "edit") { 138 | g.AddButton("xs w" bw / 1.2, "删除它").OnEvent("Click", e_delete) 139 | e_delete(*) { 140 | fn_set(action, 1) 141 | } 142 | } 143 | 144 | fn_set(action, delete) { 145 | g.Destroy() 146 | 147 | if (delete) { 148 | try { 149 | IniDelete("InputTip.ini", "InputCursorMode", itemValue.id) 150 | LV.Delete(RowNumber) 151 | } 152 | } else { 153 | value := itemValue.exe_name ":" itemValue.mode 154 | ; 没有进行移动 155 | writeIni(itemValue.id, value, "InputCursorMode", "InputTip.ini") 156 | if (action == "edit") { 157 | LV.Modify(RowNumber, , itemValue.exe_name, itemValue.mode, itemValue.id) 158 | } else { 159 | LV.Insert(RowNumber, , itemValue.exe_name, itemValue.mode, itemValue.id) 160 | } 161 | 162 | if (needAddWhiteList) { 163 | updateWhiteList(itemValue.exe_name) 164 | } 165 | } 166 | 167 | autoHdrLV(LV) 168 | 169 | updateCursorMode() 170 | } 171 | return g 172 | } 173 | 174 | fn_add(parentLV) { 175 | args := { 176 | title: "快捷添加", 177 | configName: "", 178 | LV: parentLV, 179 | } 180 | createProcessListGui(args, addClick, e_add_manually) 181 | 182 | addClick(args) { 183 | windowInfo := args.windowInfo 184 | RowNumber := args.RowNumber 185 | 186 | itemValue := { 187 | exe_name: windowInfo.exe_name, 188 | mode: "HOOK", 189 | id: windowInfo.id 190 | } 191 | fn_edit(gc.%args.parentArgs.LV%, RowNumber, "add", itemValue).Show() 192 | } 193 | 194 | e_add_manually(args) { 195 | windowInfo := args.windowInfo 196 | 197 | itemValue := { 198 | exe_name: windowInfo.exe_name, 199 | mode: "HOOK", 200 | id: windowInfo.id 201 | } 202 | fn_edit(gc.%args.parentArgs.LV%, 1, "add", itemValue).Show() 203 | } 204 | } 205 | tab.UseTab(2) 206 | g.AddEdit("ReadOnly VScroll r13 w" w, "1. 简要说明`n - 这个菜单用来设置【光标获取模式】的匹配规则`n - 下方是对应的规则列表`n - 双击列表中的任意一行,进行编辑或删除`n - 如果需要添加,请查看下方按钮相关的使用说明`n`n2. 规则列表 —— 进程名称`n - 应用窗口实际的进程名称`n`n3. 规则列表 —— 光标获取模式`n - 包括【HOOK】【UIA】【MSAA】【JAB】等等`n - 不用在意这些模式是啥,哪个能用,就用哪个即可`n - 如果想了解详细的相关内容,可以查看下方相关链接`n`n4. 规则列表 —— 创建时间`n - 它是每条规则的创建时间`n`n5. 按钮 —— 添加应用的光标获取模式`n - 点击它,可以添加一条新的规则`n - 这条规则用来指定某个应用进程使用什么光标获取模式`n - 它会弹出一个新的菜单页面,会显示当前正在运行的【应用进程列表】`n - 你可以双击【应用进程列表】中的任意一行进行快速添加`n - 详细的使用说明请参考弹出的菜单页面中的【关于】`n`n6. 光标获取模式 —— JAB`n - 在所有的光标获取模式中,【JAB】是特殊的`n - 它仅用于需要使用 Java Access Bridge 的进程,比如 JetBrains IDE`n - 它需要配合【其他设置】中的【JAB/JetBrains IDE 支持】一起使用`n - 详情参考【JAB/JetBrains IDE 支持】的使用说明") 207 | g.AddLink(, '相关链接: 光标获取模式 如何在 JetBrains 系列 IDE 中使用 InputTip') 208 | g.OnEvent("Close", fn_close) 209 | fn_close(*) { 210 | g.Destroy() 211 | try { 212 | gc.w.subGui.Destroy() 213 | gc.w.subGui := "" 214 | } 215 | } 216 | return g 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/menu/other-config.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_ohter_config(*) { 4 | createUniqueGui(ohterConfigGui).Show() 5 | ohterConfigGui(info) { 6 | g := createGuiOpt("InputTip - 其他设置") 7 | tab := g.AddTab3("-Wrap", ["其他设置", "其他设置2", "关于"]) 8 | tab.UseTab(1) 9 | g.AddText("Section cRed", gui_help_tip) 10 | 11 | if (info.i) { 12 | g.AddText(, gui_width_line) 13 | return g 14 | } 15 | w := info.w 16 | bw := w - g.MarginX * 2 17 | line := gui_width_line "-------" 18 | g.AddText("xs", line) 19 | 20 | g.AddButton("xs w" bw / 2, "更新检查").OnEvent("Click", fn_check_update) 21 | g.AddButton("yp w" bw / 2, "指定窗口自动退出").OnEvent("Click", fn_auto_exit) 22 | g.AddButton("xs w" bw / 2, "打开软件目录").OnEvent("Click", (*) => (Run("explorer.exe /select," A_ScriptFullPath))) 23 | g.AddButton("yp w" bw / 2, "设置用户名").OnEvent("Click", (*) => (fn_update_user(userName))) 24 | g.AddButton("xs w" bw / 2, "暂停/运行快捷键").OnEvent("Click", (*) => ( 25 | setHotKeyGui([{ 26 | config: "hotkey_Pause", 27 | tip: "暂停/运行" 28 | }], "软件暂停/运行") 29 | )) 30 | g.AddButton("yp w" bw / 2, "JAB/JetBrains IDE 支持 【" (enableJABSupport ? "启用" : "禁用") "中】").OnEvent("Click", e_enableJABSupport) 31 | e_enableJABSupport(item, *) { 32 | global enableJABSupport := !enableJABSupport 33 | 34 | if (enableJABSupport) { 35 | if (runJAB()) { 36 | return 37 | } 38 | writeIni("enableJABSupport", enableJABSupport) 39 | item.Text := "JAB/JetBrains IDE 支持【启用中】" 40 | createUniqueGui(JABGui).Show() 41 | JABGui(info) { 42 | g := createGuiOpt("InputTip - 启用 JAB/JetBrains IDE 支持") 43 | g.AddText(, "已经成功启用了 JAB/JetBrains IDE 支持,你还需要进行以下操作步骤: ") 44 | 45 | if (info.i) { 46 | return g 47 | } 48 | w := info.w 49 | bw := w - g.MarginX * 2 50 | 51 | g.AddEdit("xs -VScroll ReadOnly cGray w" w, "1. 启用 Java Access Bridge (如果不需要在输入光标处显示符号,忽略此步骤)`n2. 点击下方的或【托盘菜单】中的【光标获取模式】`n3. 设置 JetBrains IDE 或其他 JAB 应用进程的光标获取模式为【JAB】`n4. 如果未生效,请重启 InputTip`n5. 如果仍未生效,请重启正在运行的 JetBrains IDE 或其他 JAB 应用`n6. 如果仍未生效,请重启系统`n7. 有多块屏幕时,副屏幕上可能有坐标偏差,需要通过【特殊偏移量】调整") 52 | g.AddText("cRed", "部分 JDK/JRE 中的 Java Access Bridge 无效,推荐使用 Microsoft OpenJDK 21").Focus() 53 | g.AddLink(, '详细操作步骤,请查看: 官网 Github Gitee') 54 | g.AddButton("xs w" w, "【光标获取模式】").OnEvent("Click", fn_cursor_mode) 55 | g.AddButton("xs w" w, "【特殊偏移量】").OnEvent("Click", fn_app_offset) 56 | y := g.AddButton("xs w" w, "我知道了") 57 | y.OnEvent("Click", e_close) 58 | e_close(*) { 59 | g.Destroy() 60 | } 61 | return g 62 | } 63 | } else { 64 | item.Text := "JAB/JetBrains IDE 支持【禁用中】" 65 | SetTimer(killAppTimer, -1) 66 | killAppTimer() { 67 | try { 68 | killJAB(1, A_IsCompiled || A_IsAdmin) 69 | writeIni("enableJABSupport", enableJABSupport) 70 | } 71 | } 72 | } 73 | } 74 | 75 | g.AddText("xs", line) 76 | 77 | g.AddText("xs", "自定义软件托盘图标") 78 | iconList := StrSplit(iconPaths, ":") 79 | g.AddText("xs cRed", "运行中: ").GetPos(, , &_w) 80 | _ := g.AddDropDownList("yp r9 w" bw - _w, iconList) 81 | _.Text := iconRunning 82 | _.OnEvent("Change", e_iconRunning) 83 | e_iconRunning(item, *) { 84 | value := item.Text 85 | global iconRunning := value 86 | writeIni("iconRunning", value) 87 | if (!A_IsPaused) { 88 | setTrayIcon(value) 89 | } 90 | } 91 | g.AddText("xs cRed", "暂停中: ") 92 | _ := g.AddDropDownList("yp r9 w" bw - _w, iconList) 93 | _.Text := iconPaused 94 | _.OnEvent("Change", e_iconPaused) 95 | e_iconPaused(item, *) { 96 | value := item.Text 97 | global iconPaused := value 98 | writeIni("iconPaused", value) 99 | if (A_IsPaused) { 100 | setTrayIcon(value) 101 | } 102 | } 103 | g.AddButton("xs w" bw / 2, "打开图标目录").OnEvent("Click", (*) => (Run("explorer.exe InputTipIcon"))) 104 | g.AddButton("yp w" bw / 2, "刷新下拉列表").OnEvent("Click", (*) => ( 105 | getPathList(), 106 | fn_ohter_config() 107 | )) 108 | 109 | tab.UseTab(2) 110 | g.AddText("Section cRed", gui_help_tip) 111 | g.AddText("xs", line) 112 | 113 | g.AddText("Section", "1. 配置菜单字体大小:") 114 | _ := g.AddEdit("yp Number Limit2") 115 | _.Value := readIni("gui_font_size", "12") 116 | _.OnEvent("Change", e_change_gui_fs) 117 | e_change_gui_fs(item, *) { 118 | static db := debounce((config, value) => ( 119 | writeIni(config, value) 120 | )) 121 | 122 | value := item.value 123 | if (value = "" || value < 5 || value > 30) { 124 | return 125 | } 126 | global fontOpt := ["s" value, "Microsoft YaHei"] 127 | 128 | db("gui_font_size", value) 129 | } 130 | 131 | g.AddText("xs", "2. 轮询响应间隔时间:") 132 | _ := g.AddEdit("yp Number Limit2") 133 | _.Value := delay 134 | _.OnEvent("Change", e_delay) 135 | e_delay(item, *) { 136 | static db := debounce((config, value) => ( 137 | writeIni(config, value), 138 | restartJAB() 139 | )) 140 | 141 | value := item.value 142 | if (value = "") { 143 | return 144 | } 145 | value += value <= 0 146 | if (value > 100) { 147 | value := 100 148 | } 149 | global delay := value 150 | 151 | db("delay", value) 152 | } 153 | 154 | g.AddText("Section xs", "3. 按键输入次数统计:") 155 | _ := g.AddDropDownList("yp AltSubmit", ["【否】关闭", "【是】开启"]) 156 | _.Value := enableKeyCount + 1 157 | _.OnEvent("Change", fn_keyCount) 158 | fn_keyCount(item, *) { 159 | value := item.value - 1 160 | global enableKeyCount := value 161 | writeIni("enableKeyCount", value) 162 | updateTip() 163 | } 164 | 165 | g.AddText("Section xs", "4. 鼠标悬停在【托盘图标】上时的文字模板") 166 | _ := g.AddEdit("w" bw) 167 | _.Value := trayTipTemplate 168 | _.OnEvent("Change", e_trayTemplate) 169 | e_trayTemplate(item, *) { 170 | static db := debounce((config, value) => ( 171 | writeIni(config, value) 172 | )) 173 | 174 | value := item.value 175 | global trayTipTemplate := value 176 | updateTip(A_IsPaused) 177 | 178 | db("trayTipTemplate", value) 179 | } 180 | g.AddText("Section xs", "5. 按键输入次数统计的文字模板") 181 | _ := g.AddEdit("w" bw) 182 | _.Value := keyCountTemplate 183 | _.OnEvent("Change", e_countTemplate) 184 | e_countTemplate(item, *) { 185 | static db := debounce((config, value) => ( 186 | writeIni(config, value) 187 | )) 188 | value := item.value 189 | global keyCountTemplate := value 190 | updateTip(A_IsPaused) 191 | 192 | db("keyCountTemplate", value) 193 | } 194 | g.AddEdit("xs ReadOnly cGray -VScroll w" bw, '这里有一些模板变量,它们在 4 和 5 中都可用:`n%\n% (换行),%keyCount% (按键次数),%appState% (软件运行状态)') 195 | tab.UseTab(3) 196 | g.AddEdit("Section r15 ReadOnly w" bw, "1. 按钮 —— 更新检查`n - 点击后,会打开版本更新相关的配置菜单`n`n2. 按钮 —— JAB/JetBrains IDE 支持`n - 如果你正在使用 JetBrains IDE,请双击启用它,并根据窗口提示完成所有步骤`n - 完成后,InputTip 才能在 JetBrains IDE 中获取到输入光标位置并显示符号`n`n3. 按钮 —— 打开软件目录`n - 点击后,会自动打开软件所在的运行目录`n`n4. 按钮 —— 设置用户名`n - InputTip 需要用到你的用户名来确保【开机自启动】等功能正常运行`n - 首次初始化时会自动获取用户名并保存到配置文件中,之后可通过此按钮修改`n - 对于域用户,自动获取的用户名缺少域前缀,需要手动添加`n`n5. 配置 —— 自定义软件托盘图标`n - 只需将 png 图片添加到图片目录中,然后在下拉列表中选择它即可实现自定义`n`n6. 按钮 —— 打开图标目录`n - 点击它后,会自动打开图标目录,你可以将自己喜欢的图标添加到这里`n - 然后通过【自定义软件托盘图标】进行设置`n`n7. 按钮 —— 刷新下拉列表`n - 当你将 png 图片添加到图片目录后,可能需要通过点击它刷新下拉列表`n`n8. 配置 —— 配置菜单字体大小`n - 可以通过设置字体大小来优化配置菜单在不同屏幕上的显示效果`n - 取值范围: 5-30,建议 12-20 之间`n`n9. 配置 —— 轮询响应间隔时间`n - 每隔 x 毫秒,InputTip 就会更新符号的位置/状态、输入法状态、鼠标样式等`n - 这里的 x 就是轮询响应间隔时间,单位:毫秒,默认为 20`n - 建议 50 以内,因为轮询间隔越短,依赖轮询的部分功能的稳定性越好`n`n10. 配置 —— 按键输入次数统计`n - 开启后,InputTip 会记录你的按键输入次数`n - 然后通过写入下方的文字模板中进行显示`n - 注意: 只有当上一次按键和当前按键不同时,才会记为一次有效按键`n`n11. 配置 —— 鼠标悬停在【托盘图标】上时的文字模板`n - 鼠标悬停在【托盘图标】上时,会根据此处设置的文字模板进行显示`n - 变量: %\n% (换行),%keyCount% (按键次数),%appState% (软件运行状态)`n`n12. 配置 —— 按键输入次数统计的文字模板`n - 开启【按键输入次数统计】后,此处的文字模板会被添加到鼠标悬停的提示中`n - 变量: %\n% (换行),%keyCount% (按键次数),%appState% (软件运行状态)") 197 | g.AddLink(, '相关链接: 在 JetBrains 系列 IDE 中使用 InputTip') 198 | 199 | g.OnEvent("Close", e_close) 200 | e_close(*) { 201 | g.Destroy() 202 | gc.tab := 0 203 | gc.timer := 0 204 | try { 205 | gc.w.subGui.Destroy() 206 | gc.w.subGui := "" 207 | } 208 | } 209 | return g 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/menu/auto-exit.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_auto_exit(*) { 4 | showGui() 5 | showGui(deep := 0) { 6 | createUniqueGui(autoExitGui).Show() 7 | autoExitGui(info) { 8 | g := createGuiOpt("InputTip - 指定窗口自动退出") 9 | tab := g.AddTab3("-Wrap", ["自动退出", "关于"]) 10 | tab.UseTab(1) 11 | 12 | if (info.i) { 13 | g.AddText(, gui_width_line) 14 | return g 15 | } 16 | w := info.w 17 | bw := w - g.MarginX * 2 18 | 19 | addItem(state) { 20 | gc.%"LV_" state%.Opt("-Redraw") 21 | valueArr := StrSplit(readIniSection("App-Auto-" state), "`n") 22 | for v in valueArr { 23 | kv := StrSplit(v, "=", , 2) 24 | part := StrSplit(kv[2], ":", , 4) 25 | if (part.Length >= 2) { 26 | name := part[1] 27 | isGlobal := part[2] 28 | isRegex := "" 29 | title := "" 30 | if (part.Length == 4) { 31 | isRegex := part[3] 32 | title := part[4] 33 | } 34 | 35 | tipGlobal := isGlobal ? "进程级" : "标题级" 36 | 37 | tipRegex := isRegex ? "正则" : "相等" 38 | gc.%"LV_" state%.Add(, name, tipGlobal, tipRegex, title, kv[1]) 39 | } else { 40 | IniDelete("InputTip.ini", "App-Auto-" state, kv[1]) 41 | } 42 | } 43 | gc.%"LV_" state%.Opt("+Redraw") 44 | gc.%state "_title"%.Text := "( " gc.%"LV_" state%.GetCount() " 个 )" 45 | autoHdrLV(gc.%"LV_" state%) 46 | } 47 | 48 | fn_dbClick(LV, RowNumber) { 49 | handleClick(LV, RowNumber, LV._type) 50 | } 51 | 52 | handleClick(LV, RowNumber, from) { 53 | if (!RowNumber) { 54 | return 55 | } 56 | exe_name := LV.GetText(RowNumber) 57 | if (gc.w.subGui) { 58 | gc.w.subGui.Destroy() 59 | gc.w.subGui := "" 60 | } 61 | 62 | itemValue := { 63 | exe_name: LV.GetText(RowNumber, 1), 64 | status: stateMap.%from%, 65 | tipGlobal: LV.GetText(RowNumber, 2), 66 | tipRegex: LV.GetText(RowNumber, 3), 67 | title: LV.GetText(RowNumber, 4), 68 | id: LV.GetText(RowNumber, 5) 69 | } 70 | createGui(editGui).Show() 71 | editGui(info) { 72 | return fn_edit(LV, RowNumber, from, "edit", itemValue) 73 | } 74 | } 75 | 76 | fn_edit(LV, RowNumber, from, action, itemValue) { 77 | if (action == "edit") { 78 | actionText := "编辑" 79 | } else { 80 | actionText := "添加" 81 | } 82 | 83 | label := "正在" actionText "一条自动" stateMap.%from% "规则" 84 | 85 | g := createGuiOpt("InputTip - " label) 86 | 87 | if (info.i) { 88 | g.AddText(, gui_width_line) 89 | return g 90 | } 91 | w := info.w 92 | bw := w - g.MarginX * 2 93 | 94 | scaleWidth := bw / 1.5 95 | 96 | g.AddText(, "1. 进程名称: ") 97 | _ := g.AddEdit("yp w" scaleWidth, "") 98 | _.Text := itemValue.exe_name 99 | _.OnEvent("Change", e_changeName) 100 | e_changeName(item, *) { 101 | v := item.Text 102 | itemValue.exe_name := v 103 | } 104 | 105 | g.AddText("xs", "2. 行为类型: ") 106 | _ := g.AddDropDownList("yp Disabled w" scaleWidth, ["暂停", "退出"]) 107 | _.Text := itemValue.status 108 | _.OnEvent("Change", e_changeState) 109 | e_changeState(item, *) { 110 | v := item.Text 111 | itemValue.status := v 112 | } 113 | 114 | g.AddText("xs", "3. 匹配范围: ") 115 | _ := g.AddDropDownList("yp w" scaleWidth, ["进程级", "标题级"]) 116 | _.Text := itemValue.tipGlobal 117 | _.OnEvent("Change", e_changeLevel) 118 | e_changeLevel(item, *) { 119 | v := item.Text 120 | itemValue.tipGlobal := v 121 | } 122 | 123 | g.AddText("xs cGray", "【匹配模式】和【匹配标题】仅在【匹配范围】为【标题级】时有效") 124 | g.AddText("xs", "4. 匹配模式: ") 125 | _ := g.AddDropDownList("yp w" scaleWidth, ["相等", "正则"]) 126 | _.Text := itemValue.tipRegex 127 | _.OnEvent("Change", e_changeMatch) 128 | e_changeMatch(item, *) { 129 | v := item.Text 130 | itemValue.tipRegex := v 131 | } 132 | 133 | g.AddText("xs", "5. 匹配标题: ") 134 | _ := g.AddEdit("yp w" scaleWidth) 135 | _.Text := itemValue.title 136 | _.OnEvent("Change", e_changeTitle) 137 | e_changeTitle(item, *) { 138 | v := item.Text 139 | itemValue.title := v 140 | } 141 | 142 | g.AddButton("xs w" bw / 1.2, "完成" actionText).OnEvent("Click", e_set) 143 | e_set(*) { 144 | fn_set(action, 0) 145 | } 146 | if (action == "edit") { 147 | g.AddButton("xs w" bw / 1.2, "删除它").OnEvent("Click", e_delete) 148 | e_delete(*) { 149 | fn_set(action, 1) 150 | } 151 | } 152 | 153 | fn_set(action, delete) { 154 | g.Destroy() 155 | 156 | if (delete) { 157 | try { 158 | IniDelete("InputTip.ini", "App-Auto-" from, itemValue.id) 159 | LV.Delete(RowNumber) 160 | gc.%from "_title"%.Text := "( " gc.%"LV_" from%.GetCount() " 个 )" 161 | } 162 | } else { 163 | isGlobal := itemValue.tipGlobal == "进程级" ? 1 : 0 164 | isRegex := itemValue.tipRegex == "正则" ? 1 : 0 165 | value := itemValue.exe_name ":" isGlobal ":" isRegex ":" itemValue.title 166 | ; 没有进行移动 167 | if (itemValue.status == from) { 168 | writeIni(itemValue.id, value, "App-Auto-" from, "InputTip.ini") 169 | LV.Modify(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.id) 170 | } else { 171 | try { 172 | IniDelete("InputTip.ini", "App-Auto-" from, itemValue.id) 173 | LV.Delete(RowNumber) 174 | gc.%from "_title"%.Text := "( " gc.%"LV_" from%.GetCount() " 个 )" 175 | } 176 | state := stateTextMap.%itemValue.status% 177 | writeIni(itemValue.id, value, "App-Auto-" state, "InputTip.ini") 178 | gc.%"LV_" state%.Insert(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.id) 179 | gc.%state "_title"%.Text := "( " gc.%"LV_" state%.GetCount() " 个 )" 180 | } 181 | } 182 | try { 183 | autoHdrLV(gc.%"LV_" state%) 184 | } 185 | global app_AutoExit := StrSplit(readIniSection("App-Auto-Exit"), "`n") 186 | } 187 | return g 188 | } 189 | 190 | for i, v in ["Exit"] { 191 | g.AddText("Section cRed", gui_help_tip) 192 | g.AddText("Section", "需要自动" stateMap.%v% " InputTip") 193 | g.AddText("yp", "的应用窗口") 194 | gc.%v "_title"% := g.AddText("yp cRed w" bw / 3, "( 0 个 )") 195 | 196 | LV := "LV_" v 197 | gc.%LV% := g.AddListView("xs -LV0x10 -Multi r7 NoSortHdr Sort Grid w" w, ["进程名称", "匹配范围", "匹配模式", "匹配标题", "创建时间"]) 198 | addItem(v) 199 | autoHdrLV(gc.%LV%) 200 | gc.%LV%.OnEvent("DoubleClick", fn_dbClick) 201 | gc.%LV%._type := v 202 | 203 | _ := g.AddButton("xs w" w / 2, "快捷添加") 204 | _._LV := LV 205 | _._type := v 206 | _.OnEvent("Click", (item, *) => (fn_add(item._LV, item._type))) 207 | 208 | _ := g.AddButton("yp w" w / 2, "手动添加") 209 | _._LV := LV 210 | _._type := v 211 | _.OnEvent("Click", fn_add_manually) 212 | 213 | tab.UseTab(i + 1) 214 | } 215 | fn_add_manually(item, *) { 216 | itemValue := { 217 | exe_name: "", 218 | status: stateMap.%item._type%, 219 | tipGlobal: "进程级", 220 | tipRegex: "相等", 221 | title: "", 222 | id: returnId() 223 | } 224 | fn_edit(item._LV, 1, item._type, "add", itemValue).Show() 225 | } 226 | 227 | fn_add(LV, state) { 228 | args := { 229 | title: "快捷添加", 230 | state: state, 231 | configName: "", 232 | LV: LV 233 | } 234 | createProcessListGui(args, addClick, e_add_manually) 235 | 236 | addClick(args) { 237 | windowInfo := args.windowInfo 238 | LV := args.LV 239 | RowNumber := args.RowNumber 240 | state := args.parentArgs.state 241 | 242 | itemValue := { 243 | exe_name: windowInfo.exe_name, 244 | status: stateMap.%state%, 245 | tipGlobal: "进程级", 246 | tipRegex: "相等", 247 | title: windowInfo.title, 248 | id: windowInfo.id, 249 | } 250 | fn_edit(LV, RowNumber, state, "add", itemValue).Show() 251 | } 252 | 253 | e_add_manually(args) { 254 | windowInfo := args.windowInfo 255 | LV := args.parentArgs.LV 256 | state := args.parentArgs.state 257 | 258 | itemValue := { 259 | exe_name: windowInfo.exe_name, 260 | status: stateMap.%state%, 261 | tipGlobal: "进程级", 262 | tipRegex: "相等", 263 | title: windowInfo.title, 264 | id: windowInfo.id, 265 | } 266 | fn_edit(LV, 1, state, "add", itemValue).Show() 267 | } 268 | } 269 | 270 | g.AddEdit("ReadOnly VScroll r12 w" w, "1. 简要说明`n - 这个菜单用来设置【指定窗口自动退出】的匹配规则`n - 通常用于指定一些游戏进程,以确保启动游戏时自动退出 InputTip`n - 请注意,启动游戏前,手动确保退出 InputTip 才是最佳的使用习惯`n`n - 下方是对应的规则列表`n - 双击列表中的任意一行,进行编辑或删除`n - 如果需要添加,请查看下方按钮相关的使用说明`n`n2. 规则列表 —— 进程名称`n - 应用窗口实际的进程名称`n`n3. 规则列表 —— 匹配范围`n - 【进程级】或【标题级】,它控制触发时机`n - 【进程级】: 只有从一个进程切换到另一个进程时,才会触发`n - 【标题级】: 只要窗口标题发生变化,且匹配成功,就会触发`n`n4. 规则列表 —— 匹配模式`n - 只有当匹配范围为【标题级】时,才会生效`n - 【相等】或【正则】,它控制标题匹配的模式`n - 【相等】: 只有窗口标题和指定的标题完全一致,才会触发`n - 【正则】: 使用正则表达式匹配标题,匹配成功则触发`n`n5. 规则列表 —— 匹配标题`n - 只有当匹配范围为【标题级】时,才会生效`n - 指定一个标题或者正则表达式,与【匹配模式】相对应,匹配成功则触发`n - 如果不知道当前窗口的相关信息(进程/标题等),可以通过以下方式获取`n - 【托盘菜单】=>【获取窗口信息】`n`n6. 规则列表 —— 创建时间`n - 它是每条规则的创建时间`n`n7. 规则列表 —— 操作`n - 双击列表中的任意一行,进行编辑或删除`n`n8. 按钮 —— 快捷添加`n - 点击它,可以添加一条新的规则`n - 它会弹出一个新的菜单页面,会显示当前正在运行的【应用进程列表】`n - 你可以双击【应用进程列表】中的任意一行进行快速添加`n - 详细的使用说明请参考弹出的菜单页面中的【关于】`n`n9. 按钮 —— 手动添加`n - 点击它,可以添加一条新的规则`n - 它会直接弹出添加窗口,你需要手动填写进程名称、标题等信息") 271 | g.AddLink(, '相关链接: 游戏可能将 InputTip 视为病毒外挂') 272 | 273 | g.OnEvent("Close", fn_close) 274 | fn_close(*) { 275 | g.Destroy() 276 | try { 277 | gc.w.subGui.Destroy() 278 | gc.w.subGui := "" 279 | } 280 | } 281 | return g 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/menu/switch-window.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_switch_window(*) { 4 | showGui() 5 | showGui(deep := 0) { 6 | createUniqueGui(switchWindowGui).Show() 7 | switchWindowGui(info) { 8 | g := createGuiOpt("InputTip - 指定窗口自动切换输入法状态") 9 | tab := g.AddTab3("-Wrap", ["中文状态", "英文状态", "大写锁定", "关于"]) 10 | tab.UseTab(1) 11 | 12 | if (info.i) { 13 | g.AddText(, gui_width_line) 14 | return g 15 | } 16 | w := info.w 17 | bw := w - g.MarginX * 2 18 | 19 | addItem(state) { 20 | gc.%"LV_" state%.Opt("-Redraw") 21 | valueArr := StrSplit(readIniSection("App-" state), "`n") 22 | for v in valueArr { 23 | kv := StrSplit(v, "=", , 2) 24 | part := StrSplit(kv[2], ":", , 4) 25 | if (part.Length >= 2) { 26 | name := part[1] 27 | isGlobal := part[2] 28 | isRegex := "" 29 | title := "" 30 | if (part.Length == 4) { 31 | isRegex := part[3] 32 | title := part[4] 33 | } 34 | 35 | tipGlobal := isGlobal ? "进程级" : "标题级" 36 | 37 | tipRegex := isRegex ? "正则" : "相等" 38 | gc.%"LV_" state%.Add(, name, tipGlobal, tipRegex, title, kv[1]) 39 | } else { 40 | IniDelete("InputTip.ini", "App-" state, kv[1]) 41 | } 42 | } 43 | gc.%"LV_" state%.Opt("+Redraw") 44 | gc.%state "_title"%.Text := "( " gc.%"LV_" state%.GetCount() " 个 )" 45 | autoHdrLV(gc.%"LV_" state%) 46 | } 47 | 48 | fn_dbClick(LV, RowNumber) { 49 | handleClick(LV, RowNumber, LV._type) 50 | } 51 | 52 | handleClick(LV, RowNumber, from) { 53 | if (!RowNumber) { 54 | return 55 | } 56 | exe_name := LV.GetText(RowNumber) 57 | if (gc.w.subGui) { 58 | gc.w.subGui.Destroy() 59 | gc.w.subGui := "" 60 | } 61 | 62 | itemValue := { 63 | exe_name: LV.GetText(RowNumber, 1), 64 | status: stateMap.%from%, 65 | tipGlobal: LV.GetText(RowNumber, 2), 66 | tipRegex: LV.GetText(RowNumber, 3), 67 | title: LV.GetText(RowNumber, 4), 68 | id: LV.GetText(RowNumber, 5) 69 | } 70 | createGui(editGui).Show() 71 | editGui(info) { 72 | return fn_edit(LV, RowNumber, from, "edit", itemValue) 73 | } 74 | } 75 | 76 | fn_edit(LV, RowNumber, from, action, itemValue) { 77 | ; 是否自动添加到符号的白名单中 78 | needAddWhiteList := 1 79 | 80 | if (action == "edit") { 81 | actionText := "编辑" 82 | } else { 83 | actionText := "添加" 84 | } 85 | 86 | label := "正在" actionText "一条自动切换规则" 87 | 88 | g := createGuiOpt("InputTip - " label) 89 | 90 | if (info.i) { 91 | g.AddText(, gui_width_line) 92 | return g 93 | } 94 | w := info.w 95 | bw := w - g.MarginX * 2 96 | 97 | if (action != "edit") { 98 | g.AddText("cRed", "是否添加到【符号的白名单】中: ") 99 | _ := g.AddDropDownList("yp", ["【否】不添加", "【是】自动添加"]) 100 | _.Value := needAddWhiteList + 1 101 | _.OnEvent("Change", e_change) 102 | e_change(item, *) { 103 | needAddWhiteList := item.value - 1 104 | } 105 | g.AddText("xs cGray", "如果选择【是】,且它在白名单中不存在,将以【进程级】自动添加") 106 | } 107 | 108 | scaleWidth := bw / 1.5 109 | 110 | g.AddText(, "1. 进程名称: ") 111 | _ := g.AddEdit("yp w" scaleWidth, "") 112 | _.Text := itemValue.exe_name 113 | _.OnEvent("Change", e_changeName) 114 | e_changeName(item, *) { 115 | v := item.Text 116 | itemValue.exe_name := v 117 | } 118 | 119 | g.AddText("xs", "2. 状态切换: ") 120 | _ := g.AddDropDownList("yp w" scaleWidth, ["中文状态", "英文状态", "大写锁定"]) 121 | _.Text := itemValue.status 122 | _.OnEvent("Change", e_changeState) 123 | e_changeState(item, *) { 124 | v := item.Text 125 | itemValue.status := v 126 | } 127 | 128 | g.AddText("xs", "3. 匹配范围: ") 129 | _ := g.AddDropDownList("yp w" scaleWidth, ["进程级", "标题级"]) 130 | _.Text := itemValue.tipGlobal 131 | _.OnEvent("Change", e_changeLevel) 132 | e_changeLevel(item, *) { 133 | v := item.Text 134 | itemValue.tipGlobal := v 135 | } 136 | 137 | g.AddText("xs cGray", "【匹配模式】和【匹配标题】仅在【匹配范围】为【标题级】时有效") 138 | g.AddText("xs", "4. 匹配模式: ") 139 | _ := g.AddDropDownList("yp w" scaleWidth, ["相等", "正则"]) 140 | _.Text := itemValue.tipRegex 141 | _.OnEvent("Change", e_changeMatch) 142 | e_changeMatch(item, *) { 143 | v := item.Text 144 | itemValue.tipRegex := v 145 | } 146 | 147 | g.AddText("xs", "5. 匹配标题: ") 148 | _ := g.AddEdit("yp w" scaleWidth) 149 | _.Text := itemValue.title 150 | _.OnEvent("Change", e_changeTitle) 151 | e_changeTitle(item, *) { 152 | v := item.Text 153 | itemValue.title := v 154 | } 155 | 156 | g.AddButton("xs w" bw / 1.2, "完成" actionText).OnEvent("Click", e_set) 157 | e_set(*) { 158 | fn_set(action, 0) 159 | } 160 | if (action == "edit") { 161 | g.AddButton("xs w" bw / 1.2, "删除它").OnEvent("Click", e_delete) 162 | e_delete(*) { 163 | fn_set(action, 1) 164 | } 165 | } 166 | 167 | fn_set(action, delete) { 168 | g.Destroy() 169 | 170 | if (delete) { 171 | try { 172 | IniDelete("InputTip.ini", "App-" from, itemValue.id) 173 | LV.Delete(RowNumber) 174 | gc.%from "_title"%.Text := "( " gc.%"LV_" from%.GetCount() " 个 )" 175 | } 176 | } else { 177 | isGlobal := itemValue.tipGlobal == "进程级" ? 1 : 0 178 | isRegex := itemValue.tipRegex == "正则" ? 1 : 0 179 | value := itemValue.exe_name ":" isGlobal ":" isRegex ":" itemValue.title 180 | ; 没有进行移动 181 | if (itemValue.status == from) { 182 | writeIni(itemValue.id, value, "App-" from, "InputTip.ini") 183 | LV.Modify(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.id) 184 | } else { 185 | if (action == "edit") { 186 | try { 187 | IniDelete("InputTip.ini", "App-" from, itemValue.id) 188 | LV.Delete(RowNumber) 189 | gc.%from "_title"%.Text := "( " gc.%"LV_" from%.GetCount() " 个 )" 190 | } 191 | } 192 | state := stateTextMap.%itemValue.status% 193 | writeIni(itemValue.id, value, "App-" state, "InputTip.ini") 194 | gc.%"LV_" state%.Insert(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.id) 195 | gc.%state "_title"%.Text := "( " gc.%"LV_" state%.GetCount() " 个 )" 196 | } 197 | if (needAddWhiteList) { 198 | updateWhiteList(itemValue.exe_name) 199 | } 200 | } 201 | try { 202 | autoHdrLV(gc.%"LV_" state%) 203 | } 204 | updateAutoSwitchList() 205 | } 206 | return g 207 | } 208 | 209 | 210 | for i, v in ["CN", "EN", "Caps"] { 211 | g.AddText("Section cRed", gui_help_tip) 212 | g.AddText("Section", "需要自动切换到") 213 | g.AddText("yp cRed", stateMap.%v%) 214 | g.AddText("yp", "的应用窗口") 215 | gc.%v "_title"% := g.AddText("yp cRed w" bw / 3, "( 0 个 )") 216 | 217 | LV := "LV_" v 218 | gc.%LV% := g.AddListView("xs -LV0x10 -Multi r7 NoSortHdr Sort Grid w" w, ["进程名称", "匹配范围", "匹配模式", "匹配标题", "创建时间"]) 219 | addItem(v) 220 | autoHdrLV(gc.%LV%) 221 | gc.%LV%.OnEvent("DoubleClick", fn_dbClick) 222 | gc.%LV%._type := v 223 | 224 | _ := g.AddButton("xs w" w / 2, "快捷添加") 225 | _._LV := LV 226 | _._type := v 227 | _.OnEvent("Click", (item, *) => (fn_add(item._LV, item._type))) 228 | 229 | _ := g.AddButton("yp w" w / 2, "手动添加") 230 | _._LV := LV 231 | _._type := v 232 | _.OnEvent("Click", fn_add_manually) 233 | 234 | tab.UseTab(i + 1) 235 | } 236 | fn_add_manually(item, *) { 237 | itemValue := { 238 | exe_name: "", 239 | status: stateMap.%item._type%, 240 | tipGlobal: "进程级", 241 | tipRegex: "相等", 242 | title: "", 243 | id: returnId() 244 | } 245 | fn_edit(item._LV, 1, item._type, "add", itemValue).Show() 246 | } 247 | 248 | fn_add(LV, state) { 249 | args := { 250 | title: "快捷添加", 251 | state: state, 252 | configName: "", 253 | LV: LV 254 | } 255 | createProcessListGui(args, addClick, e_add_manually) 256 | 257 | addClick(args) { 258 | windowInfo := args.windowInfo 259 | LV := args.LV 260 | RowNumber := args.RowNumber 261 | state := args.parentArgs.state 262 | 263 | itemValue := { 264 | exe_name: windowInfo.exe_name, 265 | status: stateMap.%state%, 266 | tipGlobal: "进程级", 267 | tipRegex: "相等", 268 | title: windowInfo.title, 269 | id: windowInfo.id, 270 | } 271 | fn_edit(LV, RowNumber, state, "add", itemValue).Show() 272 | } 273 | 274 | e_add_manually(args) { 275 | windowInfo := args.windowInfo 276 | LV := args.parentArgs.LV 277 | state := args.parentArgs.state 278 | 279 | itemValue := { 280 | exe_name: windowInfo.exe_name, 281 | status: stateMap.%state%, 282 | tipGlobal: "进程级", 283 | tipRegex: "相等", 284 | title: windowInfo.title, 285 | id: windowInfo.id, 286 | } 287 | fn_edit(LV, 1, state, "add", itemValue).Show() 288 | } 289 | } 290 | 291 | g.AddEdit("ReadOnly VScroll r12 w" w, "1. 简要说明`n - 这个菜单用来设置【指定窗口自动切换状态】的匹配规则`n - 上方是三个 Tab 标签页: 【中文状态】【英文状态】【大写锁定】`n - 下方是对应的规则列表`n - 双击列表中的任意一行,进行编辑或删除`n - 如果需要添加,请查看下方按钮相关的使用说明`n`n2. 规则列表 —— 进程名称`n - 应用窗口实际的进程名称`n`n3. 规则列表 —— 匹配范围`n - 【进程级】或【标题级】,它控制自动切换的时机`n - 【进程级】: 只有从一个进程切换到另一个进程时,才会触发`n - 【标题级】: 只要窗口标题发生变化,且匹配成功,就会触发`n`n4. 规则列表 —— 匹配模式`n - 只有当匹配范围为【标题级】时,才会生效`n - 【相等】或【正则】,它控制标题匹配的模式`n - 【相等】: 只有窗口标题和指定的标题完全一致,才会触发自动切换`n - 【正则】: 使用正则表达式匹配标题,匹配成功则触发自动切换`n`n5. 规则列表 —— 匹配标题`n - 只有当匹配范围为【标题级】时,才会生效`n - 指定一个标题或者正则表达式,与【匹配模式】相对应,匹配成功则触发自动切换`n - 如果不知道当前窗口的相关信息(进程/标题等),可以通过以下方式获取`n - 【托盘菜单】=>【获取窗口信息】`n`n6. 规则列表 —— 创建时间`n - 它是每条规则的创建时间`n`n7. 规则列表 —— 操作`n - 双击列表中的任意一行,进行编辑或删除`n`n8. 按钮 —— 快捷添加`n - 点击它,可以添加一条新的规则`n - 它会弹出一个新的菜单页面,会显示当前正在运行的【应用进程列表】`n - 你可以双击【应用进程列表】中的任意一行进行快速添加`n - 详细的使用说明请参考弹出的菜单页面中的【关于】`n`n9. 按钮 —— 手动添加`n - 点击它,可以添加一条新的规则`n - 它会直接弹出添加窗口,你需要手动填写进程名称、标题等信息`n`n10. 自动切换生效需要满足的前提条件`n - 前提条件: `n - 当 InputTip 执行自动切换时,此时的输入法可以正常切换状态`n - 大写锁定除外,因为它是通过模拟输入【CapsLock】按键实现,不受其他影响`n`n - 以【微软输入法】为例`n - 和常规输入法不同,只有当聚焦到输入框时,它才能正常切换输入法状态`n - 但是,当 InputTip 执行自动切换时,可能并没有聚焦到输入框,自动切换就会失效`n`n - 再以【美式键盘 ENG】为例`n - 它只有英文状态和大写锁定,所以只有英文状态的和大写锁定的自动切换有效") 292 | g.AddLink(, '相关链接: 指定窗口自动切换状态') 293 | 294 | g.OnEvent("Close", fn_close) 295 | fn_close(*) { 296 | g.Destroy() 297 | try { 298 | gc.w.subGui.Destroy() 299 | gc.w.subGui := "" 300 | } 301 | } 302 | return g 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

InputTip

2 | 3 |

4 | Github | 5 | Gitee 6 |

7 | 8 |

9 | 10 | license 11 | 12 | 13 | version 14 | 15 | 16 | code size 17 | 18 | 19 | repo size 20 | 21 | 22 | created 23 | 24 |

25 | 26 | --- 27 | 28 | ![github-socialify](https://abgox.com/github-socialify-InputTip.svg) 29 | 30 |

31 | 喜欢这个项目?请给它 Star ⭐️ 或 赞赏 💰 32 |

33 | 34 | > [!Tip] 35 | > 36 | > - InputTip 使用 [AutoHotkey(AHK)](https://github.com/AutoHotkey/AutoHotkey) 语言编写 37 | > - [InputTip 可能被误判为游戏外挂,exe 版本可能被误判为病毒程序](https://inputtip.abgox.com/faq/about-virus) 38 | > - [只推荐使用 zip 版本,除非你有不得不使用 exe 版本的理由](https://inputtip.abgox.com/faq/zip-vs-exe) 39 | > - InputTip 只适用于 **Windows** 系统: **Win10** 以上可用,以下未知 40 | > - `托盘菜单` 指的是鼠标右键点击任务栏托盘区域的 InputTip 后显示的菜单 41 | 42 | ## 介绍 43 | 44 | - 一个输入法状态管理工具: 实时提示(鼠标方案/符号方案) + 状态切换(窗口触发/热键触发) 45 | 46 | - [多种状态提示方案](#关于状态提示方案) 47 | - [鼠标方案](#鼠标方案): 使用多套鼠标光标样式,根据输入法状态加载不同的鼠标光标样式 48 | - [符号方案](#符号方案): 根据输入法状态在输入光标附近显示不同的 **符号** 49 | - 折中方案: `在鼠标附近显示符号` 50 | - 切换不同窗口时,自动切换到指定的输入法状态(`进程级` 和 `标题级` 窗口匹配) 51 | - 设置按键切换到指定的输入法状态,参考 [状态切换](#关于状态切换) 52 | - 详细便捷的配置菜单 53 | - 所有的配置变动,都只应该在 `托盘菜单` 中进行 54 | - 不要手动修改 `InputTip.ini` 文件 55 | 56 | ## 演示 57 | 58 | > [!Tip] 59 | > 60 | > - 如果这里无法正常显示,[可前往官网查看](https://inputtip.abgox.com/) 61 | > - [点击查看 Bilibili 中的介绍及教学视频](https://www.bilibili.com/video/BV15oYKz5EQ8) 62 | 63 | ![InputTip-demo](https://inputtip.abgox.com/releases/v2/demo.gif) 64 | 65 | ## 新的变化 66 | 67 | - 请查阅 [更新日志](./src/CHANGELOG.md) 68 | 69 | ## 常见问题 70 | 71 | - 如果有使用问题,请先确保当前使用的 InputTip 是最新版本 72 | 73 | - 如果是 [zip 版本](#zip-版本): 74 | 75 | - `托盘菜单` => `其他设置` => `更新检查` => `与源代码仓库同步` 76 | - 同步完成后,检查问题是否仍然存在 77 | 78 | - 如果是 [exe 版本](#exe-版本): 79 | - 在 `托盘菜单` => `关于` 中查看当前版本号 80 | - `托盘菜单` => `其他设置` => `更新检查` => `立即检查版本更新` 81 | - 如果存在新版本,你应该先更新,然后检查问题是否仍然存在 82 | 83 | - 然后查看相关的文档 84 | - 本页面中包含的内容 85 | - [常见问题(FAQ)](https://inputtip.abgox.com/faq/) 86 | - [输入法和应用窗口兼容情况](#兼容情况) 87 | - [issues](https://github.com/abgox/InputTip/issues) 88 | - ... 89 | - 如果还有问题,可以创建 [issues](https://github.com/abgox/InputTip/issues) 或前往 [腾讯频道](https://pd.qq.com/s/gyers18g6?businessType=5) 交流反馈 90 | 91 | ## 安装 92 | 93 | ### zip 版本 94 | 95 | > [!Tip] 96 | > 97 | > - 推荐使用,它拥有 [exe 版本](#exe-版本) 不具备的额外特性,且没有 [exe 版本](#exe-版本) 的缺陷 98 | > - 详情参考: [关于 zip 与 exe 版本的区别以及相关说明](https://inputtip.abgox.com/faq/zip-vs-exe) 99 | 100 | - 下载仓库的最新代码压缩包 `InputTip-main.zip` 101 | - 在仓库的 Releases (发行版) 中下载 `InputTip.zip` 102 | - [前往官网下载 InputTip.zip](https://inputtip.abgox.com/download) 103 | - 使用 [Scoop](https://scoop.sh/) 104 | 105 | - 添加 [abyss](https://abyss.abgox.com) bucket ([Github](https://github.com/abgox/abyss) 或 [Gitee](https://gitee.com/abgox/abyss)) 106 | 107 | - 安装 `abgox.InputTip-zip` 108 | 109 | ```shell 110 | scoop install abyss/abgox.InputTip-zip 111 | ``` 112 | 113 | ### exe 版本 114 | 115 | > [!Warning] 116 | > 117 | > - 不推荐使用,和 [zip 版本](#zip-版本) 相比,它缺少了一些特性,同时存在一些缺陷 118 | > - 详情参考: [关于 zip 与 exe 版本的区别以及相关说明](https://inputtip.abgox.com/faq/zip-vs-exe) 119 | 120 | - 在仓库的 Releases (发行版) 中下载 `InputTip.exe` 121 | 122 | - 使用 [Scoop](https://scoop.sh/) 123 | 124 | - 添加 [abyss](https://abyss.abgox.com) bucket ([Github](https://github.com/abgox/abyss) 或 [Gitee](https://gitee.com/abgox/abyss)) 125 | 126 | - 安装 `abgox.InputTip` 127 | 128 | ```shell 129 | scoop install abyss/abgox.InputTip 130 | ``` 131 | 132 | - 使用 [WinGet](https://learn.microsoft.com/windows/package-manager/winget/) 133 | 134 | ```shell 135 | winget install abgox.InputTip 136 | ``` 137 | 138 | ## 使用 139 | 140 | 1. 完成 [安装](#安装) 后,运行对应文件即可 141 | 142 | - [Scoop](https://scoop.sh/): 运行安装后创建的 `InputTip` 快捷方式或命令即可 143 | - [WinGet](https://learn.microsoft.com/windows/package-manager/winget/): 运行 `InputTip` 命令即可 144 | - 手动下载 145 | 146 | - zip 版本: 运行 `InputTip.bat` 147 | - exe 版本: 运行 `InputTip.exe` 148 | 149 | 2. 鼠标左键单击 `托盘图标` 可以快速切换运行状态: `运行中` | `暂停中` 150 | 3. 设置开机自启动: `托盘菜单` => `开机自启动` 151 | 4. 设置 `配置菜单的字体大小` 152 | 153 | - 你可以设置字体大小来优化配置菜单在不同屏幕上的显示效果 154 | - `托盘菜单` => `其他设置` => `配置菜单字体大小` 155 | 156 | 5. 使用 [鼠标方案](#鼠标方案): `托盘菜单` => `状态提示 - 鼠标方案` => `加载鼠标样式` 157 | 158 | 6. 使用 [符号方案](#符号方案): `托盘菜单` => `状态提示 - 符号方案` => `指定符号的类型` 159 | 160 | 7. 更多相关配置在 `托盘菜单` 中查看 161 | 162 | ## 卸载 163 | 164 | 1. 取消 `开机自启动`: `托盘菜单` => `开机自启动` 165 | 2. 退出: `托盘菜单` => `退出` 166 | 3. 删除 [目录结构及数据](#目录结构及数据) 中展示的相关目录即可 167 | 168 | ## 目录结构及数据 169 | 170 | - zip 版本 171 | 172 | ```txt 173 | InputTip-zip/ 174 | ├── InputTip.bat # 启动脚本 175 | ├── src/ 176 | ├── InputTip.ini # 配置文件 177 | ├── InputTipCursor/ # 鼠标样式 178 | ├── InputTipSymbol/ # 图片符号 179 | ├── InputTipIcon/ # 软件图标 180 | └── ... # 其他源代码文件 181 | └── ... # 其他文件 182 | 183 | ``` 184 | 185 | - exe 版本 186 | 187 | - exe 版本是一个单文件程序,运行后会创建相关目录和文件 188 | - 建议新建一个目录,将 `InputTip.exe` 放入其中再运行 189 | 190 | ```txt 191 | InputTip-exe/ 192 | ├── InputTip.exe # 主程序 193 | ├── InputTip.ini # 配置文件 194 | ├── InputTipCursor/ # 鼠标样式 195 | ├── InputTipSymbol/ # 图片符号 196 | ├── InputTipIcon/ # 软件图标 197 | └── ... 198 | ``` 199 | 200 | --- 201 | 202 | - 如果你需要备份 InputTip 的数据以便于后续使用,请备份它们 203 | 204 | - `InputTip.ini` 205 | - `InputTipCursor` (如果没有修改,可以忽略) 206 | - `InputTipSymbol` (如果没有修改,可以忽略) 207 | - `InputTipIcon` (如果没有修改,可以忽略) 208 | 209 | - 如果使用 [Scoop](https://scoop.sh/) 安装 [abyss](https://abyss.abgox.com) 中的 `abgox.InputTip-zip` 或 `abgox.InputTip` 210 | - 它会将这些数据文件或文件夹保存在 [Scoop](https://scoop.sh/) 的 persist 目录中 211 | - 可以正常通过 [Scoop](https://scoop.sh/) 更新、卸载,不会删除它们,除非卸载时携带 `-p` 或 `--purge` 参数 212 | - 如果使用 [WinGet](https://learn.microsoft.com/windows/package-manager/winget/) 安装 [abgox.InputTip](https://github.com/microsoft/winget-pkgs/tree/master/manifests/a/abgox/InputTip) 213 | - 这些数据文件或文件夹会保存在 `$env:LocalAppData\Microsoft\WinGet\Packages\abgox.InputTip_Microsoft.Winget.Source_8wekyb3d8bbwe` 中 214 | - 可以正常通过 [WinGet](https://learn.microsoft.com/windows/package-manager/winget/) 更新、卸载,不会删除这些数据文件和文件夹 215 | 216 | ## 自定义功能 217 | 218 | > [!Caution] 219 | > 220 | > - 它是 [zip 版本](#zip-版本) 独有的功能,因为 [exe 版本](#exe-版本) 无法实现它 221 | > - 如果你不熟悉 [AutoHotkey(AHK)](https://github.com/AutoHotkey/AutoHotkey) 语言开发 ([v2 版本](https://www.autohotkey.com/docs/v2/)),不要随意进行更改 222 | > - 不要修改 `plugins` 目录以外的其他源代码文件 223 | 224 | - 在 `src` 目录下有一个 `plugins` 目录 225 | - 目录中添加了一个空的 `InputTip.plugin.ahk` 文件 226 | - InputTip 会加载这个源代码文件,你可以在其中添加自定义功能,比如 **自定义热键**、**自定义热字串** 等 227 | - 或者,在 `plugins` 目录中新建 `.ahk` 文件,然后在 `InputTip.plugin.ahk` 中使用 `#Include` 228 | - `plugins` 目录的特点: 229 | - 在 `plugins` 目录中的文件不会因为版本更新而被覆盖 230 | - 如果你使用 [Scoop](https://scoop.sh/) 安装 [abyss](https://abyss.abgox.com) 中的 `abgox.InputTip-zip`,`plugins` 目录会被 `persist` 231 | - 因此,也可以通过 [Scoop](https://scoop.sh/) 更新,而不会覆盖 `plugins` 目录中的文件 232 | 233 | ## 关于状态提示方案 234 | 235 | ### 鼠标方案 236 | 237 | - 同时使用多套鼠标样式,根据输入法状态加载不同的鼠标样式 238 | - 默认使用 [多彩水滴 Oreo 光标](https://zhutix.com/ico/oreo-cu) 239 | - 默认中文状态为 **红色**,英文状态为 **蓝色**,大写锁定为 **绿色** 240 | - 在 [更多已适配的鼠标样式](https://inputtip.abgox.com/download/extra) 中有提供它们的左手镜像版本 241 | - 详情参考 [自定义鼠标样式](#自定义鼠标样式) 242 | 243 | ### 符号方案 244 | 245 | - 根据输入法状态显示不同的符号 246 | 247 | - 它有两种显示位置 248 | 249 | - 在鼠标附近显示: `托盘菜单` => `在鼠标附近显示符号` 250 | - 在输入光标附近显示: `托盘菜单` => `符号的黑/白名单` 251 | 252 | - 在输入光标附近显示会涉及到获取应用窗口中的输入光标位置信息,受限于 [符号的名单机制](https://inputtip.abgox.com/faq/symbol-list-mechanism/) 253 | - InputTip 将尝试获取输入光标位置信息 254 | - 部分应用可能会因为接口调用出现兼容性问题 255 | - 它们应该从 `符号的白名单` 中移除 256 | - 或者,使用 `在鼠标附近显示符号` 257 | - 部分应用可能必须使用特定的 [光标获取模式](https://inputtip.abgox.com/faq/cursor-mode) 才能成功获取 258 | - 可以通过 `托盘菜单` => `光标获取模式` 进行指定并测试 259 | - 如果获取不到: 260 | - 符号无法显示 261 | - 这就只能使用 [鼠标方案](#鼠标方案) 或者 `在鼠标附近显示符号` 262 | - 获取到了,但符号位置有偏差 263 | - 如果在此应用中,所有窗口的偏差相同: 264 | - 可以通过 `偏移量`、`特殊偏移量` 解决 265 | - 如果在此应用中,不同窗口的偏差不同: 266 | - 无论如何设置,都会有窗口出现位置错误,目前无解 267 | - 参考示例: 268 | - [在 vscode v1.100 版本中,编辑界面和终端界面符号位置有不同偏差](https://github.com/abgox/InputTip/issues/172) 269 | - 在 vscode v1.101 版本中已修复 270 | 271 | ## 关于状态切换 272 | 273 | - InputTip 提供了两种方式实现状态切换: `指定窗口自动切换状态` 和 `状态切换快捷键` 274 | - 核心都是通过模拟输入状态切换按键来实现 275 | - 因此,如果按键本身无法切换状态,则功能失效 276 | - 常见的状态切换按键: `Shift`、`Ctrl + Space` 277 | - InputTip 默认模拟输入 `LShift` (左侧的 `Shift` 按键) 278 | - 指定其他按键: `托盘菜单` => `输入法相关` => `输入法状态的切换方式` 279 | - 大写锁定有些特殊,它通过模拟输入`CapsLock` 按键实现,不受其他影响 280 | 281 | ## [如何在 JetBrains 系列 IDE 中使用 InputTip](https://inputtip.abgox.com/faq/use-inputtip-in-jetbrains) 282 | 283 | > [!Tip] 284 | > 285 | > - [使用 AutoHotkey 官方论坛中的解决方案实现,方案由 Descolada 提出](https://www.autohotkey.com/boards/viewtopic.php?t=130941#p576439) 286 | > - 建议使用 [Microsoft OpenJDK 21](https://learn.microsoft.com/java/openjdk/download#openjdk-21) 287 | > - 如果使用其他版本的 JDK 或 JRE,需要自行测试可用性 288 | > - 例如 [Adoptium Temurin](https://adoptium.net/zh-CN/temurin/releases/?os=windows&arch=any),测试后发现: JDK 版本无效,JRE 版本有效 289 | 290 | > [!Warning] 291 | > 292 | > 如果不使用 [符号方案](#符号方案),或者正在使用 `在鼠标附近显示符号`,无需步骤 1 和 2 293 | 294 | 1. 安装 [Microsoft OpenJDK 21](https://learn.microsoft.com/java/openjdk/download#openjdk-21) 295 | 296 | - 使用 [Scoop](https://scoop.sh/) 297 | 298 | - 添加 [abyss](https://abyss.abgox.com) bucket ([Github](https://github.com/abgox/abyss) 或 [Gitee](https://gitee.com/abgox/abyss)) 299 | 300 | - 安装 `Microsoft.OpenJDK.21` 301 | 302 | ```shell 303 | scoop install abyss/Microsoft.OpenJDK.21 304 | ``` 305 | 306 | - 使用 [WinGet](https://learn.microsoft.com/windows/package-manager/winget/) 307 | 308 | ```shell 309 | winget install Microsoft.OpenJDK.21 310 | ``` 311 | 312 | 2. 启用 `Java Access Bridge` 313 | 314 | - 如果以下命令不存在,请检查 **步骤 1** 315 | - 如果以下命令不能正常运行,请检查环境变量是否配置正确 316 | 317 | ```shell 318 | java -version 319 | ``` 320 | 321 | ```shell 322 | jabswitch -enable 323 | ``` 324 | 325 | 3. `托盘菜单` => `其他设置` => `JAB/JetBrains IDE 支持` 326 | 327 | 4. `托盘菜单` => `光标获取模式`,设置 JetBrains IDE 的光标获取模式为 `JAB` 328 | 329 | 5. 如果未生效,请依次尝试以下操作并查看是否生效 330 | - 重启 InputTip: `托盘菜单` => `重启` 331 | - 重启正在运行的 JetBrains IDE 332 | - 重启系统 333 | 334 | > [!Tip] 335 | > 336 | > 如果有多块屏幕,副屏幕上可能有坐标偏差,需要通过 `托盘菜单` => `特殊偏移量` 手动调整 337 | 338 | ## 自定义鼠标样式 339 | 340 | > [!Tip] 341 | > 342 | > 可以直接使用 [已经适配的一些鼠标样式](https://inputtip.abgox.com/download/extra) 343 | 344 | - 你需要在 `InputTipCursor` 目录下创建一个文件夹 345 | 346 | - 不应该放在 `default` 文件夹下 347 | 348 | - 文件夹中需要包含鼠标样式文件(后缀名为 `.cur` 或 `.ani`) 349 | 350 | - 必须使用以下表格中的文件名(大小写都可以) 351 | 352 | - 每个文件都不是必须的,但建议至少添加 `Arrow`,`IBeam`,`Hand` 353 | 354 | - 详情参考 [微软文档: 关于光标(游标)](https://learn.microsoft.com/windows/win32/menurc/about-cursors) 355 | 356 | | 文件名(类型) | 说明 | 357 | | :----------: | :-----------------------------: | 358 | | Arrow | 普通选择 | 359 | | IBeam | 文本选择/文本输入 | 360 | | Hand | 链接选择 | 361 | | AppStarting | 后台工作 | 362 | | Wait | 忙碌 | 363 | | SizeAll | 移动 | 364 | | SizeNWSE | 对角线调整大小 1 (左上 => 右下) | 365 | | SizeNESW | 对角线调整大小 2 (左下 => 右上) | 366 | | SizeWE | 水平调整大小 | 367 | | SizeNS | 垂直调整大小 | 368 | | No | 无法(禁用) | 369 | | Help | 帮助选择 | 370 | | Cross | 精度选择 | 371 | | UpArrow | 备用选择 | 372 | | Pin | 位置选择 | 373 | | Person | 人员选择 | 374 | | Pen | 手写 | 375 | 376 | > [!Warning] 377 | > 378 | > - 你应该尽量让三种状态下的鼠标样式文件夹中包含的鼠标样式文件的数量和类型是一致的 379 | > - 比如: 380 | > - 如果中文状态的文件夹路径下有 `IBeam.cur` 或 `IBeam.ani` 文件,英文状态或大写锁定的文件夹路径下没有。 381 | > - 则切换到中文状态时,会加载中文状态的 `IBeam.cur` 或 `IBeam.ani` 382 | > - 但是再切换到英文或大写锁定时,`IBeam` 类型的鼠标样式不会变化,因为英文和大写锁定缺少对应的样式文件 383 | 384 | ## 兼容情况 385 | 386 | ### 应用窗口兼容情况 387 | 388 | > [!Tip] 389 | > 390 | > 如果你不使用 [符号方案](#符号方案),可以直接忽略 391 | 392 | - InputTip 在部分应用窗口中无法正确获取到输入光标位置,导致使用 [符号方案](#符号方案) 可能存在问题 393 | - [应用窗口兼容情况](https://inputtip.abgox.com/faq/app-compatibility) 会记录它们 394 | - 你也可以使用 `在鼠标附近显示符号` 作为折中方案 395 | 396 | ### 输入法兼容情况 397 | 398 | > [!Tip] 399 | > 400 | > - InputTip 使用 `通用` 和 `自定义` 模式兼容不同输入法的状态识别,默认使用 `通用` 模式 401 | > - 如果你熟悉了 `自定义` 模式,建议直接使用 `自定义` 模式去配置规则,状态识别会更稳定 402 | > - 设置: `托盘菜单` => `输入法相关` 403 | 404 | - `通用` 405 | 406 | - [微信输入法](https://z.weixin.qq.com/) 407 | - [搜狗输入法](https://shurufa.sogou.com/),[搜狗五笔输入法](https://wubi.sogou.com/) 408 | - [QQ 输入法](https://qq.pinyin.cn/) 409 | - [百度输入法](https://shurufa.baidu.com/) 410 | - 微软拼音,微软五笔,微软仓颉... 411 | - 美式键盘 ENG 412 | - [冰凌输入法](https://icesofts.com/) 413 | - [手心输入法](https://www.xinshuru.com/): 使用 `Shift` 切换中英文状态无法正常识别,需要参照下方的使用方式 414 | 415 | - 首先,确保输入法状态正确 416 | - 通过 `Shift` 将输入法状态切换为中文,然后不再使用 `Shift` 切换状态(此时可以在手心输入法设置中关闭 `Shift` 切换功能) 417 | - 后续只能使用 `Ctrl + Space` 进行中英文状态切换,否则状态识别有误 418 | - 其次,修改 InputTip 的配置 419 | - `托盘菜单` => `输入法相关` => `输入法状态的切换方式`,选择 `模拟输入 Ctrl + Space` 420 | 421 | - 小鹤音形输入法 422 | - 需要使用 [多多输入法生成器](https://duo.ink/ddimegen/ddimegen-desc.html) 生成 423 | - 使用 [多多输入法生成器](https://duo.ink/ddimegen/ddimegen-desc.html) 生成的输入法都可用 424 | - [小小输入法](http://yongim.ysepan.com/) 425 | - [华宇拼音输入法](https://pinyin.thunisoft.com/) 426 | - [影子输入法](https://gitee.com/orz707/Yzime) 427 | - 需要关闭影子输入法中的 `tsf` 428 | - 在键盘布局中,选择一个能正常识别状态的输入法(建议选择[微信输入法](https://z.weixin.qq.com/)、[搜狗输入法](https://shurufa.sogou.com/)等) 429 | - 然后正常使用影子输入法即可 430 | - [可可五笔](https://suke.kim/) 431 | - 谷歌输入法 432 | - ... 433 | 434 | - `自定义` 435 | 436 | - 一个万能的模式,根据规则列表依次匹配,需要根据实际情况添加一条或多条规则 437 | - 详情参考: [输入法状态的识别模式 —— 自定义](https://inputtip.abgox.com/faq/custom-input-mode) 438 | - 可以通过 `输入法相关` => `自定义` 使用以下已知可用的规则配置: 439 | 440 | > `默认状态` 选择 `英文状态` 441 | 442 | - [小狼毫 (Rime)](https://rime.im/download/) 443 | 444 | | 顺序 | 状态码规则 | 切换码规则 | 输入法状态 | 445 | | :--: | :--------: | :--------: | :--------: | 446 | | 1 | | 奇数 | 中文状态 | 447 | 448 | - [讯飞输入法](https://srf.xunfei.cn/) 449 | 450 | | 顺序 | 状态码规则 | 切换码规则 | 输入法状态 | 451 | | :--: | :--------: | :--------: | :--------: | 452 | | 1 | 偶数 | | 中文状态 | 453 | 454 | - [小鹤音形](https://flypy.cc) 455 | 456 | | 顺序 | 状态码规则 | 切换码规则 | 输入法状态 | 457 | | :--: | :--------: | :--------: | :--------: | 458 | | 1 | | 1025 | 中文状态 | 459 | 460 | ## 参考项目 461 | 462 | - [ImTip - aardio](https://github.com/aardio/ImTip) 463 | - [KBLAutoSwitch - flyinclouds](https://github.com/flyinclouds/KBLAutoSwitch) 464 | - [AutoHotkeyScripts - Tebayaki](https://github.com/Tebayaki/AutoHotkeyScripts) 465 | - [language-indicator - yakunins](https://github.com/yakunins/language-indicator) 466 | - [RedDot - Autumn-one](https://github.com/Autumn-one/RedDot) 467 | - [InputTip v1](../v1) 在鼠标附近显示带文字的方块符号,后来 [InputTip v2](./) 版本默认通过不同颜色的鼠标样式来区分 468 | - 之后看到了 [RedDot - Autumn-one](https://github.com/Autumn-one/RedDot) 和 [language-indicator - yakunins](https://github.com/yakunins/language-indicator) 的设计,通过不同颜色加上小符号来判断不同输入法状态 469 | - InputTip 也参照了这样的设计,简单直接,其实就是 [InputTip v1](../v1) 中带文字的方块符号,去掉文字,加上不同的背景颜色 470 | 471 | --- 472 | 473 | [![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source") 474 | -------------------------------------------------------------------------------- /src/menu/input-method.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_input_mode(*) { 4 | createUniqueGui(inputModeGui).Show() 5 | inputModeGui(info) { 6 | global mode := readIni("mode", 1, "InputMethod") 7 | 8 | g := createGuiOpt("InputTip - 输入法相关") 9 | gc.modeList := ["【自定义】", "【通用】"] 10 | tab := g.AddTab3("-Wrap", ["基础配置", "自定义", "关于自定义", "关于切换输入法状态"]) 11 | tab.UseTab(1) 12 | g.AddText("Section cRed", gui_help_tip) 13 | 14 | if (info.i) { 15 | g.AddText(, gui_width_line) 16 | return g 17 | } 18 | w := info.w 19 | bw := w - g.MarginX * 2 20 | line := gui_width_line "----------" 21 | 22 | g.AddText("xs", line) 23 | 24 | g.AddText("xs", "1. 输入法状态的识别模式:") 25 | g.AddDropDownList("yp Choose" mode + 1, [" 自定义", " 通用"]).OnEvent("Change", e_changeMode) 26 | e_changeMode(item, *) { 27 | value := item.value - 1 28 | global mode := value 29 | writeIni("mode", value, "InputMethod") 30 | } 31 | g.AddText("xs cGray", "建议使用【自定义】,并在上方的【自定义】标签页中进行配置") 32 | g.AddText("xs", "2. 输入法状态的切换方式:") 33 | gc.switchStatus := g.AddDropDownList("yp Choose" switchStatus + 1, [" 内部调用 DLL", " 模拟输入 LShift", " 模拟输入 RShift", " 模拟输入 Ctrl+Space"]) 34 | gc.switchStatus.OnEvent("Change", e_switchStatus) 35 | e_switchStatus(item, *) { 36 | value := item.value - 1 37 | if (value == 0 || value == 3) { 38 | configValue := value ? "【模拟输入 Ctrl+Space】" : "【内部调用 DLL】" 39 | createUniqueGui(warningGui).Show() 40 | warningGui(info) { 41 | gc.switchStatus.Value := switchStatus + 1 42 | _g := createGuiOpt("InputTip - 警告") 43 | _g.AddText(, "确定要使用" configValue "吗") 44 | _g.AddText("cRed", "不建议使用它,参考【关于切换输入法状态】的详细说明") 45 | 46 | if (info.i) { 47 | return _g 48 | } 49 | w := info.w 50 | bw := w - _g.MarginX * 2 51 | 52 | _g.AddButton("w" bw, "【是】").OnEvent("Click", e_yes) 53 | _ := _g.AddButton("w" bw, "【否】") 54 | _.OnEvent("Click", e_no) 55 | e_yes(*) { 56 | _g.Destroy() 57 | gc.switchStatus.Value := value + 1 58 | writeIni("switchStatus", value) 59 | global switchStatus := value 60 | restartJAB() 61 | } 62 | e_no(*) { 63 | _g.Destroy() 64 | } 65 | return _g 66 | } 67 | } 68 | else { 69 | writeIni("switchStatus", value) 70 | global switchStatus := value 71 | restartJAB() 72 | } 73 | } 74 | g.AddText("xs cGray", "如果想修改这个配置,需要先通过上方的【关于切换输入法状态】标签页了解详情") 75 | g.AddText("xs", "3. 是否保持大写锁定状态:") 76 | g.AddDropDownList("yp Choose" keepCapsLock + 1, ["【否】", "【是】"]).OnEvent("Change", e_changeKeepCapsLock) 77 | e_changeKeepCapsLock(item, *) { 78 | value := item.value - 1 79 | global keepCapsLock := value 80 | writeIni("keepCapsLock", value) 81 | } 82 | g.AddText("xs cGray", "启用之后,大写锁定只能通过手动按下 CapsLock 键取消,相关的自动切换将失效") 83 | g.AddText("xs", "4. 输入法状态的获取超时:") 84 | _ := g.AddEdit("yp Number Limit5") 85 | _.Focus() 86 | _.OnEvent("Change", e_setTimeout) 87 | _.Value := checkTimeout 88 | e_setTimeout(item, *) { 89 | static db := debounce((value) => ( 90 | writeIni("checkTimeout", value, "InputMethod"), 91 | restartJAB() 92 | )) 93 | 94 | value := item.value 95 | 96 | if value == "" 97 | return 98 | 99 | if value < 100 100 | value := 100 101 | 102 | global checkTimeout := value 103 | 104 | db(value) 105 | } 106 | g.AddText("xs cGray", "单位:毫秒,默认为 500 毫秒,非特殊情况不应该随意修改它`n每次切换输入法状态,InputTip 会从系统获取新的输入法状态`n如果超过了这个时间,则判定获取失败,直接判断为英文状态") 107 | tab.UseTab(2) 108 | 109 | g.AddText("Section ReadOnly cRed -VScroll w" w, "首先需要点击上方的【关于自定义】标签页,查看帮助说明,了解如何设置") 110 | g.AddText("xs", line) 111 | 112 | g.AddText("Section", "默认状态: ") 113 | g.AddDropDownList("yp Choose" baseStatus + 1, [" 英文状态", " 中文状态"]).OnEvent("Change", e_changeBaseStatus) 114 | e_changeBaseStatus(item, *) { 115 | value := item.Value - 1 116 | writeIni("baseStatus", value, "InputMethod") 117 | global baseStatus := value 118 | } 119 | 120 | gc.input_mode_LV := _ := g.AddListView("xs -LV0x10 -Multi r6 NoSortHdr Grid w" w, ["匹配的顺序", "状态码规则", "切换码规则", "输入法状态"]) 121 | fn_reloading_LV(_) 122 | 123 | _.OnEvent("DoubleClick", e_edit) 124 | e_edit(LV, RowNumber) { 125 | fn_edit(LV, RowNumber) 126 | } 127 | 128 | g.AddButton("xs w" w, "添加规则").OnEvent("Click", e_addRule) 129 | e_addRule(*) { 130 | fn_edit(gc.input_mode_LV, modeRules.Length + 1, 1) 131 | } 132 | 133 | fn_edit(LV, RowNumber, add := 0) { 134 | if (!RowNumber) { 135 | return 136 | } 137 | if (add) { 138 | ruleInfo := { 139 | statusRule: "", 140 | conversionRule: "", 141 | status: !baseStatus, 142 | } 143 | typeText := "添加" 144 | } else { 145 | rule := modeRules[RowNumber] 146 | r := StrSplit(rule, "*") 147 | 148 | ruleInfo := { 149 | statusRule: r[1], 150 | conversionRule: r[2], 151 | status: r[3], 152 | } 153 | typeText := "编辑" 154 | } 155 | 156 | createUniqueGui(editRuleGui).Show() 157 | editRuleGui(info) { 158 | g := createGuiOpt("InputTip - " typeText "规则") 159 | 160 | _gc := { 161 | statusNum: "", 162 | statusRule: "", 163 | conversionNum: "", 164 | conversionRule: "", 165 | order: RowNumber, 166 | } 167 | 168 | g.AddText(, "1. 匹配的顺序: ") 169 | 170 | num := 1 171 | list := [] 172 | while (num <= modeRules.Length + add) { 173 | list.Push(" " num) 174 | num++ 175 | } 176 | _ := g.AddDropDownList("yp r9", list) 177 | _.Value := _gc.order 178 | _.OnEvent("Change", e_changeOrder) 179 | e_changeOrder(item, *) { 180 | _gc.order := Trim(item.Value) 181 | } 182 | 183 | g.AddText("xs", "2. 输入法状态: ") 184 | _ := g.AddDropDownList("yp", ["英文状态", "中文状态"]) 185 | _.value := ruleInfo.status + 1 186 | _.OnEvent("Change", e_changeStatus) 187 | e_changeStatus(item, *) { 188 | v := item.Value 189 | ruleInfo.status := v - 1 190 | } 191 | 192 | g.AddText("xs", "3. 状态码规则: ") 193 | g.AddText("xs", " - 指定数字: ") 194 | 195 | _gc.statusNum := _ := g.AddEdit("yp", "") 196 | 197 | if (info.i) { 198 | return g 199 | } 200 | w := info.w 201 | bw := w - g.MarginX * 2 202 | 203 | if (!InStr(ruleInfo.statusRule, "oddNum") && !InStr(ruleInfo.statusRule, "evenNum")) { 204 | _.Value := ruleInfo.statusRule 205 | } 206 | _.OnEvent("Change", e_statusMode) 207 | e_statusMode(item, *) { 208 | ruleInfo.statusRule := item.value 209 | _gc.statusRule.value := 0 210 | } 211 | 212 | g.AddText("xs", " - 指定规律: ") 213 | 214 | _gc.statusRule := _ := g.AddDropDownList("yp", ["", "奇数", "偶数"]) 215 | if (ruleInfo.statusRule == "oddNum") { 216 | _.Value := 2 217 | } else if (ruleInfo.statusRule == "evenNum") { 218 | _.Value := 3 219 | } 220 | _.OnEvent("Change", e_statusRule) 221 | e_statusRule(item, *) { 222 | v := item.Value 223 | if (v == 2) { 224 | ruleInfo.statusRule := "oddNum" 225 | } else if (v == 3) { 226 | ruleInfo.statusRule := "evenNum" 227 | } else { 228 | ruleInfo.statusRule := "" 229 | } 230 | _gc.statusNum.value := "" 231 | } 232 | 233 | g.addText("xs", "4. 切换码规则: ") 234 | g.AddText("xs", " - 指定数字: ") 235 | _gc.conversionNum := _ := g.AddEdit("yp", "") 236 | if (!InStr(ruleInfo.conversionRule, "oddNum") && !InStr(ruleInfo.conversionRule, "evenNum")) { 237 | _.Value := ruleInfo.conversionRule 238 | } 239 | _.OnEvent("Change", e_conversionMode) 240 | e_conversionMode(item, *) { 241 | ruleInfo.conversionRule := item.value 242 | _gc.conversionRule.value := 0 243 | } 244 | 245 | g.AddText("xs", " - 指定规律: ") 246 | _gc.conversionRule := _ := g.AddDropDownList("yp", ["", "奇数", "偶数"]) 247 | if (ruleInfo.conversionRule == "oddNum") { 248 | _.Value := 2 249 | } else if (ruleInfo.conversionRule == "evenNum") { 250 | _.Value := 3 251 | } 252 | _.OnEvent("Change", e_conversionRule) 253 | e_conversionRule(item, *) { 254 | v := item.Value 255 | if (v == 2) { 256 | ruleInfo.conversionRule := "oddNum" 257 | } else if (v == 3) { 258 | ruleInfo.conversionRule := "evenNum" 259 | } else { 260 | ruleInfo.conversionRule := "" 261 | } 262 | _gc.conversionNum.value := "" 263 | } 264 | 265 | g.AddButton("xs w" bw, "完成" typeText).OnEvent("Click", e_set) 266 | e_set(*) { 267 | g.Destroy() 268 | 269 | ; 状态码 270 | sm := ruleInfo.statusRule 271 | ; 切换码 272 | cm := ruleInfo.conversionRule 273 | ; 输入法状态 274 | status := ruleInfo.status 275 | 276 | if (add) { 277 | modeRules.InsertAt(_gc.order, sm "*" cm "*" status) 278 | } else { 279 | if (_gc.order != RowNumber) { 280 | modeRules.RemoveAt(RowNumber) 281 | modeRules.InsertAt(_gc.order, sm "*" cm "*" status) 282 | } else { 283 | modeRules[RowNumber] := sm "*" cm "*" status 284 | } 285 | } 286 | 287 | global modeRule := arrJoin(modeRules, ":") 288 | writeIni("modeRule", modeRule, "InputMethod") 289 | fn_reloading_LV(LV) 290 | } 291 | if (!add) { 292 | g.AddButton("xs w" bw, "删除此条规则").OnEvent("Click", e_del) 293 | e_del(*) { 294 | g.Destroy() 295 | LV.Delete(RowNumber) 296 | autoHdrLV(LV) 297 | modeRules.RemoveAt(RowNumber) 298 | global modeRule := arrJoin(modeRules, ":") 299 | writeIni("modeRule", modeRule, "InputMethod") 300 | fn_reloading_LV(LV) 301 | } 302 | } 303 | 304 | return g 305 | } 306 | } 307 | 308 | fn_reloading_LV(LV) { 309 | LV.Delete() 310 | LV.Opt("-Redraw") 311 | 312 | for i, v in modeRules { 313 | r := StrSplit(v, "*") 314 | LV.Add(, i, generateCol(r*)*) 315 | } 316 | LV.Opt("+Redraw") 317 | autoHdrLV(LV) 318 | } 319 | 320 | /** 321 | * 生成列信息 322 | * @param sm 状态码 323 | * @param cm 切换码 324 | * @param status 输入法状态 325 | * @returns {Array} 列信息 326 | */ 327 | generateCol(sm, cm, status) { 328 | colList := [] 329 | colList.Push(sm == "oddNum" ? "奇数" : sm == "evenNum" ? "偶数" : sm) 330 | colList.Push(cm == "oddNum" ? "奇数" : cm == "evenNum" ? "偶数" : cm) 331 | colList.Push(status ? "中文状态" : "英文状态") 332 | return colList 333 | } 334 | 335 | gc.status_btn := g.AddButton("xs w" w, "显示实时的状态码和切换码(双击设置快捷键)") 336 | gc.status_btn.OnEvent("Click", showCode) 337 | gc.status_btn.OnEvent("DoubleClick", showCodeHotkey) 338 | showCodeHotkey(*) { 339 | gc.status_btn.Text := "显示实时的状态码和切换码(双击设置快捷键)" 340 | gc.timer := 0 341 | 342 | setHotKeyGui([{ 343 | config: "hotkey_ShowCode", 344 | tip: "显示实时的状态码和切换码" 345 | }], "显示实时的状态码和切换码") 346 | } 347 | 348 | tab.UseTab(3) 349 | g.AddText("cRed", "使用【自定义】模式之前,务必仔细阅读下方的帮助说明,查看相关链接") 350 | g.AddEdit("Section r14 ReadOnly w" w, "1. 为什么需要【自定义】模式`n - InputTip 是通过系统返回的状态码和切换码来判断当前的输入法状态的`n - 对于多数常用的输入法来说【通用】模式是可以正常识别的`n - 但是部分输入法会使系统返回的状态码和切换码很特殊,无法统一处理`n - 在这种情况下,就需要用户通过规则来告诉 InputTip 当前的输入法状态`n - 这是最稳定的方式,如果你熟悉了它,就推荐使用它而不是【通用】`n`n2. 【自定义】模式的工作机制`n - InputTip 会从系统获取到当前的状态码和切换码,通过规则列表进行顺序匹配`n - 每一条规则对应一种输入法状态,如果匹配成功,则判断为此状态`n - 如果都没有匹配成功,则使用默认状态`n - 因此,如果你同时使用多个输入法,可以尝试通过【自定义】模式实现兼容`n`n3. 配置项 —— 默认状态`n - 它实际上就是用于兜底的`n - 如果规则列表中的所有规则都没有匹配成功,就会使用这个默认状态`n`n4. 规则列表`n - 规则列表就是上方的【自定义】标签页中的表格`n - 添加规则: 点击【添加规则】按钮,在菜单中进行规则设置`n - 修改规则: 双击规则列表中已经存在的任意一条规则,在菜单中进行规则修改`n - 删除规则: 双击规则列表中已经存在的任意一条规则,在菜单中点击【删除此条规则】`n`n5. 规则设置`n - 点击【显示实时的状态码和切换码】,通过切换输入法状态,查看不同状态的区别`n - 当点击【添加规则】按钮后,会出现一个规则设置菜单`n - 菜单中包含 4 个设置: 匹配的顺序、输入法状态、状态码规则、切换码规则`n`n6. 规则设置 —— 匹配的顺序`n - 指定这一条规则在规则列表中的顺序`n`n7. 规则设置 —— 输入法状态`n - 它用来指定这一条规则对应的输入法状态`n - 当这一条规则匹配成功后,InputTip 就会判定当前输入法状态为这一状态`n`n8. 规则设置 —— 状态码规则、切换码规则`n - 有两种形式可以选择: 指定数字或指定规律`n - 这两种形式只能选择其中一种,它们会在下方进行详细解释`n - 需要注意的是,你可以同时设置状态码规则和切换码规则`n - 如果同时设置,则表示此条规则需要状态码规则和切换码规则同时匹配成功`n`n9. 规则设置 —— 指定数字`n - 你可以填入一个或多个数字,只要其中有一个数字匹配成功即可`n - 如果是多个数字,需要使用 / 连接 (如: 1/3/5)`n - 如: 你希望当状态码为 1 时匹配到这条规则,在【状态码规则】中填入 1 即可`n - 如: 你希望当切换码为 1 或 3 时匹配到这条规则,在【切换码规则】中填入 1/3 即可`n`n10. 规则设置 —— 指定规律`n - 由于部分输入法会使系统返回的状态码和切换码很特殊,呈现某种规律`n - 比如随机奇数,这种情况无法通过指定数字来表示,因为不可能填入所有的奇数`n - 对于这种情况,就可以通过指定规律来实现,在下拉列表中选择对应规律即可`n - 如: 你希望当状态码为随机奇数时匹配到这条规则,选择【奇数】即可") 351 | g.AddLink(, '相关链接: 自定义模式 输入法兼容情况') 352 | 353 | tab.UseTab(4) 354 | g.AddEdit("Section r15 ReadOnly w" w, "1. 配置项 —— 实现切换输入法状态的方式`n - 配置项有以下可选值,只建议使用 LShift 或 RShift`n - 模拟输入 LShift`n - 模拟输入 RShift`n - 模拟输入 Ctrl+Space`n - 内部调用 DLL`n - 以下功能需要切换输入法状态`n -【状态切换快捷键】`n -【指定窗口自动切换状态】`n`n2. 可选值 —— 模拟输入 LShift`n - 当需要切换输入法状态时,InputTip 会模拟输入 LShift 键`n - 这相当于 InputTip 帮你按了一次键盘左边的 Shift 键`n - 注意:状态切换生效的前提是,当前场景中输入法可以使用 LShift 键成功切换状态`n`n3. 可选值 —— 模拟输入 RShift`n - 和 LShift 类似,只是会模拟输入键盘右边的 Shift 键`n`n4. 可选值 —— 模拟输入 Ctrl+Space`n - 和 LShift、RShift 类似,只是会模拟输入组合快捷键 Ctrl + Space (空格)`n - 如果 LShift、RShift 是可用的,就不建议使用它`n - 如果必须使用它,请自行检查是否存在快捷键冲突,因为它可能触发意外的 Space`n - 例如,任务栏中有个操作中心,有 WiFi/蓝牙/声音等快捷设置`n - 点开它时,就可能判定为窗口切换,触发 Ctrl + Space,导致 WiFi 自动关闭`n`n5. 可选值 —— 内部调用 DLL`n - 不建议使用它,除非是特殊情况,其他几种都无法正常使用`n - 当需要切换输入法状态时,InputTip 会直接调用系统 DLL 接口去设置输入法状态`n - 它是一种强制的设置行为,可能会导致以下问题,不建议使用`n - 对于部分输入法可能无效`n - DLL 调用行为有概率对输入法本身产生副作用`n - 当需要设置为中文状态时,没有中文状态的键盘(如: 美式键盘),也会被强行设置`n - 这可能会导致基于此的鼠标样式和符号显示错误") 355 | g.AddLink(, '相关链接: 输入法兼容情况') 356 | g.OnEvent("Close", e_close) 357 | e_close(*) { 358 | g.Destroy() 359 | gc.timer := 0 360 | } 361 | return g 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/menu/app-offset.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | fn_app_offset(*) { 4 | args := { 5 | title: "特殊偏移量", 6 | tab: "特殊偏移量列表", 7 | config: "App-Offset", 8 | about: '1. 简要说明`n - 这个菜单用来配置【特殊偏移量列表】的匹配规则`n - 下方是对应的规则列表`n - 双击列表中的任意一行,进行编辑或删除`n - 如果需要添加,请查看下方按钮相关的使用说明`n`n2. 规则列表 —— 进程名称`n - 应用窗口实际的进程名称`n`n3. 规则列表 —— 匹配范围`n - 【进程级】或【标题级】`n - 【进程级】: 只要在这个进程中时,就会触发`n - 【标题级】: 只有在这个进程中,且标题匹配成功时,才会触发`n`n4. 规则列表 —— 匹配模式`n - 它控制标题匹配的模式,只有当匹配范围为【标题级】时,才会生效`n - 目前在【特殊偏移量】中只能使用【相等】`n - 【相等】: 只有窗口标题和指定的标题完全一致,才会触发`n - 【正则】: 使用正则表达式匹配标题,匹配成功则触发自动切换`n`n5. 规则列表 —— 匹配标题`n - 只有当匹配范围为【标题级】时,才会生效`n - 指定一个标题或者正则表达式,与【匹配模式】相对应`n - 如果不知道当前窗口的相关信息(进程/标题等),可以通过以下方式获取`n - 【托盘菜单】=>【获取窗口信息】`n`n6. 规则列表 —— 特殊偏移量`n - 这里显示的是特殊偏移量的原始字符串格式: 屏幕标识/X/Y,并以 | 分割`n - 举个例子: 1/10/20|2/30/40`n - 在屏幕 1 中,水平偏移 10,垂直偏移 20`n - 在屏幕 2 中,水平偏移 30,垂直偏移 40`n - 你也不需要纠结这个格式,因为当你双击进行编辑时,会通过配置菜单来设置它`n`n - 需要特别注意:`n - 如果是 JAB 程序的【特殊偏移量】设置,需要保存修改后才会生效`n - 保存操作:点击【完成添加】【完成编辑】或其他相关按钮`n`n7. 规则列表 —— 创建时间`n - 它是每条规则的创建时间`n`n8. 规则列表 —— 操作`n - 双击列表中的任意一行,进行编辑或删除`n`n9. 按钮 —— 快捷添加`n - 点击它,可以添加一条新的规则`n - 它会弹出一个新的菜单页面,会显示当前正在运行的【应用进程列表】`n - 你可以双击【应用进程列表】中的任意一行进行快速添加`n - 详细的使用说明请参考弹出的菜单页面中的【关于】`n`n10. 按钮 —— 手动添加`n - 点击它,可以添加一条新的规则`n - 它会直接弹出添加窗口,你需要手动填写进程名称、标题、偏移量等信息`n`n11. 按钮 —— 设置不同屏幕下的基础偏移量`n - 它会弹出一个新的配置界面,用来设置不同屏幕下的基础偏移量`n - 偏移量的计算方式: 符号偏移量 + 不同屏幕的基础偏移量 + 特殊偏移量`n - 符号偏移量: 所使用的符号(图片/方块/文本)的偏移量配置`n`n12. 如何设置偏移量?`n - 当双击任意应用进程后,会弹出偏移量设置窗口`n - 通过屏幕标识和坐标信息,判断是哪一块屏幕,然后设置对应的偏移量`n - 如何通过坐标信息判断屏幕?`n - 假设你有两块屏幕,主屏幕在左边,副屏幕在右边`n - 那么副屏幕的左上角 X 坐标一定大于或等于主屏幕的右下角 X 坐标', 9 | link: '相关链接: 特殊偏移量', 10 | } 11 | 12 | showGui() 13 | showGui(deep := 0) { 14 | createUniqueGui(commonGui).Show() 15 | commonGui(info) { 16 | g := createGuiOpt("InputTip - " args.title) 17 | tab := g.AddTab3("-Wrap", [args.tab, "关于"]) 18 | tab.UseTab(1) 19 | g.AddLink("Section cRed", gui_help_tip) 20 | 21 | if (info.i) { 22 | g.AddText(, gui_width_line) 23 | return g 24 | } 25 | w := info.w 26 | bw := w - g.MarginX * 2 27 | 28 | LV := "LV_" A_Now 29 | 30 | gc.%LV% := g.AddListView("xs -LV0x10 -Multi r7 NoSortHdr Sort Grid w" w, ["进程名称", "匹配范围", "匹配模式", "匹配标题", "特殊偏移量", "创建时间"]) 31 | 32 | gc.%LV%.Opt("-Redraw") 33 | for v in AppOffset { 34 | kv := StrSplit(v, "=", , 2) 35 | part := StrSplit(kv[2], ":", , 5) 36 | if (part.Length >= 2) { 37 | name := part[1] 38 | isGlobal := part[2] 39 | ; isRegex := "" 40 | title := "" 41 | offset := "" 42 | if (part.Length == 5) { 43 | ; isRegex := part[3] 44 | offset := part[4] 45 | title := part[5] 46 | } 47 | 48 | tipGlobal := isGlobal ? "进程级" : "标题级" 49 | ; tipRegex := isRegex ? "正则" : "相等" 50 | tipRegex := "相等" 51 | gc.%LV%.Add(, name, tipGlobal, tipRegex, title, offset, kv[1]) 52 | } else { 53 | IniDelete("InputTip.ini", args.config, kv[1]) 54 | } 55 | } 56 | gc.%LV%.Opt("+Redraw") 57 | autoHdrLV(gc.%LV%) 58 | 59 | gc.%LV%.OnEvent("DoubleClick", handleClick) 60 | gc.%LV%._LV := LV 61 | gc.%LV%._config := args.config 62 | _ := g.AddButton("xs w" w, "快捷添加") 63 | _.OnEvent("Click", e_add) 64 | _._LV := LV 65 | _._config := args.config 66 | _._parentTitle := args.title 67 | e_add(item, *) { 68 | try { 69 | fn_add(item._LV, item._config, item._parentTitle) 70 | } catch { 71 | fn_add(item._LV, item._config, "") 72 | } 73 | } 74 | 75 | _ := g.AddButton("xs w" w, "手动添加") 76 | _.OnEvent("Click", e_add_manually) 77 | _._LV := LV 78 | _._config := args.config 79 | 80 | e_add_manually(item, *) { 81 | itemValue := { 82 | exe_name: "", 83 | tipGlobal: "进程级", 84 | tipRegex: "相等", 85 | title: "", 86 | id: returnId(), 87 | configName: item._config 88 | } 89 | fn_edit(gc.%item._LV%, 1, "add", itemValue).Show() 90 | } 91 | 92 | g.AddButton("xs w" w, "设置不同屏幕下的基础偏移量").OnEvent("Click", e_baseOffset) 93 | e_baseOffset(*) { 94 | createUniqueGui(offsetScreenGui).Show() 95 | offsetScreenGui(info) { 96 | g := createGuiOpt("InputTip - 设置不同屏幕下的基础偏移量") 97 | g.AddText("Section cRed", "- 设置不同屏幕下的基础偏移量`n- 点击右上角的 x 关闭此窗口后,基础偏移量才会完全生效") 98 | pages := [] 99 | for v in screenList { 100 | pages.push("屏幕 " v.num) 101 | } 102 | tab := g.AddTab3("xs -Wrap", pages) 103 | 104 | for v in screenList { 105 | tab.UseTab(v.num) 106 | if (v.num = v.main) { 107 | g.AddText(, "这是主屏幕(主显示器),屏幕标识: " v.num) 108 | } else { 109 | g.AddText(, "这是副屏幕(副显示器),屏幕标识: " v.num) 110 | } 111 | 112 | g.AddText(, "屏幕坐标信息(X,Y): 左上角(" v.left ", " v.top "),右下角(" v.right ", " v.bottom ")") 113 | 114 | try { 115 | x := app_offset_screen.%v.num%.x 116 | y := app_offset_screen.%v.num%.y 117 | } catch { 118 | app_offset_screen.%v.num% := { x: 0, y: 0 } 119 | x := 0, y := 0 120 | } 121 | 122 | g.AddText("Section", "水平方向的偏移量: ") 123 | _ := g.AddEdit("yp") 124 | _.Value := x 125 | _.__num := v.num 126 | _.OnEvent("Change", e_change_offset_x) 127 | e_change_offset_x(item, *) { 128 | static db := debounce((config, value) => ( 129 | writeIni(config, value, "App-Offset-Screen"), 130 | updateAppOffset(), 131 | restartJAB() 132 | )) 133 | 134 | if (item.value == "") { 135 | item.value := 0 136 | } 137 | 138 | value := returnNumber(item.value) 139 | app_offset_screen.%item.__num%.x := value 140 | db(item.__num, value "/" app_offset_screen.%item.__num%.y) 141 | } 142 | g.AddText("xs", "垂直方向的偏移量: ") 143 | _ := g.AddEdit("yp") 144 | _.Value := y 145 | _.__num := v.num 146 | _.OnEvent("Change", e_change_offset_y) 147 | e_change_offset_y(item, *) { 148 | static db := debounce((config, value) => ( 149 | writeIni(config, value, "App-Offset-Screen"), 150 | updateAppOffset(), 151 | restartJAB() 152 | )) 153 | 154 | if (item.value == "") { 155 | item.value := 0 156 | } 157 | 158 | value := returnNumber(item.value) 159 | app_offset_screen.%item.__num%.y := value 160 | db(item.__num, app_offset_screen.%item.__num%.x "/" value) 161 | } 162 | } 163 | tab.UseTab(0) 164 | g.AddText("Section cGray", "如果不知道如何区分屏幕,可查看【特殊偏移量】中的【关于】") 165 | 166 | if (info.i) { 167 | return g 168 | } 169 | 170 | g.OnEvent("Close", close) 171 | close(*) { 172 | g.Destroy() 173 | for k, v in app_offset_screen.OwnProps() { 174 | writeIni(k, v.x "/" v.y, "App-Offset-Screen") 175 | } 176 | updateAppOffset() 177 | restartJAB() 178 | } 179 | return g 180 | } 181 | } 182 | 183 | handleClick(LV, RowNumber) { 184 | if (!RowNumber) { 185 | return 186 | } 187 | exe_name := LV.GetText(RowNumber) 188 | if (gc.w.subGui) { 189 | gc.w.subGui.Destroy() 190 | gc.w.subGui := "" 191 | } 192 | 193 | itemValue := { 194 | exe_name: LV.GetText(RowNumber, 1), 195 | tipGlobal: LV.GetText(RowNumber, 2), 196 | tipRegex: LV.GetText(RowNumber, 3), 197 | title: LV.GetText(RowNumber, 4), 198 | offset: LV.GetText(RowNumber, 5), 199 | id: LV.GetText(RowNumber, 6), 200 | configName: LV._config 201 | } 202 | createGui(editGui).Show() 203 | editGui(info) { 204 | return fn_edit(gc.%LV._LV%, RowNumber, "edit", itemValue) 205 | } 206 | } 207 | 208 | fn_edit(LV, RowNumber, action, itemValue) { 209 | ; 是否自动添加到符号的白名单中 210 | needAddWhiteList := 1 211 | 212 | if (action == "edit") { 213 | actionText := "编辑" 214 | } else { 215 | actionText := "添加" 216 | } 217 | 218 | label := "正在" actionText "规则" 219 | 220 | g := createGuiOpt("InputTip - " label) 221 | 222 | if (info.i) { 223 | g.AddText(, gui_width_line) 224 | return g 225 | } 226 | w := info.w 227 | bw := w - g.MarginX * 2 228 | 229 | if (action != "edit") { 230 | g.AddText("cRed", "是否添加到【符号的白名单】中: ") 231 | _ := g.AddDropDownList("yp", ["【否】不添加", "【是】自动添加"]) 232 | _.Value := needAddWhiteList + 1 233 | _.OnEvent("Change", e_change) 234 | e_change(item, *) { 235 | needAddWhiteList := item.value - 1 236 | } 237 | g.AddText("xs cGray", "如果选择【是】,且它在白名单中不存在,将以【进程级】自动添加") 238 | } 239 | 240 | scaleWidth := bw / 1.5 241 | 242 | g.AddText(, "1. 进程名称: ") 243 | _ := g.AddEdit("yp w" scaleWidth, "") 244 | _.Text := itemValue.exe_name 245 | _.OnEvent("Change", e_changeName) 246 | e_changeName(item, *) { 247 | v := item.Text 248 | itemValue.exe_name := v 249 | } 250 | 251 | g.AddText("xs", "2. 匹配范围: ") 252 | _ := g.AddDropDownList("yp w" scaleWidth, ["进程级", "标题级"]) 253 | _.Text := itemValue.tipGlobal 254 | _.OnEvent("Change", e_changeLevel) 255 | e_changeLevel(item, *) { 256 | v := item.Text 257 | itemValue.tipGlobal := v 258 | } 259 | 260 | g.AddText("xs cGray", "【匹配模式】和【匹配标题】仅在【匹配范围】为【标题级】时有效") 261 | g.AddText("xs", "3. 匹配模式: ") 262 | _ := g.AddDropDownList("Disabled yp w" scaleWidth, ["相等", "正则"]) 263 | _.Text := itemValue.tipRegex 264 | _.OnEvent("Change", e_changeMatch) 265 | e_changeMatch(item, *) { 266 | v := item.Text 267 | itemValue.tipRegex := v 268 | } 269 | 270 | g.AddText("xs", "4. 匹配标题: ") 271 | _ := g.AddEdit("yp w" scaleWidth) 272 | _.Text := itemValue.title 273 | _.OnEvent("Change", e_changeTitle) 274 | e_changeTitle(item, *) { 275 | v := item.Text 276 | itemValue.title := v 277 | } 278 | 279 | screenList := getScreenInfo() 280 | app := itemValue.exe_name 281 | 282 | pages := [] 283 | for v in screenList { 284 | pages.push("屏幕 " v.num) 285 | } 286 | tab := g.AddTab3("xs -Wrap w" bw / 1.2, pages) 287 | key := itemValue.tipGlobal == "进程级" ? app : app itemValue.title 288 | 289 | try { 290 | _ := app_offset.%key% 291 | } catch { 292 | app_offset.%key% := {} 293 | } 294 | 295 | for v in screenList { 296 | tab.UseTab(v.num) 297 | if (v.num = v.main) { 298 | g.AddText(, "这是主屏幕(主显示器),屏幕标识: " v.num) 299 | } else { 300 | g.AddText(, "这是副屏幕(副显示器),屏幕标识: " v.num) 301 | } 302 | 303 | g.AddText(, "屏幕坐标信息(X,Y): 左上角(" v.left ", " v.top "),右下角(" v.right ", " v.bottom ")") 304 | 305 | x := 0, y := 0 306 | 307 | if (action == "edit") { 308 | try { 309 | x := app_offset.%key%.%v.num%.x 310 | y := app_offset.%key%.%v.num%.y 311 | } catch { 312 | app_offset.%key%.%v.num% := { x: 0, y: 0 } 313 | } 314 | } else { 315 | app_offset.%key%.%v.num% := { x: 0, y: 0 } 316 | } 317 | 318 | g.AddText("Section", "水平方向的偏移量: ") 319 | _ := g.AddEdit("yp") 320 | _.Value := x 321 | _.__num := v.num 322 | _._itemValue := itemValue 323 | _.OnEvent("Change", e_change_offset_x) 324 | _.OnEvent("LoseFocus", e_change_offset_x) 325 | g.AddText("xs", "垂直方向的偏移量: ") 326 | _ := g.AddEdit("yp") 327 | _.Value := y 328 | _.__num := v.num 329 | _._itemValue := itemValue 330 | _.OnEvent("Change", e_change_offset_y) 331 | _.OnEvent("LoseFocus", e_change_offset_y) 332 | } 333 | e_change_offset_x(item, *) { 334 | itemValue := item._itemValue 335 | key := itemValue.tipGlobal == "进程级" ? app : app itemValue.title 336 | try { 337 | app_offset.%key%.%item.__num%.x := returnNumber(item.value) 338 | } catch { 339 | return 340 | } 341 | 342 | if (item.Focused) { 343 | return 344 | } 345 | 346 | itemValue.offset := "" 347 | for v in app_offset.%key%.OwnProps() { 348 | itemValue.offset .= "|" v "/" app_offset.%key%.%v%.x "/" app_offset.%key%.%v%.y 349 | } 350 | itemValue.offset := SubStr(itemValue.offset, 2) 351 | } 352 | e_change_offset_y(item, *) { 353 | itemValue := item._itemValue 354 | key := itemValue.tipGlobal == "进程级" ? app : app itemValue.title 355 | try { 356 | app_offset.%key%.%item.__num%.y := returnNumber(item.value) 357 | } catch { 358 | return 359 | } 360 | 361 | if (item.Focused) { 362 | return 363 | } 364 | 365 | itemValue.offset := "" 366 | for v in app_offset.%key%.OwnProps() { 367 | itemValue.offset .= "|" v "/" app_offset.%key%.%v%.x "/" app_offset.%key%.%v%.y 368 | } 369 | itemValue.offset := SubStr(itemValue.offset, 2) 370 | } 371 | tab.UseTab(0) 372 | g.AddText("Section cGray", "如果不知道如何区分屏幕,可查看【特殊偏移量】中的【关于】") 373 | 374 | g.AddButton("Section w" bw / 1.2, "完成" actionText).OnEvent("Click", e_set) 375 | e_set(*) { 376 | fn_set(action, 0) 377 | } 378 | if (action == "edit") { 379 | g.AddButton("xs w" bw / 1.2, "删除它").OnEvent("Click", e_delete) 380 | e_delete(*) { 381 | fn_set(action, 1) 382 | } 383 | } 384 | 385 | fn_set(action, delete) { 386 | g.Destroy() 387 | 388 | if (delete) { 389 | try { 390 | IniDelete("InputTip.ini", itemValue.configName, itemValue.id) 391 | LV.Delete(RowNumber) 392 | } 393 | } else { 394 | isGlobal := itemValue.tipGlobal == "进程级" ? 1 : 0 395 | ; isRegex := itemValue.tipRegex == "正则" ? 1 : 0 396 | isRegex := 0 397 | try { 398 | _ := itemValue.offset 399 | } catch { 400 | itemValue.offset := "" 401 | screenList := getScreenInfo() 402 | for v in screenList { 403 | itemValue.offset .= "|" v.num "/0/0" 404 | } 405 | itemValue.offset := SubStr(itemValue.offset, 2) 406 | } 407 | 408 | value := itemValue.exe_name ":" isGlobal ":" isRegex ":" itemValue.offset ":" itemValue.title 409 | writeIni(itemValue.id, value, itemValue.configName, "InputTip.ini") 410 | 411 | if (action == "edit") { 412 | LV.Modify(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.offset, itemValue.id) 413 | } else { 414 | LV.Insert(RowNumber, , itemValue.exe_name, itemValue.tipGlobal, itemValue.tipRegex, itemValue.title, itemValue.offset, itemValue.id) 415 | } 416 | 417 | if (needAddWhiteList) { 418 | updateWhiteList(itemValue.exe_name) 419 | } 420 | } 421 | 422 | autoHdrLV(LV) 423 | 424 | updateAppOffset() 425 | restartJAB() 426 | } 427 | return g 428 | } 429 | 430 | fn_add(parentLV, configName, parentTitle) { 431 | args := { 432 | title: parentTitle " - 快捷添加", 433 | configName: configName, 434 | LV: parentLV, 435 | } 436 | createProcessListGui(args, addClick, e_add_manually) 437 | 438 | addClick(args) { 439 | windowInfo := args.windowInfo 440 | RowNumber := args.RowNumber 441 | 442 | itemValue := { 443 | exe_name: windowInfo.exe_name, 444 | tipGlobal: "进程级", 445 | tipRegex: "相等", 446 | title: windowInfo.title, 447 | id: windowInfo.id, 448 | configName: args.parentArgs.configName 449 | } 450 | fn_edit(gc.%args.parentArgs.LV%, RowNumber, "add", itemValue).Show() 451 | } 452 | 453 | e_add_manually(args) { 454 | windowInfo := args.windowInfo 455 | 456 | itemValue := { 457 | exe_name: windowInfo.exe_name, 458 | tipGlobal: "进程级", 459 | tipRegex: "相等", 460 | title: windowInfo.title, 461 | offset: "", 462 | id: windowInfo.id, 463 | configName: args.parentArgs.configName 464 | } 465 | fn_edit(gc.%args.parentArgs.LV%, 1, "add", itemValue).Show() 466 | } 467 | } 468 | tab.UseTab(2) 469 | g.AddEdit("Section r15 w" w, args.about) 470 | g.AddLink(, args.link) 471 | return g 472 | } 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /src/utils/verify-file.ahk: -------------------------------------------------------------------------------- 1 | ; InputTip 2 | 3 | baseUrl := ["https://gitee.com/abgox/InputTip/raw/main/", "https://github.com/abgox/InputTip/raw/main/"] 4 | 5 | dirList := ["plugins", "InputTipIcon", "InputTipIcon/default", "InputTipSymbol", "InputTipSymbol/default", "InputTipCursor", "InputTipCursor/default", "InputTipCursor/default/oreo-red", "InputTipCursor/default/oreo-blue", "InputTipCursor/default/oreo-green"] 6 | 7 | for d in dirList { 8 | if (!DirExist(d)) { 9 | DirCreate(d) 10 | } 11 | } 12 | 13 | if (A_IsCompiled) { 14 | if (!FileExist("InputTipIcon/default/app.png")) { 15 | FileInstall("InputTipIcon/default/app.png", "InputTipIcon/default/app.png", 1) 16 | } 17 | if (!FileExist("InputTipIcon/default/app-paused.png")) { 18 | FileInstall("InputTipIcon/default/app-paused.png", "InputTipIcon/default/app-paused.png", 1) 19 | } 20 | 21 | if (!FileExist("InputTipSymbol/default/triangle-red.png")) { 22 | FileInstall("InputTipSymbol/default/triangle-red.png", "InputTipSymbol/default/triangle-red.png", 1) 23 | } 24 | if (!FileExist("InputTipSymbol/default/triangle-blue.png")) { 25 | FileInstall("InputTipSymbol/default/triangle-blue.png", "InputTipSymbol/default/triangle-blue.png", 1) 26 | } 27 | if (!FileExist("InputTipSymbol/default/triangle-green.png")) { 28 | FileInstall("InputTipSymbol/default/triangle-green.png", "InputTipSymbol/default/triangle-green.png", 1) 29 | } 30 | 31 | if (!FileExist("InputTipCursor/default/oreo-red/AppStarting.ani")) { 32 | FileInstall("InputTipCursor/default/oreo-red/AppStarting.ani", "InputTipCursor/default/oreo-red/AppStarting.ani", 1) 33 | } 34 | if (!FileExist("InputTipCursor/default/oreo-red/Arrow.cur")) { 35 | FileInstall("InputTipCursor/default/oreo-red/Arrow.cur", "InputTipCursor/default/oreo-red/Arrow.cur", 1) 36 | } 37 | if (!FileExist("InputTipCursor/default/oreo-red/Cross.cur")) { 38 | FileInstall("InputTipCursor/default/oreo-red/Cross.cur", "InputTipCursor/default/oreo-red/Cross.cur", 1) 39 | } 40 | if (!FileExist("InputTipCursor/default/oreo-red/Hand.cur")) { 41 | FileInstall("InputTipCursor/default/oreo-red/Hand.cur", "InputTipCursor/default/oreo-red/Hand.cur", 1) 42 | } 43 | if (!FileExist("InputTipCursor/default/oreo-red/Help.cur")) { 44 | FileInstall("InputTipCursor/default/oreo-red/Help.cur", "InputTipCursor/default/oreo-red/Help.cur", 1) 45 | } 46 | if (!FileExist("InputTipCursor/default/oreo-red/IBeam.cur")) { 47 | FileInstall("InputTipCursor/default/oreo-red/IBeam.cur", "InputTipCursor/default/oreo-red/IBeam.cur", 1) 48 | } 49 | if (!FileExist("InputTipCursor/default/oreo-red/No.cur")) { 50 | FileInstall("InputTipCursor/default/oreo-red/No.cur", "InputTipCursor/default/oreo-red/No.cur", 1) 51 | } 52 | if (!FileExist("InputTipCursor/default/oreo-red/Pen.cur")) { 53 | FileInstall("InputTipCursor/default/oreo-red/Pen.cur", "InputTipCursor/default/oreo-red/Pen.cur", 1) 54 | } 55 | if (!FileExist("InputTipCursor/default/oreo-red/SizeAll.cur")) { 56 | FileInstall("InputTipCursor/default/oreo-red/SizeAll.cur", "InputTipCursor/default/oreo-red/SizeAll.cur", 1) 57 | } 58 | if (!FileExist("InputTipCursor/default/oreo-red/SizeNESW.cur")) { 59 | FileInstall("InputTipCursor/default/oreo-red/SizeNESW.cur", "InputTipCursor/default/oreo-red/SizeNESW.cur", 1) 60 | } 61 | if (!FileExist("InputTipCursor/default/oreo-red/SizeNS.cur")) { 62 | FileInstall("InputTipCursor/default/oreo-red/SizeNS.cur", "InputTipCursor/default/oreo-red/SizeNS.cur", 1) 63 | } 64 | if (!FileExist("InputTipCursor/default/oreo-red/SizeNWSE.cur")) { 65 | FileInstall("InputTipCursor/default/oreo-red/SizeNWSE.cur", "InputTipCursor/default/oreo-red/SizeNWSE.cur", 1) 66 | } 67 | if (!FileExist("InputTipCursor/default/oreo-red/SizeWE.cur")) { 68 | FileInstall("InputTipCursor/default/oreo-red/SizeWE.cur", "InputTipCursor/default/oreo-red/SizeWE.cur", 1) 69 | } 70 | if (!FileExist("InputTipCursor/default/oreo-red/UpArrow.cur")) { 71 | FileInstall("InputTipCursor/default/oreo-red/UpArrow.cur", "InputTipCursor/default/oreo-red/UpArrow.cur", 1) 72 | } 73 | if (!FileExist("InputTipCursor/default/oreo-red/Wait.ani")) { 74 | FileInstall("InputTipCursor/default/oreo-red/Wait.ani", "InputTipCursor/default/oreo-red/Wait.ani", 1) 75 | } 76 | 77 | if (!FileExist("InputTipCursor/default/oreo-blue/AppStarting.ani")) { 78 | FileInstall("InputTipCursor/default/oreo-blue/AppStarting.ani", "InputTipCursor/default/oreo-blue/AppStarting.ani", 1) 79 | } 80 | if (!FileExist("InputTipCursor/default/oreo-blue/Arrow.cur")) { 81 | FileInstall("InputTipCursor/default/oreo-blue/Arrow.cur", "InputTipCursor/default/oreo-blue/Arrow.cur", 1) 82 | } 83 | if (!FileExist("InputTipCursor/default/oreo-blue/Cross.cur")) { 84 | FileInstall("InputTipCursor/default/oreo-blue/Cross.cur", "InputTipCursor/default/oreo-blue/Cross.cur", 1) 85 | } 86 | if (!FileExist("InputTipCursor/default/oreo-blue/Hand.cur")) { 87 | FileInstall("InputTipCursor/default/oreo-blue/Hand.cur", "InputTipCursor/default/oreo-blue/Hand.cur", 1) 88 | } 89 | if (!FileExist("InputTipCursor/default/oreo-blue/Help.cur")) { 90 | FileInstall("InputTipCursor/default/oreo-blue/Help.cur", "InputTipCursor/default/oreo-blue/Help.cur", 1) 91 | } 92 | if (!FileExist("InputTipCursor/default/oreo-blue/IBeam.cur")) { 93 | FileInstall("InputTipCursor/default/oreo-blue/IBeam.cur", "InputTipCursor/default/oreo-blue/IBeam.cur", 1) 94 | } 95 | if (!FileExist("InputTipCursor/default/oreo-blue/No.cur")) { 96 | FileInstall("InputTipCursor/default/oreo-blue/No.cur", "InputTipCursor/default/oreo-blue/No.cur", 1) 97 | } 98 | if (!FileExist("InputTipCursor/default/oreo-blue/Pen.cur")) { 99 | FileInstall("InputTipCursor/default/oreo-blue/Pen.cur", "InputTipCursor/default/oreo-blue/Pen.cur", 1) 100 | } 101 | if (!FileExist("InputTipCursor/default/oreo-blue/SizeAll.cur")) { 102 | FileInstall("InputTipCursor/default/oreo-blue/SizeAll.cur", "InputTipCursor/default/oreo-blue/SizeAll.cur", 1) 103 | } 104 | if (!FileExist("InputTipCursor/default/oreo-blue/SizeNESW.cur")) { 105 | FileInstall("InputTipCursor/default/oreo-blue/SizeNESW.cur", "InputTipCursor/default/oreo-blue/SizeNESW.cur", 1) 106 | } 107 | if (!FileExist("InputTipCursor/default/oreo-blue/SizeNS.cur")) { 108 | FileInstall("InputTipCursor/default/oreo-blue/SizeNS.cur", "InputTipCursor/default/oreo-blue/SizeNS.cur", 1) 109 | } 110 | if (!FileExist("InputTipCursor/default/oreo-blue/SizeNWSE.cur")) { 111 | FileInstall("InputTipCursor/default/oreo-blue/SizeNWSE.cur", "InputTipCursor/default/oreo-blue/SizeNWSE.cur", 1) 112 | } 113 | if (!FileExist("InputTipCursor/default/oreo-blue/SizeWE.cur")) { 114 | FileInstall("InputTipCursor/default/oreo-blue/SizeWE.cur", "InputTipCursor/default/oreo-blue/SizeWE.cur", 1) 115 | } 116 | if (!FileExist("InputTipCursor/default/oreo-blue/UpArrow.cur")) { 117 | FileInstall("InputTipCursor/default/oreo-blue/UpArrow.cur", "InputTipCursor/default/oreo-blue/UpArrow.cur", 1) 118 | } 119 | if (!FileExist("InputTipCursor/default/oreo-blue/Wait.ani")) { 120 | FileInstall("InputTipCursor/default/oreo-blue/Wait.ani", "InputTipCursor/default/oreo-blue/Wait.ani", 1) 121 | } 122 | 123 | if (!FileExist("InputTipCursor/default/oreo-green/AppStarting.ani")) { 124 | FileInstall("InputTipCursor/default/oreo-green/AppStarting.ani", "InputTipCursor/default/oreo-green/AppStarting.ani", 1) 125 | } 126 | if (!FileExist("InputTipCursor/default/oreo-green/Arrow.cur")) { 127 | FileInstall("InputTipCursor/default/oreo-green/Arrow.cur", "InputTipCursor/default/oreo-green/Arrow.cur", 1) 128 | } 129 | if (!FileExist("InputTipCursor/default/oreo-green/Cross.cur")) { 130 | FileInstall("InputTipCursor/default/oreo-green/Cross.cur", "InputTipCursor/default/oreo-green/Cross.cur", 1) 131 | } 132 | if (!FileExist("InputTipCursor/default/oreo-green/Hand.cur")) { 133 | FileInstall("InputTipCursor/default/oreo-green/Hand.cur", "InputTipCursor/default/oreo-green/Hand.cur", 1) 134 | } 135 | if (!FileExist("InputTipCursor/default/oreo-green/Help.cur")) { 136 | FileInstall("InputTipCursor/default/oreo-green/Help.cur", "InputTipCursor/default/oreo-green/Help.cur", 1) 137 | } 138 | if (!FileExist("InputTipCursor/default/oreo-green/IBeam.cur")) { 139 | FileInstall("InputTipCursor/default/oreo-green/IBeam.cur", "InputTipCursor/default/oreo-green/IBeam.cur", 1) 140 | } 141 | if (!FileExist("InputTipCursor/default/oreo-green/No.cur")) { 142 | FileInstall("InputTipCursor/default/oreo-green/No.cur", "InputTipCursor/default/oreo-green/No.cur", 1) 143 | } 144 | if (!FileExist("InputTipCursor/default/oreo-green/Pen.cur")) { 145 | FileInstall("InputTipCursor/default/oreo-green/Pen.cur", "InputTipCursor/default/oreo-green/Pen.cur", 1) 146 | } 147 | if (!FileExist("InputTipCursor/default/oreo-green/SizeAll.cur")) { 148 | FileInstall("InputTipCursor/default/oreo-green/SizeAll.cur", "InputTipCursor/default/oreo-green/SizeAll.cur", 1) 149 | } 150 | if (!FileExist("InputTipCursor/default/oreo-green/SizeNESW.cur")) { 151 | FileInstall("InputTipCursor/default/oreo-green/SizeNESW.cur", "InputTipCursor/default/oreo-green/SizeNESW.cur", 1) 152 | } 153 | if (!FileExist("InputTipCursor/default/oreo-green/SizeNS.cur")) { 154 | FileInstall("InputTipCursor/default/oreo-green/SizeNS.cur", "InputTipCursor/default/oreo-green/SizeNS.cur", 1) 155 | } 156 | if (!FileExist("InputTipCursor/default/oreo-green/SizeNWSE.cur")) { 157 | FileInstall("InputTipCursor/default/oreo-green/SizeNWSE.cur", "InputTipCursor/default/oreo-green/SizeNWSE.cur", 1) 158 | } 159 | if (!FileExist("InputTipCursor/default/oreo-green/SizeWE.cur")) { 160 | FileInstall("InputTipCursor/default/oreo-green/SizeWE.cur", "InputTipCursor/default/oreo-green/SizeWE.cur", 1) 161 | } 162 | if (!FileExist("InputTipCursor/default/oreo-green/UpArrow.cur")) { 163 | FileInstall("InputTipCursor/default/oreo-green/UpArrow.cur", "InputTipCursor/default/oreo-green/UpArrow.cur", 1) 164 | } 165 | if (!FileExist("InputTipCursor/default/oreo-green/Wait.ani")) { 166 | FileInstall("InputTipCursor/default/oreo-green/Wait.ani", "InputTipCursor/default/oreo-green/Wait.ani", 1) 167 | } 168 | } else { 169 | fileList := [ 170 | ; 图标 171 | "InputTipIcon/default/app.ico", 172 | "InputTipIcon/default/app.png", 173 | "InputTipIcon/default/app-paused.png", 174 | ; 图片 175 | "InputTipSymbol/default/triangle-red.png", 176 | "InputTipSymbol/default/triangle-blue.png", 177 | "InputTipSymbol/default/triangle-green.png", 178 | ; 启动脚本 179 | "../InputTip.bat", 180 | ; 脚本文件 181 | "./InputTip.JAB.JetBrains.ahk", 182 | ; menu 183 | "menu/about.ahk", 184 | "menu/app-offset.ahk", 185 | "menu/auto-exit.ahk", 186 | "menu/bw-list.ahk", 187 | "menu/check-update.ahk", 188 | "menu/cursor-mode.ahk", 189 | "menu/input-method.ahk", 190 | "menu/other-config.ahk", 191 | "menu/scheme-cursor.ahk", 192 | "menu/scheme-symbol.ahk", 193 | "menu/startup.ahk", 194 | "menu/switch-window.ahk", 195 | "menu/symbol-pos.ahk", 196 | "menu/tray-menu.ahk", 197 | ; plugins 198 | "plugins/InputTip.plugin.ahk", 199 | ; utils 200 | "utils/app-list.ahk", 201 | "utils/check-version.ahk", 202 | "utils/create-gui.ahk", 203 | "utils/hotkey-gui.ahk", 204 | "utils/IME.ahk", 205 | "utils/ini.ahk", 206 | "utils/options.ahk", 207 | "utils/show.ahk", 208 | "utils/tools.ahk", 209 | "utils/var.ahk", 210 | ] 211 | 212 | missFileList := [] 213 | 214 | for v in fileList { 215 | if (!FileExist(v)) { 216 | missFileList.Push(v) 217 | } 218 | } 219 | styleList := [ 220 | "AppStarting.ani", "Arrow.cur", "Cross.cur", "Hand.cur", "Help.cur", "IBeam.cur", "No.cur", "Pen.cur", "SizeAll.cur", "SizeNESW.cur", "SizeNS.cur", "SizeNWSE.cur", "SizeWE.cur", "UpArrow.cur", "Wait.ani" 221 | ] 222 | colorMap := Map( 223 | "CN", "red", 224 | "EN", "blue", 225 | "Caps", "green", 226 | ) 227 | 228 | for v in ["CN", "EN", "Caps"] { 229 | for s in styleList { 230 | p := "InputTipCursor/default/oreo-" colorMap[v] "/" s 231 | if (!FileExist(p)) { 232 | missFileList.Push(p) 233 | } 234 | } 235 | } 236 | if (missFileList.Length) { 237 | try { 238 | icon := A_IsPaused ? "InputTipIcon\default\app-paused.png" : "InputTipIcon\default\app.png" 239 | TraySetIcon(icon, , 1) 240 | } 241 | 242 | downloading(*) { 243 | g := Gui(, "InputTip - 正在处理文件丢失...") 244 | g.SetFont("s14", "Microsoft YaHei") 245 | g.AddText(, "正在下载丢失的文件: ") 246 | g.tip := g.AddText("xs cRed", "------------------------------------------------------------") 247 | 248 | g.AddText("xs", "------------------------------------------------------------") 249 | g.AddText("xs", "官网:") 250 | g.AddLink("yp", 'inputtip.abgox.com') 251 | g.AddText("xs", "Github:") 252 | g.AddLink("yp", 'github.com/abgox/InputTip') 253 | g.AddText("xs", "Gitee: :") 254 | g.AddLink("yp", 'gitee.com/abgox/InputTip') 255 | g.AddLink("xs", '版本更新日志: 官网 Github Gitee') 256 | g.Show() 257 | g.OnEvent("Close", downloading) 258 | return g 259 | } 260 | downloadingGui := downloading() 261 | 262 | done := 1 263 | fileCount := missFileList.Length 264 | for i, f in missFileList { 265 | for u in baseUrl { 266 | downloadingGui.tip.Text := i '/' fileCount " : " f 267 | dir := RegExReplace(f, "/[^/]*$", "") 268 | try { 269 | if (!DirExist(dir)) { 270 | DirCreate(dir) 271 | } 272 | Download(u "src/" f, f) 273 | break 274 | } 275 | } 276 | 277 | if (FileExist(f)) { 278 | if (InStr(f, ".ahk") || InStr(f, ".bat")) { 279 | try { 280 | if (!InStr(FileOpen(f, "r").ReadLine(), "InputTip")) { 281 | done := 0 282 | FileDelete(f) 283 | break 284 | } 285 | } catch { 286 | done := 0 287 | break 288 | } 289 | } 290 | } else { 291 | done := 0 292 | break 293 | } 294 | } 295 | downloadingGui.Destroy() 296 | 297 | if (done) { 298 | Run('"' A_AhkPath '" "' A_ScriptFullPath '" ' 0) 299 | ExitApp() 300 | } else { 301 | g := Gui(, "InputTip - 正在处理文件丢失...") 302 | g.SetFont("s14", "Microsoft YaHei") 303 | g.AddText("cRed", "可能因为网络等其他原因,文件没有正常恢复,请手动处理") 304 | g.AddLink("cGray", '你可以前往 官网 Github Github 手动下载') 305 | g.Show("Hide") 306 | g.GetPos(, , &_w) 307 | 308 | g := Gui(, "InputTip - 正在处理文件丢失...") 309 | g.SetFont("s12", "Microsoft YaHei") 310 | g.AddText("cRed", "可能因为网络等其他原因,文件没有正常恢复,请手动处理") 311 | g.AddLink("cGray", '你可以前往 官网 Github Github 手动下载') 312 | g.AddButton("xs w" _w, "我知道了").OnEvent("Click", (*) => (ExitApp())) 313 | g.Show() 314 | 315 | while (1) { 316 | Sleep(5000) 317 | } 318 | } 319 | } 320 | } 321 | 322 | 323 | /** 324 | * 检查文件是否存在,如果不存在,从远程获取 325 | * @param urlPath 基于 baseUrl 的相对路径 326 | * @param filePath 文件路径 327 | */ 328 | ensureFile(urlPath, filePath) { 329 | if (FileExist(filePath)) { 330 | return 331 | } 332 | done := 0 333 | for u in baseUrl { 334 | dir := RegExReplace(filePath, "/[^/]*$", "") 335 | try { 336 | if (!DirExist(dir)) { 337 | DirCreate(dir) 338 | } 339 | Download(u urlPath, filePath) 340 | done := 1 341 | break 342 | } 343 | } 344 | } 345 | 346 | /** 347 | * - 检查配置文件 348 | * - 当配置文件不存在(无法读取 Installer 中的 init 配置项)时,进入初始化引导 349 | */ 350 | checkIni() { 351 | try { 352 | IniRead("InputTip.ini", "Installer", "init") 353 | } catch { 354 | gc.init := 1 355 | 356 | writeIni("init", 1, "Installer") 357 | 358 | fz := "s14" 359 | createGui(confirmGui).Show() 360 | confirmGui(info) { 361 | g := Gui(, "InputTip - 初始化引导") 362 | g.SetFont(fz, "Microsoft YaHei") 363 | g.AddText(, "你是否希望 InputTip 使用【鼠标方案】?") 364 | g.AddText("xs cRed", "InputTip 会同时使用三套不同的鼠标样式`n然后根据不同输入法状态加载对应的鼠标样式") 365 | g.AddLink(, '详情参考【鼠标方案】: 官网 Github Gitee').Focus() 366 | 367 | if (info.i) { 368 | return g 369 | } 370 | w := info.w 371 | bw := w - g.MarginX * 2 372 | 373 | g.AddButton("xs cRed w" bw, "【是】").OnEvent("Click", e_yes) 374 | e_yes(*) { 375 | g.Destroy() 376 | createGui(yesGui).Show() 377 | yesGui(info) { 378 | g := Gui() 379 | g.SetFont(fz, "Microsoft YaHei") 380 | g.AddText(, "你真的确定要使用【鼠标方案】吗?") 381 | g.AddText("cRed", "如果误点了【是】,恢复鼠标样式需要以下步骤: `n 1. 点击【托盘菜单】中的【状态提示 - 鼠标方案】`n 2. 将【加载鼠标样式】更改为【否】") 382 | g.AddLink(, '详情参考【鼠标方案】: 官网 Github Gitee') 383 | 384 | if (info.i) { 385 | return g 386 | } 387 | w := info.w 388 | bw := w - g.MarginX * 2 389 | 390 | g.AddButton("xs cRed w" bw, "【是】").OnEvent("Click", e_yes) 391 | e_yes(*) { 392 | g.Destroy() 393 | writeIni("changeCursor", 1) 394 | global changeCursor := 1 395 | showSymbol() 396 | } 397 | g.AddButton("w" bw, "【否】").OnEvent("Click", e_no) 398 | e_no(*) { 399 | g.Destroy() 400 | writeIni("changeCursor", 0) 401 | global changeCursor := 0 402 | showSymbol() 403 | } 404 | return g 405 | } 406 | } 407 | _ := g.AddButton("w" bw, "【否】") 408 | _.OnEvent("Click", e_no) 409 | e_no(*) { 410 | g.Destroy() 411 | writeIni("changeCursor", 0) 412 | global changeCursor := 0 413 | showSymbol() 414 | } 415 | g.OnEvent("Close", e_exit) 416 | e_exit(*) { 417 | try { 418 | IniDelete("InputTip.ini", "Installer", "init") 419 | } 420 | ExitApp() 421 | } 422 | return g 423 | } 424 | showSymbol() { 425 | createGui(confirmGui).Show() 426 | confirmGui(info) { 427 | g := Gui(, "InputTip - 初始化引导") 428 | g.SetFont(fz, "Microsoft YaHei") 429 | g.AddText(, "你是否希望 InputTip 使用【符号方案】?") 430 | g.AddText("xs cRed", "InputTip 会尝试获取输入光标位置,在其附近显示符号") 431 | g.AddLink(, '详情参考【符号方案】: 官网 Github Gitee') 432 | 433 | if (info.i) { 434 | return g 435 | } 436 | w := info.w 437 | bw := w - g.MarginX * 2 438 | 439 | g.AddButton("xs cRed w" bw, "【是】").OnEvent("Click", e_yes) 440 | e_yes(*) { 441 | g.Destroy() 442 | writeIni("symbolType", 1) 443 | global symbolType := 1 444 | initWhiteList() 445 | } 446 | _ := g.AddButton("w" bw, "【否】") 447 | _.OnEvent("Click", e_no) 448 | e_no(*) { 449 | g.Destroy() 450 | writeIni("symbolType", 0) 451 | global symbolType := 0 452 | } 453 | g.OnEvent("Close", e_exit) 454 | e_exit(*) { 455 | try { 456 | IniDelete("InputTip.ini", "Installer", "init") 457 | } 458 | ExitApp() 459 | } 460 | return g 461 | } 462 | } 463 | 464 | initWhiteList() { 465 | createGui(listTipGui).Show() 466 | listTipGui(info) { 467 | g := Gui(, "InputTip - 初始化引导") 468 | g.SetFont(fz, "Microsoft YaHei") 469 | g.AddText("cRed", "对于【符号方案】,InputTip 使用强制的白名单机制`n只有添加到【符号的白名单】中的应用进程窗口才会尝试显示符号") 470 | g.AddLink(, '详情参考: 关于符号方案中的名单机制') 471 | g.AddText(, "建议立即添加常用的应用进程窗口到【符号的白名单】中") 472 | 473 | if (info.i) { 474 | return g 475 | } 476 | w := info.w 477 | bw := w - g.MarginX * 2 478 | 479 | _c := g.AddButton("w" bw, "【是】现在去添加") 480 | _c.OnEvent("Click", add_white_list) 481 | add_white_list(*) { 482 | close() 483 | fn_white_list() 484 | } 485 | _ := g.AddButton("w" bw, "【否】暂时不添加") 486 | _.OnEvent("Click", close) 487 | close(*) { 488 | g.Destroy() 489 | gc.init := 0 490 | checkUpdate(1, 1) 491 | } 492 | return g 493 | } 494 | } 495 | } 496 | } 497 | --------------------------------------------------------------------------------