torazaemon2016’s blog

手書き文字認識メモ開発

Autohotkey Ver2のGUIによる分離QWERTYスクリーンタッチキーボード

2025-11-21 全部集約したものになりました。 torazaemon2016.hatenablog.jp

以下は、上ができる前までの内容です。


AutohotkeyGUIを使って、物理的なキーボードのないPC (Surface)を、ペンや指だけで操作するを目指すシリーズ 2回目です。

前回は、GUIを出す習作みたいなメニューでしたが、今回は、もう少し実用的なタッチタイプキーボードの実装です。

torazaemon2016.hatenablog.jp

Keyword: Autohotkey, ahk, タッチタイプキーボード, スクリーンキーボード, 仮想キーボード, Virtual keyboard

Windowsでのタッチタイプキーボード

Surface ProなどのWindowsタブレット型PCでのWindows標準のスクリーンタッチタイプキーボードは、ちょっとイマイチな感があるので、 WWWを探すと、

hotvirtualkeyboard.com

とかがありますが、自分で配置やキー割り当てができて、Surfaceで物理キーボードがなくても使えるものをと、作ってみました。

Autohotkeyでスクリーンタッチキーボード

そこで、AutohotkeyGUIを使って作るにはと思い、探すと、

ahkscript.github.io がありましたが、v1からのものをv2にしたものらしく、実行すると、表示はされるけど、キー操作できないみたいでした。

さらに探すと、v2で動く

https://www.reddit.com/r/AutoHotkey/comments/13gawje/can_buttons_in_gui_act_like_keyboard_keys/

が見つかりました。

これを参考にして、自分でキー配置を自由に配置してみたいとして、分離QWERTYスクリーンタッチキーボードを作成しました。

(このほかにも、カタナ式などやってみました。自由にタッチキーボードが作れて面白いです)

なお、Autohotkeyの詳しいことやインストールなどについては、他の詳しいページを参考にしてください。

分離QWERTY配置

2つのWindowで、分離キーボードを実現しています。

小さいサイズ

大きいサイズで、IME ON状態

Windows標準の分割タイプと違って、2つのWindowになっているので、間が見えますし、左右ばらばらに自由に配置できるので、都合の良いところに置けます。(基本的に左右の高さが一致してないと使いづらいっぽいですが)

標準的なテンキーなし日本語QWERTY配列より、右側の記号部分を削っていますので、それらは左下の「Extra」キーを押すことで出てきます。

詳しくは以下のスクリプトや、実際に動かしてみてみてください。 キーボードの大きさなど、プログラム中で値を書いていますので、それを修正すれば、好みのサイズにすることができるかと思います。

あと、物理キーボードがないので、TouchPanelGesturesアプリは必須です。

suwa.pupu.jp

プログラム中でホットキーとして「F2」を指定しているので、 「指3本のSingleTap」とかを F2 と設定しておくと、画面を指3本でタップすることでキーボードを出したり、隠したりできるので、すごく楽になります。

torazaemon2016.hatenablog.jp

物理キーボードがないSurfaceタブレットとして使う生活をエンジョイしてください。


QWERTY2.ahk スクリプト

; 2025-09-23 分離QWERTY by torazemon2016

; 参考
; title: "Can buttons in GUI act like keyboard keys?"
; https://www.reddit.com/r/AutoHotkey/comments/13gawje/can_buttons_in_gui_act_like_keyboard_keys/

#Requires AutoHotkey 2.0+       ; Version 2以上

;IME_SET,GET() IME制御ライブラリを使用
; https://qiita.com/kenichiro_ayaki/items/d55005df2787da725c6f
#Include "IMEv2.ahk"    

