torazaemon2016’s blog

手書き文字認識メモ開発

Autohotkey Ver2のGUIによる分離QWERTYスクリーンタッチキーボード に「けいならべ」のような配置を追加

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

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


AutohotkeyGUIを使って、物理的なキーボードのないPC (Surface)を、ペンや指だけで操作するを目指すシリーズで、実用的なタッチタイプキーボードの実装に、「けいならべ」のような配置を追加したものです。

この前の記事 torazaemon2016.hatenablog.jp

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

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

40%キーボード+けいならべ もどきスクリーンタッチキーボード

自分でキー配置を自由に配置できる、分離QWERTYスクリーンタッチキーボードです。

いわゆる40%キーボードとかと呼ばれている、キーの数が少ないキーボードを参考にして、画面が狭くならないスクリーンキーボードを目指したものです。

通常のQWERTY配列のものを、左Shiftキー、右Shiftキー、Numberキー、Funcキーという切り替えキーで、キーの種類を切り替えます。

これに、日本語IMEのON/offキーをわかりやすく、スペースキーの左右に配置して、状態も表示しています。

「A->あ」ボタンを押して、日本語IMEをONにした

サイズを小さいものに

左Shiftキー(大文字)

Number(数字中心配置)

Func(機能中心)

今回は、QWERTY配列だけではなく、ローマ字日本語入力として「けいならべ」を参考にした配列を追加しています。

けいならべもどき配置

「けいならべ」とは左手側に子音、右手側に母音がある並びです。

web1.nazca.co.jp

分離ではなく普通の一体型40%QWERTYも作りましたので追加しておきます(2025/10/13)

torazaemon2016.hatenablog.jp

分離QWERTY+けいならべもどき配置

Autohotkeyでのプログラムです

