きっかけは、会社PCの矢印キーが壊れたこと。
会社の管理者から修理までの一時しのぎで「ChangeKey」を教えてもらい、調べているうちに、HJKLやIJKLを矢印キーとして使うお作法を知る。そのうち、「これならホームポジションから手を動かさなくて済むのでは?」と思い、先人の知恵を漁るうちAutoHotKeyにたどり着いた。
しかし、私はプログラムを書けない。ネットの先人たちのサンプルを見よう見まねで試してもうまくいかず。そこで、ArduinoやM5Stack電子工作で体感したAIの破壊力を思い出し、分からないところはChat君やGemini君に質問して試行錯誤を始める。(試験勉強中に部屋を片付けてしまう状態)
最初はF13+IJKLによるカーソル移動だけだったものが、次第に、マウス操作、テキスト編集、一発Google翻訳(和訳英訳ですごく便利)、タイムスタンプ、定型文入力(3種を同じキーで実現)、ショートカットのリンク先を1キーでたどる機能(共有フォルダの密林を一発でたどれて便利)など、どんどん機能が増えてゆく。
途中、出張時の借用PCではF13が使えない問題も発生し、F13・無変換・変換をそれぞれ別「レイヤー」として使う構成に方針を変更。
その結果、最初の目的だった「壊れた矢印キーへの対策」はどうでもよくなり、キーボードが修理されても、IJKLでカーソルを動かし、無変換や変換を押しながら各種操作をするのが当たり前になってしまった。もはや自分のAutoHotKey設定なしには、他人のPCを使ってまともに作業できないカラダになってしまった。
結局、半年ほどかけて、「できるだけホームポジションから手を動かさない」ための、自分専用AutoHotKeyキーボード設定が完成した。
プログラムを書けない自分でも、AIに相談しながらここまで作れたのはよくやったと感じる。先人の記録でアイディアがわき、それをAIが急速に増幅させるループを体感。コードを書かない職の自分でも、楽しめて役立ちました。(14年ぶりに更新したくなるくらいに面白かった。)
こうした経験もお役にたてばと思い、現在使っている設定を記録として公開しておきます。以下、README(AIで作成)と実際のAutoHotKeyスクリプトです。
AutoHotKey設定
今回作成したAutoHotKeyの設定です。
README
AutoHotKey v2 キーボードカスタマイズスクリプト
Windows上で、できるだけホームポジションから手を動かさずに、カーソル移動・テキスト編集・マウス操作・ウィンドウ操作などを行うためのスクリプトです。
無変換、変換、F13を修飾キーとして使い、複数の操作をレイヤー化しています。
また、SpaceキーにはSandS(Space and Shift)を設定しています。Spaceを単独で押すとSpace、押しながら別のキーを操作するとShiftとして動作します。
主な機能は以下の通りです。
F13レイヤー
- I/J/K/Lによるカーソル移動
- Enter / Delete / BackSpace
- マウスカーソル移動
- 左クリック・右クリック・ホイール操作
- Explorer関連操作
無変換レイヤー
- カーソル移動
- 行単位の削除・コピー・カット
- F1~F12
- タイムスタンプ入力
変換レイヤー
- ブラウザのタブ切り替え
- ブラウザの戻る・進む
- Alt+Tabによるアプリ切り替え
Google翻訳
- 選択した文字列をAlt+Shift+Eで英訳
- 選択した文字列をAlt+Shift+Jで和訳
SandS
- Space長押しでShift
- Space単独では通常のSpace
ホットストリング
;;+キーワードによる定型文入力
F13キーは通常のキーボードにはないため、ChangeKeyなどを使ってCapsLockなどのキーをF13に割り当てています。ただし、出張先などF13を使えない環境でも使用できるよう、無変換・変換レイヤーを組み合わせた構成にしています。
動作環境:Windows 10 / 11、AutoHotKey v2.0以降
AutoHotKeyスクリプト
#Requires AutoHotkey v2.0
#SingleInstance Force
; ==============================================================================
; キーボード設定 / AutoHotKey v2
; ------------------------------------------------------------------------------
; 基本方針
; F13 : カーソル移動・マウス操作・Explorer操作(F13はChengeKeyで作る事)
; 無変換 : カーソル移動・編集・F1~F12・日時入力
; 変換 : タブ/アプリ切替・ブラウザの戻る/進む
; Space : SandS(SpaceをShiftとして使用)
; 単独キー : 翻訳・定型文
;
; 変更時の注意
; ・#HotIf の範囲を意識してホットキーを追加すること。
; ・* は Shift/Ctrl/Alt 等が同時に押されても反応させる指定。
; ・{Blind} は、押されている修飾キーの状態を維持してキーを送信する指定。
; ==============================================================================
; ==============================================================================
; 1. F13 ベースのショートカット
; F13 を押している間だけ有効
; ==============================================================================
#HotIf GetKeyState("F13", "P")
; ------------------------------------------------------------------------------
; カーソル移動 / ページ移動
; ------------------------------------------------------------------------------
*i::Send "{Blind}{Up}" ; F13 + I → ↑
*j::Send "{Blind}{Left}" ; F13 + J → ←
*k::Send "{Blind}{Down}" ; F13 + K → ↓
*l::Send "{Blind}{Right}" ; F13 + L → →
*u::Send "{Blind}{PgUp}" ; F13 + U → Page Up
*o::Send "{Blind}{PgDn}" ; F13 + O → Page Down
*h::Send "{Blind}{Home}" ; F13 + H → Home
*n::Send "{Blind}{End}" ; F13 + N → End
; ------------------------------------------------------------------------------
; テキスト編集 / システム操作
; ------------------------------------------------------------------------------
*m::Send "{Blind}{Enter}" ; F13 + M → Enter
*,::Send "{Blind}{Delete}" ; F13 + , → Delete
*.::Send "{Blind}{BackSpace}" ; F13 + . → BackSpace
*z::Send "{Blind}{Volume_Mute}" ; F13 + Z → ミュート切替
; ------------------------------------------------------------------------------
; マウス操作
; ------------------------------------------------------------------------------
*Space::
{
; Space を押している間は左クリックを押しっぱなしにする。
; → F13 + Space をドラッグ操作として使用。
Click "Down Left"
KeyWait "Space"
Click "Up Left"
}
*v::Click "Right" ; F13 + V → 右クリック
*r::Click "WheelUp" ; F13 + R → ホイール上
*f::Click "WheelDown" ; F13 + F → ホイール下
; ------------------------------------------------------------------------------
; マウスカーソル移動
; 押し続けると徐々に加速
; ------------------------------------------------------------------------------
*w::MouseContinuousMove(0, -1) ; F13 + W → 上
*a::MouseContinuousMove(-1, 0) ; F13 + A → 左
*s::MouseContinuousMove(0, 1) ; F13 + S → 下
*d::MouseContinuousMove(1, 0) ; F13 + D → 右
; ------------------------------------------------------------------------------
; Windows Explorer / デスクトップ操作
; ------------------------------------------------------------------------------
; F13 + 0
; デスクトップアイコンの表示 / 非表示を切り替える
*0::
{
ToggleDesktopIcons()
}
; F13 + 1
; 選択した .lnk のリンク先、または選択したファイルを Explorer で表示
*1::
{
OpenSelectedPathInExplorer()
}
; F13 + 2
; 選択したファイルのショートカットを同じフォルダに作成
*2::
{
CreateSelectedFileShortcuts()
}
; F13 + 3
; 選択したファイル・フォルダのフルパスを引用符付きでコピー(エクスプローラでのみ有効)
*3::
{
CopySelectedLinePath()
}
#HotIf
; ==============================================================================
; 2. 無変換キー ベースのショートカット
; 無変換キーを押している間だけ有効
; ==============================================================================
; 無変換キー単体の本来の動作を抑制する。
*vk1D::Send "{Blind}{vkE8}"
#HotIf GetKeyState("vk1D", "P")
; ------------------------------------------------------------------------------
; カーソル移動
; ------------------------------------------------------------------------------
*i::Send "{Blind}{Up}" ; 無変換 + I → ↑
*j::Send "{Blind}{Left}" ; 無変換 + J → ←
*k::Send "{Blind}{Down}" ; 無変換 + K → ↓
*l::Send "{Blind}{Right}" ; 無変換 + L → →
*h::Send "{Blind}{Home}" ; 無変換 + H → Home
*n::Send "{Blind}{End}" ; 無変換 + N → End
; ------------------------------------------------------------------------------
; ページ移動
; ------------------------------------------------------------------------------
*u::Send "{Blind}{PgUp}" ; 無変換 + U → Page Up
*o::Send "{Blind}{PgDn}" ; 無変換 + O → Page Down
; ------------------------------------------------------------------------------
; Enter / Delete / BackSpace
; ------------------------------------------------------------------------------
*m::Send "{Blind}{Enter}" ; 無変換 + M → Enter
*,::Send "{Blind}{Delete}" ; 無変換 + , → Delete
*.::Send "{Blind}{BackSpace}" ; 無変換 + . → BackSpace
; ------------------------------------------------------------------------------
; 行単位のテキスト編集
; ------------------------------------------------------------------------------
*w::Send "{Blind}{End}+{Home}{BackSpace}" ; 無変換 + W → 現在行を削除
*r::Send "{Blind}{End}+{Home}^{c}" ; 無変換 + R → 現在行をコピー
*e::Send "{Blind}{End}+{Home}^{x}" ; 無変換 + E → 現在行をカット
*s::Send "{Blind}+{Home}" ; 無変換 + S → 行頭まで選択
*f::Send "{Blind}+{End}" ; 無変換 + F → 行末まで選択
*d::Send "{Blind}{End}{Enter}" ; 無変換 + D → 行末で改行
; ------------------------------------------------------------------------------
; F1 ~ F12
; ------------------------------------------------------------------------------
1::Send "{Blind}{F1}" ; 無変換 + 1 → F1
2::Send "{Blind}{F2}" ; 無変換 + 2 → F2
3::Send "{Blind}{F3}" ; 無変換 + 3 → F3
4::Send "{Blind}{F4}" ; 無変換 + 4 → F4
5::Send "{Blind}{F5}" ; 無変換 + 5 → F5
6::Send "{Blind}{F6}" ; 無変換 + 6 → F6
7::Send "{Blind}{F7}" ; 無変換 + 7 → F7
8::Send "{Blind}{F8}" ; 無変換 + 8 → F8
9::Send "{Blind}{F9}" ; 無変換 + 9 → F9
0::Send "{Blind}{F10}" ; 無変換 + 0 → F10
-::Send "{Blind}{F11}" ; 無変換 + - → F11
^::Send "{Blind}{F12}" ; 無変換 + ^ → F12
; ------------------------------------------------------------------------------
; 入力モード切替
; ------------------------------------------------------------------------------
; 無変換 + 変換 → 半角/全角
vk1C::Send "{Blind}{vkF3sc029}"
; ------------------------------------------------------------------------------
; タイムスタンプ
; ------------------------------------------------------------------------------
; 無変換 + T を短時間に連打
; 1回 → yyyy-MM-dd
; 2回 → yyyyMMdd
; 3回 → yyyyMMdd_HHmm
*t::InsertTimestamp()
#HotIf
; ==============================================================================
; 3. 変換キー ベースのショートカット
; ==============================================================================
; 変換キー単体の本来の動作を抑制する。
*vk1C::Send "{Blind}{vkE8}"
#HotIf GetKeyState("vk1C", "P")
; ------------------------------------------------------------------------------
; タブ / ブラウザ履歴
; ------------------------------------------------------------------------------
*o::Send "{Blind}^{Tab}" ; 変換 + O → 次のタブ
*u::Send "{Blind}^+{Tab}" ; 変換 + U → 前のタブ
*n::Send "{Blind}!{Left}" ; 変換 + N → 戻る
*,::Send "{Blind}!{Right}" ; 変換 + , → 進む
; ------------------------------------------------------------------------------
; アプリ切替
; ------------------------------------------------------------------------------
*j::Send "{Alt Down}{Tab}" ; 変換 + J → Alt + Tab(選択画面を保持)
*l::Send "{Alt Down}+{Tab}" ; 変換 + L → Alt + Shift + Tab
; 変換 + K → Alt + Tab を一度だけ実行して即切替
*k::
{
Send "{Alt Down}{Tab}"
Sleep 10
Send "{Alt Up}"
}
#HotIf
; 変換キーを離した際、Alt が残っていたら解除する。
*vk1C Up::
{
if GetKeyState("Alt")
Send "{Alt Up}"
}
; ==============================================================================
; 4. SandS(Space and Shift)
; F13 を押している間は無効
; ==============================================================================
#HotIf !GetKeyState("F13", "P")
; Excel 等で Ctrl + Space / Shift + Space を使う場合は、そのまま送信。
+Space::Send "+{Space}"
^Space::Send "^{Space}"
; Space を押している間 → 右 Shift
*Space::
{
Send "{RShift Down}"
}
; Space を離したとき
*Space Up::
{
Send "{RShift Up}"
; Space 単体の短い押下だけを「通常の Space」として入力する。
; Shift / Ctrl と同時押しの場合は通常の修飾キー操作を優先。
if (
A_PriorKey == "Space"
&& A_TimeSincePriorHotkey < 200
&& !GetKeyState("Shift", "P")
&& !GetKeyState("Ctrl", "P")
)
{
Send "{Space}"
}
}
#HotIf
; ==============================================================================
; 5. Google 翻訳
; Alt + Shift + E : 英訳
; Alt + Shift + J : 和訳
; ==============================================================================
+!e::TranslateSelectedText("en")
+!j::TranslateSelectedText("ja")
; ==============================================================================
; 6. 定型文(ホットストリング)
; ==============================================================================
; 「;;」+ キーワードで定型文に置換
:*:;;best::
(
Best regards,
(名前)Hogeta Hogekichi
Hogehoge Company
)
:*:;;mail::メールアドレス
:*:;;mobile::携帯電話
:*:;;sig::署名(hogehoge株式会社 hoge hogeo 電話番号)
:*:;;name::名前(hoge hogeo)
; ==============================================================================
; 7. 共通関数
; ==============================================================================
; ------------------------------------------------------------------------------
; Explorer / デスクトップ
; ------------------------------------------------------------------------------
ToggleDesktopIcons()
{
hProgman := WinExist("ahk_class Progman")
if !hProgman
hProgman := WinExist("ahk_class WorkerW")
if !hProgman
return
try
{
control := "ahk_id " hProgman
if ControlGetVisible("SysListView321", control)
ControlHide("SysListView321", control)
else
ControlShow("SysListView321", control)
}
catch
{
; Explorer の状態によって ControlGetVisible/Show/Hide が失敗する
; 場合があるため、Windows のメッセージをフォールバックとして使用。
PostMessage(0x111, 0x7402, 0, , "ahk_id " hProgman)
}
}
OpenSelectedPathInExplorer()
{
clipSaved := ClipboardAll()
try
{
A_Clipboard := ""
Send "^c"
if !ClipWait(1)
return
path := StrReplace(A_Clipboard, '"', "")
; .lnk の場合はリンク先の実体へ変換。
if (SubStr(path, -4) = ".lnk")
{
try
{
FileGetShortcut(path, &targetPath)
path := targetPath
}
}
if (path != "")
Run 'explorer.exe /select,"' path '"'
}
finally
{
A_Clipboard := clipSaved
}
}
CreateSelectedFileShortcuts()
{
try
{
shellApp := ComObject("Shell.Application")
activeHwnd := WinActive("A")
for window in shellApp.Windows
{
if (window.hwnd != activeHwnd)
continue
selectedItems := window.Document.SelectedItems
if (selectedItems.Count == 0)
return
for item in selectedItems
{
filePath := item.Path
SplitPath(
filePath,
&name,
&dir,
&ext,
&nameNoExt
)
shortcutPath := dir "\" nameNoExt " - ショートカット.lnk"
FileCreateShortcut(filePath, shortcutPath)
}
return
}
}
catch
{
; Explorer 以外で実行した場合などは何もしない。
return
}
}
CopySelectedLinePath()
{
try
{
shellApp := ComObject("Shell.Application")
activeHwnd := WinActive("A")
pathList := ""
for window in shellApp.Windows
{
if (window.hwnd != activeHwnd)
continue
selectedItems := window.Document.SelectedItems
if (selectedItems.Count == 0)
return
; 選択されたすべてのアイテムのパスを収集
for item in selectedItems
{
pathList .= '"' item.Path '"`n'
}
; 最後の余分な改行を削除してクリップボードに格納
A_Clipboard := RTrim(pathList, "`n")
; コピー完了を音(ピピッ)で通知(不要なら削除してください)
SoundBeep 1500, 100
return
}
}
catch
{
; Explorer 以外(デスクトップなど)でのフォールバック処理
; 通常の Ctrl+C でパスが取得できる性質を利用
clipSaved := ClipboardAll()
try {
A_Clipboard := ""
Send "^c"
if ClipWait(0.5, 1) {
formattedPaths := ""
loop parse, A_Clipboard, "`n", "`r" {
if (A_LoopField != "")
formattedPaths .= '"' A_LoopField '"`n'
}
A_Clipboard := RTrim(formattedPaths, "`n")
SoundBeep 1500, 100
}
} catch {
A_Clipboard := clipSaved
}
}
}
; ------------------------------------------------------------------------------
; タイムスタンプ
; ------------------------------------------------------------------------------
InsertTimestamp()
{
static pressCount := 0
pressCount++
; 300ms 以内に次の T が押されなければ確定。
SetTimer(InsertTimestamp_Commit, -300)
InsertTimestamp_Commit()
{
IME_SET(0)
switch pressCount
{
case 1:
Send FormatTime(, "yyyy-MM-dd")
case 2:
Send FormatTime(, "yyyyMMdd")
case 3:
Send FormatTime(, "yyyyMMdd_HHmm")
}
pressCount := 0
}
}
; ------------------------------------------------------------------------------
; Google 翻訳
; ------------------------------------------------------------------------------
TranslateSelectedText(targetLanguage)
{
clipSaved := ClipboardAll()
try
{
; 選択文字列をコピー。
A_Clipboard := ""
Send "^c"
if !ClipWait(1)
{
MsgBox(
"テキストのコピーに失敗したか、文章が選択されていません。",
"エラー",
48
)
return
}
encodedText := WinUriEncode(A_Clipboard)
url := (
"https://translate.google.co.jp/"
"?hl=ja"
"&sl=auto"
"&tl=" targetLanguage
"&text=" encodedText
"&op=translate"
)
Run url
}
finally
{
; 翻訳処理で変更したクリップボードを元に戻す。
Sleep 100
A_Clipboard := clipSaved
}
}
WinUriEncode(str)
{
; 日本語を含む文字列を URL に安全に埋め込むため、
; Windows の UrlEscapeW を使用する。
maxLen := (StrLen(str) * 3) + 1
outBuff := Buffer(maxLen * 2)
status := DllCall(
"shlwapi\UrlEscapeW",
"Str", str,
"Ptr", outBuff,
"UInt*", &maxLen,
"UInt", 0x00002000,
"Int"
)
if (status == 0)
return StrGet(outBuff, "UTF-16")
; 変換に失敗した場合は元の文字列を返す。
return str
}
; ------------------------------------------------------------------------------
; マウス連続移動
; ------------------------------------------------------------------------------
MouseContinuousMove(xDir, yDir)
{
; ホットキー名から * などを除いて、押されているキーを取得。
currentKey := RegExReplace(A_ThisHotkey, "[\*\#\!\^\+]")
startTime := A_TickCount
while GetKeyState(currentKey, "P")
{
elapsed := (A_TickCount - startTime) / 1000
; 長押し時間に応じて加速。
speed := 3 + (elapsed ** 2 * 20)
MouseMove(
xDir * speed,
yDir * speed,
0,
"R"
)
Sleep 10
}
}
; ------------------------------------------------------------------------------
; IME
; ------------------------------------------------------------------------------
IME_GET(WinTitle := "A")
{
hwnd := WinExist(WinTitle)
if !hwnd
return 0
ptrSize := A_PtrSize
stGTI := Buffer(24 + 6 * ptrSize, 0)
NumPut("UInt", stGTI.Size, stGTI, 0)
if !DllCall("GetGUIThreadInfo", "UInt", 0, "Ptr", stGTI.Ptr)
return 0
hwndCaret := NumGet(stGTI, 8 + ptrSize, "Ptr")
targetHwnd := hwndCaret ? hwndCaret : hwnd
return DllCall(
"SendMessage",
"Ptr",
DllCall(
"imm32\ImmGetDefaultIMEWnd",
"Ptr", targetHwnd,
"Ptr"
),
"UInt", 0x0283,
"Ptr", 0x0005,
"Ptr", 0,
"Ptr"
)
}
IME_SET(setState, WinTitle := "A")
{
hwnd := WinExist(WinTitle)
if !hwnd
return 0
ptrSize := A_PtrSize
stGTI := Buffer(24 + 6 * ptrSize, 0)
NumPut("UInt", stGTI.Size, stGTI, 0)
if !DllCall("GetGUIThreadInfo", "UInt", 0, "Ptr", stGTI.Ptr)
return 0
hwndCaret := NumGet(stGTI, 8 + ptrSize, "Ptr")
targetHwnd := hwndCaret ? hwndCaret : hwnd
return DllCall(
"SendMessage",
"Ptr",
DllCall(
"imm32\ImmGetDefaultIMEWnd",
"Ptr", targetHwnd,
"Ptr"
),
"UInt", 0x0283,
"Ptr", 0x0006,
"Ptr", setState,
"Ptr"
)
}
※このスクリプトは、Web上の様々な人の例をもとに半年ほどかけてAIと相談しながら改造したものです。AutoHotKeyを使いはじめた方の参考になれば幸いです。ChangeKeyでF13作成や、AHKインストールなどは例が多く転がっているので検索してみてください。