; --- キーボードボタンのCSVデータ ---
; 定義の記述の ;から書き始まっているのは読み飛ばす
; csv := と 次の行の ( と 次の行の " は、このとおりにしておかないとエラー。同様に、一番最後の " と ) も独立行
; 2025-09-23 分離QWERTY
csv :=
(
"
; 1:button名, 2:文字, 3:英字時, 4:shift時, 5:extra時(, 6:コメント) の必須5項目か、任意のコメントの6項目の並び  

; qwerty 1段
bnEsc,Esc,Esc,Esc,Esc,      comment test Escape key
bn1,1,1,!,F1
bn2,2,2,`",F2,              ダブルコーテーションは`でエスケープすることでダブルコーテーション1文字になる
bn3,3,3,#,F3
bn4,4,4,$,F4
bn5,5,5,%,F5

bn6,6,6,&&,F6,              Buttonコントロールに&文字を表示させるには、&&と記述する。send_key関数での判断の際にも case '&&' としてやる必要がある
bn7,7,7,',F7
bn8,8,8,(,F8
bn9,9,9,),F9
bn0,0,0,|,F10,              | を 0のshiftにする
bnMinus,-,-,=,]
bnHat,^,^,~,End,            分離では未使用
bnEnmark,\,\,|,|,           分離では未使用 円マークと|のキーはキー削除のため | を 0のshiftにする
bnBackspace,Backspace,BS,}+{BS,BS


; qwerty 2段
bnTab,Tab,Tab,}+{Tab},Tab
bnq,q,q,Q,F11
bnw,w,w,W,F12
bne,e,e,E,+
bnr,r,r,R,*
bnt,t,t,T,|

bny,y,y,Y,^
bnu,u,u,U,~
bni,i,i,I,@
bno,o,o,O,{
bnp,p,p,P,[
bnAtmark,@,@,``,[,          分離では未使用 backquoteは`でエスケープすることで`の1文字になる
bnLBracket,[,[,{,{,         分離では未使用
bnEnter,Enter,Enter,Enter,Enter


; qwerty 3段 CaspはLCtrl
bnLCtrl,LCtrl,Ctrl,Ctrl,Ctrl
bna,a,a,A,}^{a
bns,s,s,S,}^{s
bnd,d,d,D,}^{d
bnf,f,f,F,}^{f
bng,g,g,G,\

bnh,h,h,H,;
bnj,j,j,J,:
bnk,k,k,K,``,                backquoteは`でエスケープすることで`の1文字になる
bnl,l,l,L,}
bnSemiColon,;,;,+,],        分離では未使用
bnColon,:,:,*,],            分離では未使用
bnRBracket,],],},},         分離では未使用       


; qwerty 4段
bnLShift,LShift,LShift,LShift,LShift
bnz,z,z,Z,}^{z
bnx,x,x,X,}^{x
bnc,c,c,C,}^{c
bnv,v,v,V,}^{v
bnb,b,b,B,_

bnn,n,n,N,/
bnm,m,m,M,?
bnComma,,,,,                コンマはCSVで文字として扱うのは面倒なのでMakeMapsFromCSV関数で直接代入することで処理する
bnPeriod,.,.,>,End,          
bnSlash,/,/,?,?,            分離では未使用
bnUnder,\,\,_,PgUp,         分離では未使用
bnUp,Up,Up,Up,}+{Up}
bnRShift,RShift,RShift,RShift,RShift,   RShift は領域選択ができる(+をつけて送出する処理)


; 5段目
bnExtra,Extra,Extra,Extra,Extra,        特殊キー(実キーボードにはない創作キー) send_key()関数で処理
bnLWin,LWin,LWin,LWin,LWin
bnLAlt,LAlt,LAlt,LAlt,LAlt
bnvk1D,vk1D,無変換,無変換,無変換,          無変換キー vk1D
bnSpace,Space,Space,}+{Space},Space,    左スペースキー

bnRSpace,Space,Space,Space,Space,       右スペースキー
bnvk1C,vk1C,変換,変換,変換,               変換キー vk1C
bnLeft,Left,Left,Left,}+{Left}
bnDown,Down,Down,Down,}+{Down}
bnRight,Right,Right,Right,}+{Right}


; 物理キーには定義がある特殊キー---------------
bnRWin,RWin,RWin,RWin,RWin,             分離では未使用
bnRCtrl,RCtrl,RCtrl,RCtrl,RCtrl,        分離では未使用
bnRAlt,RAlt,RAlt,RAlt,RAlt,             分離では未使用

bnF11,F11,F11,F11,F11,                  分離では未使用
bnF12,F12,F12,F12,F12,                  分離では未使用
bnF13,F13,F13,F13,F13                   分離では未使用
bnF14,F14,F14,F14,F14,                  分離では未使用
bnF15,F15,F15,F15,F15,                  分離では未使用
bnF16,F16,F16,F16,F16,                  分離では未使用
"
)