; 2025-10-05 分離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データ ---
;  ( ... ) は 行継続 (continuation section)
; AutoHotkey では、( で始めて ) で終わる部分を「行継続セクション」でこの中はそのまま文字列リテラルとして扱われる
; csv := と 次の行の ( と 次の行の " は、このとおりにしておかないとエラー。同様に、一番最後の " と ) も独立行
csv :=
(
"
; 記述の ;から書き始まっているのは読み飛ばす
; 1:button名, 2:normal, 3:左shift, 4:右シフト, 5:Number, 6:Func, 7:けいならべ, (8:コメント) の必須7項目と、任意のコメントの8項目の並び  

; 2025-09-23 分離QWERTY
; qwerty 1段   40%キーボードでは使用せず
bnEsc,Esc,Esc,Esc,Esc,SIZE,,,       comment test Escape key size変更
bn1,1,~,!,!,F1,F1,,
bn2,2,`",\,`",F2,F2,,               ; ダブルコーテーションは`でエスケープすることでエラーを回避
bn3,3,#,#,F3,F3,,
bn4,4,$,$,F4,F4,,
bn5,5,%,%,F5,F5,,

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


; qwerty 2段
bnTab,Tab,Tab,}+{Tab,Esc,HIDE,Esc,  HIDEは両windowを隠す
bnq,q,Q,!,1,F1,f,               ; q->f               
bnw,w,W,`",2,F2,w,              ;ダブルコーテーションは`でエスケープすることでエラーを回避
bne,e,E,#,3,F3,m,
bnr,r,R,$,4,F4,r,
bnt,t,T,%,5,F5,p,

bny,y,Y,&&,6,F6,}ye{,       ; &&にしなければならないことに注意 
bnu,u,U,',7,F7,}yo{,            ; 'は特別な処理せずともいける
bni,i,I,(,8,F8,u,
bno,o,O,),9,F9,o,
bnp,p,P,|,0,F10,x,
bnAtmark,@,A.``,[,{,,,          ;分離では未使用 backquoteは`でエスケープすることで`の1文字になる
bnLBracket,[,[,{,{,{,,          ;分離では未使用


; qwerty 3段 CaspはLCtrl
bnLCtrl,LCtrl,LCtrl,LCtrl,LCtrl,LCtrl,LCtrl,
bna,a,A,+,+,}^a{,n,
bns,s,S,-,-,}^s{,s,
bnd,d,D,*,*,}^d{,t,
bnf,f,F,/,/,}^f{,k,
bng,g,G,=,=,}^g{,h,

bnh,h,H,;,;,}^a{,}yu{,
bnj,j,J,:,:,}^s{,a,
bnk,k,K,[,[,,i,              
bnl,l,L,],],Home,e,
bnSemiColon,;,,+,PgDn,,,        ;分離では未使用
bnColon,:,,*,],,,               ;分離では未使用
bnRBracket,],,},},},,           ;分離では未使用      

bnMinus,-,=,@,``,End,-,         ; 1段から3段へ backquoteは`でエスケープすることで`の1文字になる

bnEnter,Enter,Enter,}+{Enter,Enter,Enter,Enter,Enter,


; qwerty 4段
bnLShift,LShift,LLL,LShift,LShift,LShift,LShift,
bnz,z,Z,~,$,}^z{,v,             ;けいならべでは、vaのヴぁは出せないのでv
bnx,x,X,^,%,}^x{,z,
bnc,c,C,\,\,}^c{,d,
bnv,v,V,|,<,}^v{,g,
bnb,b,B,_,>,}^b{,b,

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


; 5段目
bnNumber,Number,Number,Number,NNN,Number,Number,    ; 記号キー(数字キー)(実キーボードにはない創作キー) send_key()関数で処理
bnLWin,LWin,LWin,LWin,LWin,LWin,LWin,
bnLAlt,LAlt,LAlt,LAlt,LAlt,LAlt,LAlt,
bnvk1D,無変換,無変換,無変換,無変換,無変換,無変換,     ;無変換キー vk1D
bnSpace,Space,Space,Space,Space,Space,Space,    ;左スペースキー すべてノーマル

bnRSpace,Space,Space,}+{Space, ,SIZE,Enter,   ;右スペースキー Number時全角空白、Funcのときサイズ変更に                  
bnvk1C,変換,変換,変換,変換,変換,変換,                   ;変換キー vk1C  
bnFunc,Func,Func,Func,Func,FFF,Func,            ;特殊キー(実キーボードにはない創作キー) send_key()関数で処理

bnLeft,Left,Left,}+{Left,Left,}+{Left,Left,
bnDown,Down,Down,}+{Down,Down,}+{Down,Down,
bnRight,Right,Right,}+{Right,Right,}+{Right,Right,


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

bnF11,F11,F11,F11,F11,F11,F11,                  ;分離では未使用
bnF12,F12,F12,F12,F12,F12,F12,                  ;分離では未使用
bnF13,F13,F13,F13,F13,F13,F13,                  ;分離では未使用
bnF14,F14,F14,F14,F14,F14,F14,                  ;分離では未使用
bnF15,F15,F15,F15,F15,F15,F15,                  ;分離では未使用
bnF16,F16,F16,F16,F16,F16,F16,                  ;分離では未使用
"
)
; ) で 継続行終わり

; --- CSVからMap化する関数 ---
MakeMapsFromCSV(csvText) {
    ;bm1 := Map()  ; reverse map (normal を button name にマップ (asc2bn))不要に
    bm2 := Map()  ; 英字表示マップ   (normal)
    bm3 := Map()  ; LShift表示マップ(LShift)
    bm4 := Map()  ; RShift表示マップ(RShift)
    bm5 := Map()  ; Number表示マップ(Number)
    bm6 := Map()  ; Func表示マップ  (Func)
    bm7 := Map()  ; けいならべ表示マップ(Keinarabe)

    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 >= 6) {     ; 7項目以上
            Try {
                btnname := parts[1]
                normal := parts[2]
                LShift := parts[3]
                RShift := parts[4]
                Number := parts[5]
                Func   := parts[6]
                Keinarabe := parts[7]   ; けいならべ
                comment := parts[8]     ; 読み捨てるだけだけど、7項目(,が6つ)がなければエラーでcatchへ

                ;bm1[normal] := btnname ; 不要に
                bm2[btnname] := normal
                bm3[btnname] := LShift
                bm4[btnname] := RShift
                bm5[btnname] := Number
                bm6[btnname] := Func
                bm7[btnname] := Keinarabe

            } Catch as e {
                MsgBox("error: " btnname)   ; 定義ミスボタン名を表示 (,の間違いなどのミス)
                exit                        ; 終了する
            }
        }
    }

    ; 文字Comma「,」 は特別扱い csvロード関数(MakeMapsFromCSV)でやるには例外処理面倒いので、以下のように直接代入する
    ;bm1[","] := "bnComma"  ; 不要に
    bm2["bnComma"] := ","   ; normal
    bm3["bnComma"] := "<"    ; LShift
    bm4["bnComma"] := "{"   ; RShift
    bm5["bnComma"] := ","   ; Number
    bm6["bnComma"] := "}^c{"    ; Func
    bm7["bnComma"] := ","   ; Keinarabe
    
    ;return [bm1, bm2, bm3, bm4, bm5, bm6]  ; 配列で返す
    return [bm2, bm3, bm4, bm5, bm6, bm7]  ; 配列で返す
}

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