; --- CSVからMap化する関数 ---
MakeMapsFromCSV(csvText) {
    bm := Map()   ; send文字マップ
    bm2 := Map()  ; reverse map (英字 2 button name マップ (asc2bn))
    bm3 := Map()  ; 英字表示マップ   normal
    bm4 := Map()  ; shift表示マップ (shift)
    bm5 := Map()  ; extra表示マップ (F13)

    Loop Parse, csvText, "`n", "`r"
    {
        line := Trim(A_LoopField)
        
        if ( (line = "") || (InStr(line, ";", 0, 1) = 1) )  ; 読み飛ばし定義: 空行 または 1文字目が「;」
            continue

        ;MsgBox("line=" line)

        parts := StrSplit(line, ",")    ; , で分ける
        if (parts.Length >= 5) {     ; 5項目以上
            /* 
            ; 6番目の要素をコメント扱い
            if (parts.Length >= 6) {
                key := parts[1]
                comment := parts[6] ; 読み捨てるだけ
                MsgBox(key " : " comment)
            }
            */
            key := parts[1]
            sendmoji := parts[2]
            normal := parts[3]
            shift := parts[4]
            extra := parts[5]

            bm[key] := sendmoji
            bm2[sendmoji] := key
            bm3[key] := normal
            bm4[key] := shift
            bm5[key] := extra
        }
    }

    ; 文字Comma「,」 は特別扱い csvロード関数(MakeMapsFromCSV)でやるには例外処理面倒いので、以下のように直接代入する
    bm["bnComma"] := ","
    bm2[","] := "bnComma"
    bm3["bnComma"] := ","   ; normal
    bm4["bnComma"] := "<"    ; shift
    bm5["bnComma"] := "Home"    ; extra
    
    ; これと、「&」に対する処理だけはどうしようもない cvsでの代入はできるが、GUIで表示には「&&」としなければならないため、send_key()で特殊処理やる
    ;bm4["bn6"] := "&&"     ; shift

    return [bm, bm2, bm3, bm4, bm5]  ; 配列で返す
}

; --- CSVからMap作成 ---
maps := MakeMapsFromCSV(csv)

bn2send_map := maps[1]
asc2bn_map := maps[2]
bn2asc_map := maps[3]
bn2shift_map := maps[4]
bn2extra_map := maps[5]

; --- キーラベル(ボタン名bn???)から文字を取得関数 ---
GetKey2Name(key) {
    global CurrentMode, bn2send_map, asc2bn_map, bn2asc_map, bn2shift_map
    ;MsgBox(key)
    if (CurrentMode = "normal")
        return bn2send_map.Has(key) ? bn2asc_map[key] : ""
    else if (CurrentMode = "shift")
        return bn2shift_map.Has(key) ? bn2shift_map[key] : ""
    else
        return bn2extra_map.Has(key) ? bn2extra_map[key] : ""
}

; --- 文字からキーラベル(bn???)を取得関数 ---
GetName2Key(name) {
    global CurrentMode, bn2send_map, bn2asc_map, bn2shift_map, asc2bn_map
    ;MsgBox(name)
    ; keyには、最初のもの(asc)しか入っていない
    ;if (CurrentMode = "normal")
        return asc2bn_map.Has(name) ? asc2bn_map[name] : ""
    ;else
    ;   return asc2bn_map.Has(name) ? shift2bn_map[name] : ""
}

/**************************************************************************/
; たまに(万が一)、Ctrlキーなどが押されたままになっていたりするので、それを外すため
clear_mods(*)
{
    for _, key in ['LShift', 'RShift', 'LCtrl', 'RCtrl', 'LAlt','RAlt', 'LWin', 'RWin' ]
    {
        ;MsgBox("clear_mods() " key " -> " GetName2Key(key))

        if GetKeyState(key)                 ; 万が一、キーが押された状態になっていたら
            SendInput('{' key ' Up}')       ; ノーマルな状態にする
    }
}
    
/**************************************************************************/
; キーボードの並びの配列 
; QWERTY
/*
key_table := [
     [ 'bnEsc', 'bn1', 'bn2', 'bn3', 'bn4', 'bn5', 'bn6', 'bn7', 'bn8', 'bn9', 'bn0','bnMinus', 'bnHat', 'bnBackspace' ]
    ,[ 'bnTab', 'bnq', 'bnw', 'bne', 'bnr', 'bnt', 'bny', 'bnu', 'bni', 'bno', 'bnp','bnAtmark', 'bnLBracket' , 'bnEnter' ]
    ,[ 'bnLCtrl', 'bna', 'bns', 'bnd', 'bnf', 'bng', 'bnh', 'bnj', 'bnk', 'bnl', 'bnSemiColon', 'bnColon', 'bnRBracket' ]
    ,[ 'bnLShift', 'bnz', 'bnx', 'bnc', 'bnv', 'bnb', 'bnn', 'bnm', 'bnComma', 'bnPeriod', 'bnSlash',  'bnUnder', 'bnUp', 'bnRShift' ]
    ,[ 'bnExtra', 'bnLWin', 'bnLAlt', 'bnvk1D', 'bnSpace', 'bnvk1C', 'bnF14', 'bnF15', 'bnRAlt', 'bnLeft', 'bnDown', 'bnRight' ]
    ]
*/

; 分離QWERTY
key_table_left := [
     [ 'bnEsc', 'bn1', 'bn2', 'bn3', 'bn4', 'bn5' ]
    ,[ 'bnTab', 'bnq', 'bnw', 'bne', 'bnr', 'bnt']
    ,[ 'bnLCtrl', 'bna', 'bns', 'bnd', 'bnf', 'bng']
    ,[ 'bnLShift', 'bnz', 'bnx', 'bnc', 'bnv', 'bnb']
    ,[ 'bnExtra', 'bnLWin', 'bnLAlt', 'bnvk1D', 'bnSpace']
    ]
key_table_right := [
     [ 'bn6', 'bn7', 'bn8', 'bn9', 'bn0', 'bnBackspace' ]
    ,[ 'bny', 'bnu', 'bni', 'bno', 'bnp', 'bnEnter' ]
    ,[ 'bnh', 'bnj', 'bnk', 'bnl', 'bnMinus' ]
    ,[ 'bnn', 'bnm', 'bnComma', 'bnPeriod', 'bnUp', 'bnRShift' ]
    ,[ 'bnRSpace',   'bnvk1C',  'bnLeft', 'bnDown', 'bnRight' ]
    ]

/******************/
; モード切替用グローバル変数
global CurrentMode := "normal"  ; 初期値: normal(ノーマルキー) shift(シフトキー) extra(F1など拡張)

; 以下4つは同時にON (押されているがあるので、上記Modeとは違う扱い)
global AltState := 0    ; ALtキーが押されてるか1 ない 0
global CtrlState := 0   ; Ctrlキーが押されてるか1 ない 0
global WinState := 0    ; Winキーが押されてるか1 ない 0
global RShiftState := 0 ; 右シフト(RShift)キーが押されてるか1 ない 0  (F1のシフト(+F1)を実現するためなど)

/********************************************************/
; キーボード起動 (Autohotkey起動時に直接起動)
;virtual_keyboard_katanakai(1)  ; 1:small 2:large サイズで起動
virtual_keyboard_katanakai(2)   ; 1:small 2:large サイズで起動