bn2asc_map := maps[1]
bn2LShift_map := maps[2]
bn2RShift_map := maps[3]
bn2Number_map := maps[4]
bn2Func_map := maps[5]
bn2Kei_map := maps[6]


; --- キーラベル(ボタン名bn???)から文字を取得関数 ---
; モード切替用大域変数
global CurrentMode := "normal"  ; 初期値: normal(ノーマルキー) LShift(シフトキー) RShift(右シフト) Number(記号) Func(F1など拡張) Keinarabe(けいならべ)

GetKey2Name(key) {
    global CurrentMode, bn2asc_map, bn2LShift_map, bn2RShift_map, bn2Number_map, bn2Func_map, bn2Kei_map
    ;MsgBox(key)
    if (CurrentMode = "normal")
        return bn2asc_map.Has(key) ? bn2asc_map[key] : ""
    else if (CurrentMode = "LShift")
        return bn2LShift_map.Has(key) ? bn2LShift_map[key] : "" ; lshift
    else if (CurrentMode = "RShift")
        return bn2RShift_map.Has(key) ? bn2RShift_map[key] : ""
    else if (CurrentMode = "Number")
        return bn2Number_map.Has(key) ? bn2Number_map[key] : ""
    else if (CurrentMode = "Func")
        return bn2Func_map.Has(key) ? bn2Func_map[key] : ""
    else 
        return bn2Kei_map.Has(key) ? bn2Kei_map[key] : ""
}

/**************************************************************************/
; キーボードの並びの配列 
; 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' ]
    ,[ 'bnFunc', 'bnLWin', 'bnLAlt', 'bnvk1D', 'bnSpace', 'bnvk1C', 'bnF14', 'bnF15', 'bnRAlt', 'bnLeft', 'bnDown', 'bnRight' ]
    ]
*/

; 分離QWERTY
key_table_left := [
     [ 'bnTab',     'bnq',      'bnw',      'bne',      'bnr',      'bnt']
    ,[ 'bnLCtrl',   'bna',      'bns',      'bnd',      'bnf',      'bng']
    ,[ 'bnLShift',  'bnz',      'bnx',      'bnc',      'bnv',      'bnb']
    ,[ 'bnNumber',  'bnLWin',   'bnLAlt',   'bnvk1D',   'bnSpace'       ]
    ]

key_table_right := [
     [ 'bny',       'bnu',      'bni',      'bno',      'bnp',      'bnBackspace']
    ,[ 'bnh',       'bnj',      'bnk',      'bnl',      'bnMinus',  'bnEnter' ]
    ,[ 'bnn',       'bnm',      'bnComma',  'bnPeriod', 'bnUp',     'bnRShift']
    ,[ 'bnRSpace',  'bnvk1C',   'bnFunc',   'bnLeft',   'bnDown',   'bnRight']
    ]

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

/********************************************************/
; ここからが本体
virtual_keyboard_katanakai(size,*)
{
    /******************/
    ; 以下は同時にON (押されているがあるので、(Shiftの)Global CurrentModeとは違う扱い)
    AltState := 0   ; ALtキーが押されてるか1 ない 0
    CtrlState := 0  ; Ctrlキーが押されてるか1 ない 0
    WinState := 0   ; Winキーが押されてるか1 ない 0
    
    keysize := size ; keyboardの大きさ (SIZEキー)     ; 1: small size, 2: large 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)

    gui1.Show('x0 y1000 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 y1000 NoActivate') ; 画面右上

    ; Hotキーの定義
    toggle_hotkey := 'F5'                               ; Hotkey
    Hotkey("*" toggle_hotkey, CloseWindows)             ; keyboard on/off
    gui1.OnEvent("Close", CloseWindows1)                ; X で閉じるとき
    gui2.OnEvent("Close", CloseWindows2)                ; 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')
        }   
    }
    
    ; 1のWindowを閉じる(Hideする)-----
    CloseWindows1(*)
    {
        clear_mods()    ; 万が一、装飾キーがONになっているのをクリアするため
        
        if WinExist('ahk_id ' gui1.hwnd)
            gui1.Hide()
        ;else
            ;gui1.Show('NoActivate')
    }
    ; 2のWindowを閉じる(Hideする)-----
    CloseWindows2(*)
    {
        clear_mods()    ; 万が一、装飾キーがONになっているのをクリアするため
        
        if WinExist('ahk_id ' gui2.hwnd)
            gui2.Hide()
        ;else
            ;gui2.Show('NoActivate')
    }

    ; 万が一、Ctrlキーなどが押されたままになっていたりするのを、それを外すため
    clear_mods(*) {
        for _, key in ['lshift', 'rshift', 'lctrl', 'rctrl', 'lalt', 'ralt', 'lwin', 'rwin']
            if GetKeyState(key)             ; もし押されたままの状態だったら
                SendInput('{' key ' Up}')   ; 上げる(押されてない)にする
    }

    ;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 := 1     ; キーとキーの間
            btn_w := 90     ; ボタンの幅
            btn_h := 90     ; ボタンの高さ
            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
                        w := btn_w * 0.4 + 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つ分の隙間
                        ;w := btn_w * 2.2 + margin  ; Enterの幅 2個分+marginの分              
                        w := btn_w * 1.2  margin    ; Enterの幅 2個分+marginの分              
                        h := btn_h                  ; Enterの高さ ; 1段分
                        ;offset := btn_w * 0.2      ; p と Enterとの間 3段目に横長になった

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

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

                    case 'bnRSpace':                 ; 分離右 5段目 右スペースキー
                        ;w := btn_w * 0.6 + btn_w * 2 + margin * 1  ; bnnずれ幅分 + 2キー分 + 1つ分の隙間
                        ;w := btn_w * 0.6 + btn_w * 1  ; bnnずれ幅分 + 1キー分 (RCtrlありのとき)
                        w := btn_w * 0.4 + btn_w * 1  ; bnnずれ幅分 + 1キー分 (RCtrlありのとき)
                        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(*)
    {
        global CurrentState

        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->あ"
        }
        if(CurrentMode == "Func")
        {
            gui2["bnvk1C"].Text := "けいならべ"
        }
        
        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"   ; 今回未使用
        }

        set_BackgroundColor()
    }

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

        ; 上から優先
        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 == "Func")
        {
            gui1.BackColor := "Maroon"
            gui2.BackColor := "Maroon"
        }
        else if(CurrentMode == "Number")
        {
            gui1.BackColor := "Lime"
            gui2.BackColor := "Lime"
        }
        else if(CurrentMode == "LShift")
        {
            gui1.BackColor := "Fuchsia"
            gui2.BackColor := "Fuchsia"
        }
        else if(CurrentMode == "RShift")
        {
            gui1.BackColor := "Red"
            gui2.BackColor := "Red"
        }
        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

        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 'SIZE':    ;
                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)
                if WinExist('ahk_id ' gui1.hwnd)
                    gui1.Destroy()
                if WinExist('ahk_id ' gui2.hwnd)
                    gui2.Destroy()
                if(keysize == 1)    ; 1: small size, 2: large size
                {
                    virtual_keyboard_katanakai(2)   ; 2:largeサイズで起動
                }
                else
                {
                    virtual_keyboard_katanakai(1)   ; 1:smallサイズで起動
                }
                
            case 'HIDE':
                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)
                CurrentMode := "normal" ; 次に表示するときにはnormal表示に (そのままだとFuncのまま)
                change_keyboard()
                CloseWindows()
                
            case '変換':  ; vk1C
                if(CurrentMode == "Keinarabe")  ; けいならべ
                {
                    return
                }

                ;MsgBox("case && send_key =" key2 " GetKey2Name=" key)  
                IME_SET(1)      ; IMEをON
                sleep(100)      ; PC性能によりスピード調整が必要かも
                if(IME_GET() == 1)
                {
                    if(CurrentMode == "Func")
                    {
                        CurrentMode := "Keinarabe"  ; けいならべ
                    }
                    else
                    {
                        CurrentMode := "normal"
                    }
                    change_keyboard()
                }
                else
                {
                    ; もう一度チャレンジ
                    IME_SET(1)  ; IMEをON
                    sleep(100)      ; PC性能によりスピード調整が必要かも
                    if(IME_GET() == 1)
                    {
                        if(CurrentMode == "Func")
                        {
                            CurrentMode := "Keinarabe"  ; けいならべ
                        }
                        else
                        {
                            CurrentMode := "normal"
                        }
                        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
                {
                    CurrentMode := "normal"
                    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 'LLL':  goto LSFT
            case 'Shift':  goto LSFT
            case 'LShift': 
                LSFT:
                gui2.Show('NoActivate')
                if((AltState == 1) || (CtrlState == 1 ) || (WinState == 1)) ; 修飾キーONのときはモード変えない
                    goto END
                    
                CurrentMode := (CurrentMode != "LShift") ? "LShift" :   "normal"
                ;MsgBox("Lshift =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()   ; 色も関数でやる
                
            case 'RRR':  goto RSFT
            case 'RShift':
                RSFT:
                gui1.Show('NoActivate')
                if((AltState == 1) || (CtrlState == 1 ) || (WinState == 1)) ; 修飾キーONのときはモード変えない
                    goto END
                    
                CurrentMode := (CurrentMode != "RShift") ? "RShift" :   "normal"
                ;MsgBox("Rshift =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()   ; 色も関数でやる
                
            case 'FFF': goto FUNC
            case 'Func':
                FUNC:
                if((AltState == 1) || (CtrlState == 1 ) || (WinState == 1)) ; 修飾キーONのときはモード変えない
                    goto END

                CurrentMode := (CurrentMode != "Func") ? "Func" : "normal"
                ;MsgBox("Func =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()

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

                CurrentMode := (CurrentMode != "Number") ? "Number" : "normal"
                ;MsgBox("Number =" CurrentMode " Key=" key " key2=" key2)
                change_keyboard()

            /***実際に文字出力*****************************/
            default:    ; cvbb
                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()   ; 色も関数でやる
                }

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

                END:
                modifykey := "" ; 
        }
    }
    
}

/* END */

プログラムの簡単な説明(前回から変わったところなど)

前回の記事から、無駄な部分などをかなり整理しました。

最初の部分でのキーボタンの定義のcsv形式は、前回使っていなかった第2項目が消えて、また、ExtraだったものをNumberとかFuncと機能と一致するような名前にし、さらに今回の「けいならべ」配列を追加しています。

; --- キーボードボタンのCSVデータ ---
csv :=
(
"
; 記述の ;から書き始まっているのは読み飛ばす
; 1:button名, 2:normal, 3:左shift, 4:右シフト, 5:Number, 6:Func, 7:けいならべ, (8:コメント) の必須7項目と、任意のコメントの8項目の並び  

bnTab,Tab,Tab,}+{Tab,Esc,HIDE,Esc,  HIDEは両windowを隠す
bnq,q,Q,!,1,F1,f,               ; q->f               
bnw,w,W,`",2,F2,w,              ;ダブルコーテーションは`でエスケープすることでエラーを回避
bne,e,E,#,3,F3,m,
bnr,r,R,$,4,F4,r,
bnt,t,T,%,5,F5,p,

Funcキーでの画面などで、AutohotkeyでのModifyキー(Ctrl,Alt,Winを^,!,#)扱いになるようにして、それにAutohotkeyでのキー名を送り込むように{}で囲まれるようになっています。 プログラムの最後に送り出すSendInput()がありますので、それを見てもらえれば「 }^c{ 」とかになっている理由もわかるかと思います。

けいならべと同じ配置にするのも難しくはなく、自分が使いやすいように、キー定義やキー配置も、いろいろと改造して、自分が使いやすいキーボードにしてみてください。

TouchPanelGestures

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

suwa.pupu.jp

プログラム中でホットキーとして「F5」を指定しています。(前のF2からF5に変更)

ジェスチャーとしては「指3本の→↓」に落ち着いてきました。 Surfaceのキーボードのタッチパッドでの、3本指や4本指の操作とTouchPanelGesturesのものが衝突して、意図しない動きが出ることが多く、 三本指で→↓あたりが混乱少なく、キーボードを出したり、隠したりできます。

torazaemon2016.hatenablog.jp

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