/********************************************************/
; ここからが本体
virtual_keyboard_katanakai(size,*)
{
    ;左キーボードGUIオブジェクトを作成
    ;guikeyboard := Gui('+AlwaysOnTop', "AHK Keyboard") ; これだと文字がエディタなどに入力されない
    gui1 := Gui('+AlwaysOnTop +E0x08000000', "AHK Keyboard")    ; これだと文字を送れる ; "WS_DISABLED"
    
    key_table := key_table_left
    make_keyboard(gui1,key_table,size)  ; 1: small size, 2: large size

    gui1.Show('x0 y200 NoActivate')     ; 画面左上

    ;右キーボードGUIオブジェクトを作成
    gui2 := Gui('+AlwaysOnTop +E0x08000000', "AHK Keyboard 2")  ; これだと文字を送れる ; "WS_DISABLED"

    key_table := key_table_right 
    make_keyboard(gui2,key_table,size)  ; 1: small size, 2: large size

    gui2.Show('x1600 y200 NoActivate')  ; 画面右上

    ; Hotキーの定義
    toggle_hotkey := 'F2'                               ; Hotkey
    Hotkey("*" toggle_hotkey, CloseWindows)             ; keyboard on/off
    gui1.OnEvent("Close", CloseWindows)                 ; X で閉じるとき
    gui2.OnEvent("Close", CloseWindows)                 ; X で閉じるとき
    OnExit(clear_mods)                                  ; 終了時 実行するもの

    /***** 内部関数 **********************************************/
    ; 両方のWindowを同時に閉じる(Hideする)-----
    CloseWindows(*)
    {
        clear_mods()    ; 万が一、装飾キーがONになっているのをクリアするため
        
        if WinExist('ahk_id ' gui1.hwnd)
            gui1.Hide()
        else
            gui1.Show('NoActivate')
        
        if WinExist('ahk_id ' gui2.hwnd)
            gui2.Hide()
        else
            gui2.Show('NoActivate')
    }

    ;keyboard作成------------------
    make_keyboard(guio,key_table,size,*)
    {
        ; サイズ指定
        ;MsgBox("size = " size) ; 
        if(size == 1)
        {
            ; 小さいタイプ
            margin := 0     ; キーとキーの間
            btn_w := 60     ; 70    
            btn_h := 50     ; 70
            guio.SetFont("s14")  ; フォントサイズ(ポイント)
        }
        else
        {
            ; 大きめ
            margin := 5     ; キーとキーの間
            btn_w := 80     ; ボタンの幅
            btn_h := 70     ; ボタンの高さ
            guio.SetFont("s18")  ; フォントサイズ(ポイント)を設定
        }
        
        guio.MarginX := guio.MarginY := margin    
        guio.BackColor := "Silver"  ; 背景色 
        
        for _, row in key_table {               ; 段毎
            y := margin + (A_Index-1) * (margin + btn_h) ; 各段のY座標
            for _, bnkey in row {                 ; 段での各文字毎
                w := 0
                h := 0
                offset := 0     ; 右の最初やEnterキーのずれの位置合わせのため
                switch bnkey 
                {
                    case 'bnEsc':                   ; 1段目左端
                        w := btn_w * 0.8            ; 少し小さく
                        h := btn_h
                    
                    case 'bnTab':                   ; 2段目左端
                        w := btn_w * 1.0
                        h := btn_h
                    
                    case 'bnLCtrl':                 ; 3段目左端
                        w := btn_w * 1.2                
                        h := btn_h
                    
                    case 'bnLShift':                ; 4段目左端
                        w := btn_w * 1.4
                        h := btn_h
                    
                    case 'bnF13':                   ; 5段目左端
                        w := btn_w * 1      
                        h := btn_h
                    
                    case 'bnSpace':                 ; スペースキー
                        w := btn_w * 1.4 + btn_w * 1 + margin * 1  ; LShiftの幅分 + 1キー分 + 1つ分の隙間
                        h := btn_h

                    case 'bn6':                     ; 分離右 1段目左
                        w := btn_w * 1
                        h := btn_h
                        offset := 0

                    case 'bnBackspace':             ; 分離1段目右端
                        w := btn_w * 0.6 + btn_w * 1
                        h := btn_h

                    case 'bny':                     ; 分離右 2段目左
                        w := btn_w * 1      
                        h := btn_h
                        offset := btn_w * 0.2

                    case 'bnEnter':                 ; Enterキー
                        w := btn_w * 1.2            ; Enterの幅               
                        h := btn_h * 2.0 + margin   ; Enterの高さ ; 2段分+1つ分の隙間
                        offset := btn_w * 0.2       ; p と Enterとの間

                    case 'bnh':                     ; 分離右 3段目左
                        w := btn_w * 1              
                        h := btn_h
                        offset := btn_w * 0.4

                    case 'bnn':                     ; 分離右 4段目左
                        w := btn_w * 1          
                        h := btn_h
                        offset := btn_w * 0.6

                    case 'bnRSpace':                 ; 分離右 5段目 右スペースキー
                        w := btn_w * 0.6 + btn_w * 2 + margin * 1  ; bnnずれ幅分 + 2キー分 + 1つ分の隙間
                        h := btn_h
                        offset := 0

                    default:
                        w := btn_w                  ; 普通のキーwidth
                        h := btn_h                  ; 普通のキーheight
                }
                
                if (A_Index = 1)                ; 各段の最初のキー
                    con := guio.AddButton('xm+'         offset ' y' y ' w' w ' h' h ' v' bnkey, GetKey2Name(bnkey))
                else                            ; 最初以外のキー
                    con := guio.AddButton('x+' margin + offset ' yp ' ' w' w ' h' h ' v' bnkey, GetKey2Name(bnkey))

                con.OnEvent('Click', send_key.Bind(bnkey))  ; bn??? ボタンの名前(bn???で登録) 
            }
        }
    }
    
    ; 表示を切り替える----------
    ;change_keyboard(guio,*)
    change_keyboard(*)
    {
        global CtrlState
        global AltState
        global WinState
        global RShiftState

        for _, btn in gui1 {
            ;MsgBox("btn.Text = " btn.Text) ; 
            btn.Text := GetKey2Name(btn.Name)
        }
        for _, btn in gui2 {
            ;MsgBox("btn.Text = " btn.Text) ; 
            btn.Text := GetKey2Name(btn.Name)
        }

        ; Toggle IME
        if(IME_GET() == 1)
        {
            gui1["bnvk1D"].Text := "A->あ"
            gui2["bnvk1C"].Text := "あ"
        }
        else
        {
            gui1["bnvk1D"].Text := "A"
            gui2["bnvk1C"].Text := "A->あ"
        }

        ; Current Mode
        if(CurrentMode == "shift")
        {
            ;gui1["bnLShift"].Text := "SSS"
            ;gui2["bnRShift"].Text := "SSS"
            gui1["bnLShift"].Text := "SSS"
        }
        else if(CurrentMode == "extra")
        {
            gui1["bnExtra"].Text := "EEE"
        }
        
        if(CtrlState == 1)
        {
            gui1["bnLCtrl"].Text := "CCC"
            ;gui2["bnRCtrl"].Text := "CCC"  ; 今回未使用
        }
        if(AltState == 1)
        {
            gui1["bnLAlt"].Text := "AAA"
            ;gui2["bnRAlt"].Text := "AAA"   ; 今回未使用
        }
        if(WinState == 1)
        {
            gui1["bnLWin"].Text := "WWW"
            ;gui2["bnRWin"].Text := "WWW"   ; 今回未使用
        }
        if(RShiftState == 1)
        {
            gui2["bnRShift"].Text := "RRR"
        }

        set_BackgroundColor()
    }

    ; 背景色をセットする
    set_BackgroundColor(*)  ; 
    {
        global AltState
        global WinState
        global CtrlState
        global RShiftState
        global CurrentMode
        ;MsgBox("Ctrl " CtrlState " Alt " AltState " Win " WinState)

        ; 上から優先
        if(RShiftState == 1)
        {
            gui1.BackColor := "Red"
            gui2.BackColor := "Red"
        }
        else if(CtrlState == 1)
        {
            gui1.BackColor := "Yellow"
            gui2.BackColor := "Yellow"
        }
        else if(AltState == 1)
        {
            gui1.BackColor := "Olive"
            gui2.BackColor := "Olive"
        }
        else if(WinState == 1)
        {
            gui1.BackColor := "Blue"
            gui2.BackColor := "Blue"
        }
        else if(CurrentMode == "extra")
        {
            gui1.BackColor := "Maroon"
            gui2.BackColor := "Maroon"
        }
        else if(CurrentMode == "shift")
        {
            gui1.BackColor := "Fuchsia"
            gui2.BackColor := "Fuchsia"
        }
        else if(IME_GET() == 1) ; IME ON
        {
            ;MsgBox("IME_GET()" IME_GET())  
            gui1.BackColor := "Green"
            gui2.BackColor := "Green"
        }
        else
        {
            ;MsgBox("IME_GET()" IME_GET())  
            gui1.BackColor := "Silver"
            gui2.BackColor := "Silver"
        }
    }

    ; キー送出---------
    modifykey := "" ; 複数入力を覚えておく必要があるので関数の外に変数
                    ; ^!# が入る (また+のshiftはまた別 選択とかモードとかで扱いを別にしている)
    send_key(key2,*) 
    {
        global CurrentMode
        global AltState
        global WinState
        global CtrlState
        global RShiftState  ; 右shiftを特別扱い

        key := Getkey2Name(key2)    ;bn???->文字
        ;MsgBox("send_key =" key2 " GetKey2Name=" key)  

        switch key {
            case '&&':  ; &はautootkeyでのbutton表示で特別扱いが必要なため 
                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)
                key := "&"
                SendInput('{Blind}{' key '}')

            case '変換':  ; vk1C
                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)  
                IME_SET(1)      ; IMEをON
                sleep(100)      ; PC性能によりスピード調整が必要かも
                if(IME_GET() == 1)
                {
                    change_keyboard()
                }
                else
                {
                    ; もう一度チャレンジ
                    IME_SET(1)  ; IMEをON
                    sleep(100)      ; PC性能によりスピード調整が必要かも
                    if(IME_GET() == 1)
                    {
                        change_keyboard()
                    }
                    else
                    {
                        MsgBox("IME_ONに変わりませんでした 右下のIMEの状態を確認して再度押してみてください")
                    }
                }

            case '無変換':       ; vk1D
                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)  
                IME_SET(0)      ; IMEをoff
                sleep(100)
                if(IME_GET() == 1)
                {
                    MsgBox("IME_offに切り替わりませんでした 申し訳ありませんが再度押してみてください")
                }
                else
                {
                    change_keyboard()
                }
            
            /***装飾キー 押したかどうかのところ****************/
            case 'Alt':  goto ALT
            case 'LAlt': goto ALT
            case 'RAlt': 
                ALT:
                AltState := (AltState = 0) ? 1 : 0
                ;MsgBox("Alt =" AltState)
                change_keyboard()   ; 色も関数でやる

            case 'Ctrl':  goto CTRL
            case 'LCtrl': goto CTRL
            case 'RCtrl': 
                CTRL:
                CtrlState := (CtrlState = 0) ? 1 : 0
                ;MsgBox("Ctrl =" CtrlState)
                change_keyboard()   ; 色も関数でやる

            case 'LWin': goto wIN
            case 'RWin':
                WIN:
                if(WinState == 1)   ; Win のあと再度Winを押すと Winキーを押したのと同じにしてスタートメニューを開く
                {
                    SendInput('{Blind}' modifykey '{' key '}')
                }
                WinState := (WinState = 0) ? 1 : 0
                ;MsgBox(""in =" WinState)
                change_keyboard()   ; 色も関数でやる

            /***Mode(状態)のところ****************/
            case 'Shift':  goto SFT
            case 'LShift': goto SFT
                SFT:
                if((AltState == 1) || (CtrlState == 1 ) || (WinState == 1)) ; 修飾キーONのときはモード変えない
                    goto END
                    
                CurrentMode := (CurrentMode != "shift") ? "shift" : "normal"
                RShiftState := 0    ; 解除する
                ;MsgBox("Shift =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()   ; 色も関数でやる

            case 'RShift':  ; 選択するため&F13でのF?をShiftつけたいとき (+F1 になる)
                ;MsgBox("RS =" RShiftState)
                ; Shift状態にはしない(F13モードのままで) 
                ;;CurrentMode := (CurrentMode != "shift") ? "shift" :   "normal"    
                RShiftState := (RShiftState = 0) ? 1 : 0
                change_keyboard()   ; 色も関数でやる

            case 'Extra':
                if((AltState == 1) || (CtrlState == 1 ) || (WinState == 1)) ; 修飾キーONのときはモード変えない
                    goto END

                CurrentMode := (CurrentMode != "extra") ? "extra" : "normal"
                RShiftState := 0    ; 解除する
                ;MsgBox("F13 =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()
        

            /***実際に文字出力*****************************/
            default:    ; cvbb
                ;MsgBox("default send_key = '{Blind}' " modifykey "'{'" key "'}'")
                ;SendInput('{Blind}{' key '}')
                ;Send('{' key '}')  ; {Blind}なくても変化なし!?

                if(CtrlState == 1)
                {
                    modifykey := modifykey . "^"
                    CtrlState := 0
                    change_keyboard()   ; 色も関数でやる
                }
                if(AltState == 1)
                {
                    modifykey := modifykey . "!"
                    AltState := 0
                    change_keyboard()   ; 色も関数でやる
                }
                if(WinState == 1)
                {
                    modifykey := modifykey . "#"
                    WinState := 0
                    change_keyboard()   ; 色も関数でやる
                }

                if(RShiftState == 1)
                {
                    ;MsgBox("RS == 1 " RShiftState)
                    modifykey := "+"
                    SendInput('{Blind}' modifykey '{' key '}')
                    goto END
                }

                ; 実際に文字出力 '{' と '}' で挟んで出すので、Enterとかそのまま書ける
                ; F13での }^x とかの } は これの最初の '{' を打ち消すだめ (正確には }^x{ となるけど動いている)
                SendInput('{Blind}' modifykey '{' key '}')
                
                END:
                modifykey := "" ; 
        }
    }
    
}

/* END */

けいならべ配置やカタナ式配置など自分で作れますので、いろいろと改造してみてください。

torazaemon2016.hatenablog.jp

(2025-10-06追加) 今のものをupdateして、けいならべ もどき配置を追加したバージョン torazaemon2016.hatenablog.jp