Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSK #326

Open
daiaji opened this issue Jan 8, 2025 · 3 comments
Open

OSK #326

daiaji opened this issue Jan 8, 2025 · 3 comments

Comments

@daiaji
Copy link

daiaji commented Jan 8, 2025

V1:

#SingleInstance
SendMode Input

If (A_ScriptFullPath = A_LineFile) { ; if run as script rather than included elsewhere - for testing
    Global keyboard := new OSK("dark", "qwerty")

    toggle := ObjBindMethod(keyboard, "toggle")
    move_left := ObjBindMethod(keyboard, "changeIndex", "Left")
    move_up := ObjBindMethod(keyboard, "changeIndex", "Up")
    move_down := ObjBindMethod(keyboard, "changeIndex", "Down")
    move_right := ObjBindMethod(keyboard, "changeIndex", "Right")
    send_press := func("SendKeyboardPress").bind()

    keyboard.Show()

    Hotkey, ^Ins, % toggle

    Hotkey, If, keyboard.Enabled
    Hotkey, Left, % move_left
    Hotkey, Up, % move_up
    Hotkey, Down, % move_down
    Hotkey, Right, % move_right

    Hotkey, If, keyboard.Enabled && keyboard.IsDPadKeyboard()
    Hotkey, Enter, % send_press

    SendKeyboardPress() {
        keyboard.HandleOSKClick(keyboard.RetrieveDPadSelected())
        Return
    }
}

; for context sensitive hotkeys
#If, keyboard.Enabled
#If, keyboard.Enabled && keyboard.IsDPadKeyboard()
#If

; can't use method for gui onClick
HandleOSKClick() {
    keyboard.HandleOSKClick()
    return
}

Class OSK
; Adapted from feiyue's script: https://www.autohotkey.com/boards/viewtopic.php?t=58366 
{

    __New(theme:="dark", layout:="qwerty") {
        this.Enabled := False

        this.Keys := []
        this.Controls := []
        this.Modifiers := ["LShift", "LCtrl", "LWin", "LAlt", "RShift", "RCtrl", "RWin", "RAlt", "CapsLock", "ScrollLock"]

        if (theme = "light") {
            this.Background := "FDF6E3"
            this.ButtonColour := "EEE8D5" 
            this.ButtonOutlineColour := "8E846F" 
            this.ActiveButtonColour := "DDD6C1" 
            this.SentButtonColour := "AC9D57"
            this.ToggledButtonColour := "AC9D58" ; don't set exactly the same as SentButtonColour
            this.TextColour := "657B83"
        }
        else { ; default dark theme
            this.Background := "2A2A2E"
            this.ButtonColour := "010409" 
            this.ButtonOutlineColour := "010409" 
            this.ActiveButtonColour := "1b1a20" 
            this.SentButtonColour := "553b6b"
            this.ToggledButtonColour := "553b6a" ; don't set exactly the same as SentButtonColour
            this.TextColour := "8b949e"
        }

        this.MonitorKeyPresses := ObjBindMethod(this, "MonitorAllKeys") ; can choose between MonitorModifiers and MonitorAllKeys

        this.Layout := []
        ; layout format is ["Text", width:=45, x-offset:=2]
        ; "Text" is what is sent when the button is clicked.
        ; ` and ~ had getkeystate issues so I replaced them with scancode sc029
        if (layout = "colemak-dh") {
            this.Layout.Push([ ["Esc"],["F1",,23],["F2"],["F3"],["F4"],["F5",,15],["F6"],["F7"],["F8"],["F9",,15],["F10"],["F11"],["F12"],["PrintScreen",60,10],["ScrollLock",60],["Pause",60] ])
            this.Layout.Push([ ["sc029", 30],["1"],["2"],["3"],["4"],["5"],["6"],["7"],["8"],["9"],["0"],["-"],["="],["BS", 60],["Ins",60,10],["Home",60],["PgUp",60] ])
            this.Layout.Push([ ["Tab"],["q"],["w"],["f"],["p"],["b"],["j"],["l"],["u"],["y"],["`;"],["["],["]"],["\"],["Del",60,10],["End",60],["PgDn",60] ])
            this.Layout.Push([ ["CapsLock",60],["a"],["r"],["s"],["t"],["g"],["m"],["n"],["e"],["i"],["o"],["'"],["Enter",77] ])
            this.Layout.Push([ ["LShift",90],["x"],["c"],["d"],["v"],["z"],["k"],["h"],["`,"],["."],["/"],["RShift",94],["",60,72] ])
            this.Layout.Push([ ["LCtrl",60],["LWin",60],["LAlt",60],["Space",222],["RAlt",60],["RWin",60],["App",60],["RCtrl",60],["Left",60,10],["Down",60],["Right",60] ])
        }
        else { ; default qwerty
            this.Layout.Push([ ["Esc"],["F1",,23],["F2"],["F3"],["F4"],["F5",,15],["F6"],["F7"],["F8"],["F9",,15],["F10"],["F11"],["F12"],["PrintScreen",60,10],["ScrollLock",60],["Pause",60] ])
            this.Layout.Push([ ["sc029", 30],["1"],["2"],["3"],["4"],["5"],["6"],["7"],["8"],["9"],["0"],["-"],["="],["BS", 60],["Ins",60,10],["Home",60],["PgUp",60] ])
            this.Layout.Push([ ["Tab"],["q"],["w"],["e"],["r"],["t"],["y"],["u"],["i"],["o"],["p"],["["],["]"],["\"],["Del",60,10],["End",60],["PgDn",60] ])
            this.Layout.Push([ ["CapsLock",60],["a"],["s"],["d"],["f"],["g"],["h"],["j"],["k"],["l"],["`;"],["'"],["Enter",77] ])
            this.Layout.Push([ ["LShift",90],["z"],["x"],["c"],["v"],["b"],["n"],["m"],["`,"],["."],["/"],["RShift",94],["Up",60,72] ])
            this.Layout.Push([ ["LCtrl",60],["LWin",60],["LAlt",60],["Space",222],["RAlt",60],["RWin",60],["App",60],["RCtrl",60],["Left",60,10],["Down",60],["Right",60] ])
        }

        ; ; Optionally sets alternate text for the button actions named in this.Layout - doesn't have to be in same order as layout
        this.PrettyName := { "PrintScreen": "Prt Scr", "ScrollLock": "Scr Lk"
                                , "sc029": "~", 1: "1 !", 2: "2 @", 3: "3 #", 4: "4 $", 5: "5 `%", 6: "6 ^", 7: "7 &&", 8: "8 *", 9: "9 (", 0: "0 )", "-": "- _", "+": "= +", "BS": "", "PgUp": "Pg Up", "PgDn": "Pg Dn"
                                , "q": "Q", "w": "W", "e": "E", "r": "R", "t": "T", "y": "Y", "u": "U", "i": "I", "o": "O", "p": "P", "[": "[ {", "]": "] }", "\": "\ |"
                                , "CapsLock": "Caps", "a": "A", "s": "S", "d": "D", "f": "F", "g": "G", "h": "H", "j": "J", "k": "K", "l": "L", "`;": "`; :", "'": "' """
                                , "LShift": "Shift", "z": "Z", "x": "X", "c": "C", "v": "V", "b": "B", "n": "N", "m": "M", "`,": "`, <", ".": ". >", "/": "/ ?", "RShift": "Shift"
                                , "LCtrl": "Ctrl", "LWin": "Win", "LAlt": "Alt", "Space": " ", "RAlt": "Alt", "RWin": "Win", "AppsKey": "App", "RCtrl": "Ctrl", "Up": "", "Down": "", "Left": "", "Right": ""}
        this.Make()
    }

    SetTimer(TimerID, Period) {
        Timer := this[TimerID]
        SetTimer % Timer, % Period
        return
    }

    Make() {
        Gui, OSK: +AlwaysOnTop -DPIScale +Owner -Caption +E0x08000000 
        Gui, OSK: Font, s12, Verdana
        Gui, OSK: Margin, 10, 10
        Gui, OSK: Color, % this.Background
        SS_CenterTextInBox := 0x200 ; styling adjustment
        For Index, Row in this.Layout {
            For i, Button in Row {
                Width := Button.2 ? Button.2 : 45 
                HorizontalOffset := Button.3 ? Button.3 : 2
                RelativePosition := Index <= 2 and i = 1 ? "xm" : i=1 ? "xm y+2" : "x+" HorizontalOffset
                ButtonText := this.PrettyName[Button.1] ? this.PrettyName[Button.1] : Button.1

                ; Control handling is from Hellbent's script: https://www.autohotkey.com/boards/viewtopic.php?t=87535
                Gui, OSK:Add, Text, % RelativePosition " c" this.TextColour " w" Width " h" 30 " -Wrap BackgroundTrans Center hwndbottomt gHandleOSKClick " SS_CenterTextInBox, % Button.1 ; handles the click
                Gui, OSK:Add, Progress, % "xp yp w" Width " h" 30 " Disabled Background" this.ButtonOutlineColour " c" this.ButtonColour " hwndp", 100
                Gui, OSK:Add, Text, % "xp yp c" this.TextColour " w" Width " h" 30 " -Wrap BackgroundTrans Center hwndtopt " SS_CenterTextInBox, % ButtonText ; displays the pretty name

                this.Keys[Button.1] := [Index, i]
                this.Controls[Index, i] := {Progress: p, Text: topt, Label: HandlePress, Colour: this.ButtonColour}
            }
        }	
        Return
    }

    Show() {
        this.Enabled := True

        ; reset active key
        this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ButtonColour)
        this.ColumnIndex := 0
        this.RowIndex := 0

        CurrentMonitorIndex := this.GetCurrentMonitorIndex()
        DetectHiddenWindows On
        Gui, OSK: +LastFound
        Gui, OSK:Show, Hide
        GUI_Hwnd := WinExist()
        this.GetClientSize(GUI_Hwnd,GUI_Width,GUI_Height)
        DetectHiddenWindows Off

        GUI_X := this.CoordXCenterScreen(GUI_Width,CurrentMonitorIndex)
        GUI_Y := this.CoordYCenterScreen(GUI_Height,CurrentMonitorIndex)

        Gui, OSK:Show, % "x" GUI_X " y" GUI_Y " NA", On-Screen Keyboard

        this.SetTimer("MonitorKeyPresses", 30)

        Return
    }

    Hide() {
        this.Enabled := False
        Gui, OSK: Hide
        this.SetTimer("MonitorKeyPresses", "off")
        return
    }

    Toggle() {
        If this.Enabled {
            this.Hide()
        }
        Else {
            this.Show()
        }
        Return
    }

    ; for centering keyboard on screen
    GetCurrentMonitorIndex() {
        CoordMode, Mouse, Screen
        MouseGetPos, mx, my
        SysGet, monitorsCount, 80

        Loop %monitorsCount%{
            SysGet, monitor, Monitor, %A_Index%
            if (monitorLeft <= mx && mx <= monitorRight && monitorTop <= my && my <= monitorBottom){
                Return A_Index
                }
            }
        Return 1
    }

    CoordXCenterScreen(WidthOfGUI,ScreenNumber) {
        SysGet, Mon1, Monitor, %ScreenNumber%
        return ((Mon1Right-Mon1Left - WidthOfGUI) / 2) + Mon1Left
    }

    CoordYCenterScreen(HeightofGUI,ScreenNumber) {
        SysGet, Mon1, Monitor, %ScreenNumber%
        return (Mon1Bottom - 80 - HeightofGUI)
    }

    GetClientSize(hwnd, ByRef w, ByRef h) {
        VarSetCapacity(rc, 16)
        DllCall("GetClientRect", "uint", hwnd, "uint", &rc)
        w := NumGet(rc, 8, "int")
        h := NumGet(rc, 12, "int")
        Return
    }

    HandleOSKClick(Key:="") {
        if not Key {
            Key := A_GuiControl
        }
        if (this.IsModifier(Key)) {
            this.SendModifier(Key)
        }
        else {
            this.SendPress(Key)
        }
        return
    }

    IsModifier(Key) {
        if (Key = "LShift" 
            or Key = "LCtrl" 
            or Key = "LAlt" 
            or Key = "LWin" 
            or Key = "RShift" 
            or Key = "RCtrl" 
            or Key = "RAlt" 
            or Key = "RWin"
            or Key = "CapsLock"
            or Key = "ScrollLock")
            return True
        else
            return False
    }

    MonitorModifiers() {
        For _, Modifier in this.Modifiers {
            this.MonitorKey(Modifier)
        }
        Return
    }


    MonitorAllKeys() {
        For _, Row in this.Layout {
            For i, Button in Row {
                this.MonitorKey(Button.1)
            }
        }
        Return
    }

    MonitorKey(Key) {
        if (Key = "CapsLock" or Key = "ScrollLock" or Key = "Pause")
            KeyOn := GetKeyState(Key, "T")
        else
            KeyOn := GetKeyState(Key)
        KeyRow := this.Keys[Key][1]
        KeyColumn := this.Keys[Key][2]
        if (KeyOn and this.Controls[KeyRow, KeyColumn].Colour != this.ToggledButtonColour) {
            this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ToggledButtonColour)
        }
        else if (not KeyOn and this.Controls[KeyRow, KeyColumn].Colour = this.ToggledButtonColour) {
            if (KeyRow = this.RowIndex and KeyColumn = this.ColumnIndex)
                this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ActiveButtonColour)
            else
                this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ButtonColour)
        }
        Return
    }

    SendPress(Key) {
        SentRow := this.Keys[Key][1]
        SentColumn := this.Keys[Key][2]
        OldColor := this.Controls[SentRow][SentColumn].Colour
        this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.SentButtonColour)
        SendInput, % "{Blind}{" Key "}" 
        For _, Modifier in this.Modifiers {
            ModifierOn := GetKeyState(Modifier)
            if (ModifierOn)
                SendInput, % "{" Modifier " up}"
        }
        Sleep, 100
        if (SentRow = this.RowIndex and SentColumn = this.ColumnIndex)
            this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.ActiveButtonColour)
        else
            this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.ButtonColour)
        Return
    }

    SendModifier(Key) {
        ModifierRow := this.Keys[Key][1]
        ModifierColumn := this.Keys[Key][2]
        if (Key = "CapsLock")
            SetCapsLockState, % not GetKeyState(Key, "T")
        else if (Key = "ScrollLock")
            SetScrollLockState, % not GetKeyState(Key, "T")
        else {
            ModifierOn := GetKeyState(Key)
            if (ModifierOn)
                SendInput, % "{" Key " up}"
            else 
                SendInput, % "{" Key " down}"
        }
        return
    }


    ChangeIndex(Direction) {
        if (not this.RowIndex) {
            this.RowIndex := 4
            this.ColumnIndex := 7
        }

        if (this.Controls[this.RowIndex, this.ColumnIndex].Colour != this.ToggledButtonColour)
            this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ButtonColour)

        this.HandleChangeIndex(Direction)

        if (Direction = "Up") {
            if this.RowIndex = 1
                this.RowIndex := this.Controls.Length()
            else
                this.RowIndex := this.RowIndex - 1
            this.ColumnIndex := min(this.ColumnIndex, this.Controls[this.RowIndex].Length())
        }
        if (Direction = "Down") {
            this.RowIndex := mod(this.RowIndex, this.Controls.Length()) + 1
            this.ColumnIndex := min(this.ColumnIndex, this.Controls[this.RowIndex].Length())
        }
        if (Direction = "Left") {
            if this.ColumnIndex = 1
                this.ColumnIndex := this.Controls[this.RowIndex].Length()
            else
                this.ColumnIndex := this.ColumnIndex - 1
        }
        if (Direction = "Right") {
            this.ColumnIndex := mod(this.ColumnIndex, this.Controls[this.RowIndex].Length()) + 1
        }

        if (this.Controls[this.RowIndex, this.ColumnIndex].Colour != this.ToggledButtonColour)
            this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ActiveButtonColour)
        Return
    }

    HandleChangeIndex(Direction) {
        ; hardcoded logic to fix unusual index changes due to variable button widths
        if (this.RowIndex = 1) {
            if (this.ColumnIndex > 1 and Direction = "Down")
                this.ColumnIndex += 1
            else if (this.ColumnIndex > 12 and Direction = "Up")
                this.ColumnIndex -= 5
            else if (this.ColumnIndex > 8 and Direction = "Up")
                this.ColumnIndex -= 4
            else if (this.ColumnIndex > 3 and Direction = "Up")
                this.ColumnIndex := 4
        }
        else if (this.RowIndex = 2) {
            if (this.ColumnIndex > 1 and Direction = "Up")
                this.ColumnIndex -= 1
        }
        else if (this.RowIndex = 3) {
            if (this.ColumnIndex = 14 and Direction = "Down")
                this.ColumnIndex -= 1
            else if (this.ColumnIndex > 14 and Direction = "Down")
                this.RowIndex += 1

        }
        else if (this.RowIndex = 4) {
            if (this.ColumnIndex = 13 and Direction = "Up") 
                this.ColumnIndex += 1
            else if (this.ColumnIndex = 13 and Direction = "Down")
                this.ColumnIndex -= 1
        }
        else if (this.RowIndex = 5) {
            if (this.ColumnIndex = 13 and Direction = "Up") {
                this.RowIndex -= 1
                this.ColumnIndex += 3
            }
            else if (this.ColumnIndex = 13 and Direction = "Down")
                this.ColumnIndex -= 3
            else if (this.ColumnIndex = 12 and Direction = "Up")
                this.ColumnIndex += 1
            else if (this.ColumnIndex > 8 and Direction = "Down")
                this.ColumnIndex -= 4
            else if (this.ColumnIndex > 3 and Direction = "Down")
                this.ColumnIndex := 4
        }
        else if (this.RowIndex = 6) {
            if (this.ColumnIndex > 7 and Direction = "Down") {
                this.ColumnIndex += 5
            }
            else if (this.ColumnIndex > 4 and (Direction = "Up" or Direction = "Down")) {
                this.ColumnIndex += 4
            }
            else if (this.ColumnIndex = 4 and (Direction = "Up" or Direction = "Down")) {
                this.ColumnIndex := 6
            }
        }
        return
    }

    IsDPadKeyboard() {
        return this.RowIndex
    }

    RetrieveDPadSelected() {
        if this.IsDPadKeyboard() {
            return keyboard.Layout[keyboard.RowIndex, keyboard.ColumnIndex].1
        }
        return ""
    }

    UpdateGraphics(Obj, Colour){
        GuiControl, % "OSK: +C" Colour, % Obj.Progress
        GuiControl, OSK: +Redraw, % obj.Text
        Obj.Colour := Colour
        Return
    }
}

V2 (Converted):

#SingleInstance
SendMode("Input")

If (A_ScriptFullPath = A_LineFile) { ; if run as script rather than included elsewhere - for testing
    Global keyboard := OSK("dark", "qwerty")

    toggle := ObjBindMethod(keyboard, "toggle")
    move_left := ObjBindMethod(keyboard, "changeIndex", "Left")
    move_up := ObjBindMethod(keyboard, "changeIndex", "Up")
    move_down := ObjBindMethod(keyboard, "changeIndex", "Down")
    move_right := ObjBindMethod(keyboard, "changeIndex", "Right")
    send_press := SendKeyboardPress.bind()

    keyboard.Show()

    Hotkey("^Ins", "toggle")

    HotIf(keyboard.Enabled)
    Hotkey("Left", move_left)
    Hotkey("Up", move_up)
    Hotkey("Down", move_down)
    Hotkey("Right", move_right)

    HotIf(keyboard.Enabled && keyboard.IsDPadKeyboard())
    Hotkey("Enter", send_press)

    SendKeyboardPress() {
        keyboard.HandleOSKClick(keyboard.RetrieveDPadSelected())
        Return
    }
}

; for context sensitive hotkeys
#HotIf keyboard.Enabled
#HotIf keyboard.Enabled && keyboard.IsDPadKeyboard()
#HotIf

; can't use method for gui onClick
HandleOSKClick() {
    keyboard.HandleOSKClick()
    return
}

Class OSK
; Adapted from feiyue's script: https://www.autohotkey.com/boards/viewtopic.php?t=58366 
{

    __New(theme:="dark", layout:="qwerty") {
        this.Enabled := False

        this.Keys := []
        this.Controls := []
        this.Modifiers := ["LShift", "LCtrl", "LWin", "LAlt", "RShift", "RCtrl", "RWin", "RAlt", "CapsLock", "ScrollLock"]

        if (theme = "light") {
            this.Background := "FDF6E3"
            this.ButtonColour := "EEE8D5" 
            this.ButtonOutlineColour := "8E846F" 
            this.ActiveButtonColour := "DDD6C1" 
            this.SentButtonColour := "AC9D57"
            this.ToggledButtonColour := "AC9D58" ; don't set exactly the same as SentButtonColour
            this.TextColour := "657B83"
        }
        else { ; default dark theme
            this.Background := "2A2A2E"
            this.ButtonColour := "010409" 
            this.ButtonOutlineColour := "010409" 
            this.ActiveButtonColour := "1b1a20" 
            this.SentButtonColour := "553b6b"
            this.ToggledButtonColour := "553b6a" ; don't set exactly the same as SentButtonColour
            this.TextColour := "8b949e"
        }

        this.MonitorKeyPresses := ObjBindMethod(this, "MonitorAllKeys") ; can choose between MonitorModifiers and MonitorAllKeys

        this.Layout := []
        ; layout format is ["Text", width:=45, x-offset:=2]
        ; "Text" is what is sent when the button is clicked.
        ; ` and ~ had getkeystate issues so I replaced them with scancode sc029
        if (layout = "colemak-dh") {
            this.Layout.Push([ ["Esc"],["F1",,23],["F2"],["F3"],["F4"],["F5",,15],["F6"],["F7"],["F8"],["F9",,15],["F10"],["F11"],["F12"],["PrintScreen",60,10],["ScrollLock",60],["Pause",60] ])
            this.Layout.Push([ ["sc029", 30],["1"],["2"],["3"],["4"],["5"],["6"],["7"],["8"],["9"],["0"],["-"],["="],["BS", 60],["Ins",60,10],["Home",60],["PgUp",60] ])
            this.Layout.Push([ ["Tab"],["q"],["w"],["f"],["p"],["b"],["j"],["l"],["u"],["y"],["`;"],["["],["]"],["\"],["Del",60,10],["End",60],["PgDn",60] ])
            this.Layout.Push([ ["CapsLock",60],["a"],["r"],["s"],["t"],["g"],["m"],["n"],["e"],["i"],["o"],["'"],["Enter",77] ])
            this.Layout.Push([ ["LShift",90],["x"],["c"],["d"],["v"],["z"],["k"],["h"],["`,"],["."],["/"],["RShift",94],["",60,72] ])
            this.Layout.Push([ ["LCtrl",60],["LWin",60],["LAlt",60],["Space",222],["RAlt",60],["RWin",60],["App",60],["RCtrl",60],["Left",60,10],["Down",60],["Right",60] ])
        }
        else { ; default qwerty
            this.Layout.Push([ ["Esc"],["F1",,23],["F2"],["F3"],["F4"],["F5",,15],["F6"],["F7"],["F8"],["F9",,15],["F10"],["F11"],["F12"],["PrintScreen",60,10],["ScrollLock",60],["Pause",60] ])
            this.Layout.Push([ ["sc029", 30],["1"],["2"],["3"],["4"],["5"],["6"],["7"],["8"],["9"],["0"],["-"],["="],["BS", 60],["Ins",60,10],["Home",60],["PgUp",60] ])
            this.Layout.Push([ ["Tab"],["q"],["w"],["e"],["r"],["t"],["y"],["u"],["i"],["o"],["p"],["["],["]"],["\"],["Del",60,10],["End",60],["PgDn",60] ])
            this.Layout.Push([ ["CapsLock",60],["a"],["s"],["d"],["f"],["g"],["h"],["j"],["k"],["l"],["`;"],["'"],["Enter",77] ])
            this.Layout.Push([ ["LShift",90],["z"],["x"],["c"],["v"],["b"],["n"],["m"],["`,"],["."],["/"],["RShift",94],["Up",60,72] ])
            this.Layout.Push([ ["LCtrl",60],["LWin",60],["LAlt",60],["Space",222],["RAlt",60],["RWin",60],["App",60],["RCtrl",60],["Left",60,10],["Down",60],["Right",60] ])
        }

        ; ; Optionally sets alternate text for the button actions named in this.Layout - doesn't have to be in same order as layout
        this.PrettyName := { PrintScreen: Prt Scr, ScrollLock: Scr Lk
                                , sc029: ~, 1: 1 !, 2: 2 @, 3: 3 #, 4: 4 $, 5: 5 `%, 6: 6 ^, 7: 7 &&, 8: 8 *, 9: 9 (, 0: 0 ), -: - _, +: = +, BS: ←, PgUp: Pg Up, PgDn: Pg Dn
                                , q: Q, w: W, e: E, r: R, t: T, y: Y, u: U, i: I, o: O, p: P, [: [ {, ]: ] }, \: \ |
                                , CapsLock: Caps, a: A, s: S, d: D, f: F, g: G, h: H, j: J, k: K, l: L, `;: `; :, ': ' 
                                , LShift: Shift, z: Z, x: X, c: C, v: V, b: B, n: N, m: M, `,: `, <, .: . >, /: / ?, RShift: Shift
                                , LCtrl: Ctrl, LWin: Win, LAlt: Alt, Space:  , RAlt: Alt, RWin: Win, AppsKey: App, RCtrl: Ctrl, Up: ↑, Down: ↓, Left: ←, Right: ""}
        this.Make()
    }

    SetTimer(TimerID, Period) {
        Timer := this[TimerID]
        SetTimer(Timer,Period)
        return
    }

    Make() {
        OSK.Opt("+AlwaysOnTop -DPIScale +Owner -Caption +E0x08000000")
        OSK.SetFont("s12", "Verdana")
        OSK.MarginX := "10", OSK.MarginY := "10"
        OSK.BackColor := this.Background
        SS_CenterTextInBox := 0x200 ; styling adjustment
        For Index, Row in this.Layout {
            For i, Button in Row {
                Width := Button.2 ? Button.2 : 45 
                HorizontalOffset := Button.3 ? Button.3 : 2
                RelativePosition := Index <= 2 and i = 1 ? "xm" : i=1 ? "xm y+2" : "x+" HorizontalOffset
                ButtonText := this.PrettyName[Button.1] ? this.PrettyName[Button.1] : Button.1

                ; Control handling is from Hellbent's script: https://www.autohotkey.com/boards/viewtopic.php?t=87535
                ogc% Button.1bottomt := OSK.Add("Text", RelativePosition " c" this.TextColour " w" Width " h" 30 " -Wrap BackgroundTrans Center  " SS_CenterTextInBox, Button.1)
                ogc% Button.1bottomt.OnEvent("Click", HandleOSKClick.Bind("Normal"))
                bottomt := ogc% Button.1bottomt.hwnd ; handles the click
                ogc100p" := OSK.Add("Progress", "xp yp w" Width " h" 30 " Disabled Background" this.ButtonOutlineColour " c" this.ButtonColour ", "100")
                p" := ogc100p".hwnd
                ogc% ButtonTexttopt := OSK.Add("Text", "xp yp c" this.TextColour " w" Width " h" 30 " -Wrap BackgroundTrans Center " SS_CenterTextInBox, ButtonText)
                topt := ogc% ButtonTexttopt.hwnd ; displays the pretty name

                this.Keys[Button.1] := [Index, i]
                this.Controls[Index, i] := {Progress: p, Text: topt, Label: HandlePress, Colour: this.ButtonColour}
            }
        }	
        Return
    }

    Show() {
        this.Enabled := True

        ; reset active key
        this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ButtonColour)
        this.ColumnIndex := 0
        this.RowIndex := 0

        CurrentMonitorIndex := this.GetCurrentMonitorIndex()
        DetectHiddenWindows(true)
        OSK.Opt("+LastFound")
        OSK.Show("Hide")
        GUI_Hwnd := WinExist()
        this.GetClientSize(GUI_Hwnd, &GUI_Width, &GUI_Height)
        DetectHiddenWindows(false)

        GUI_X := this.CoordXCenterScreen(GUI_Width,CurrentMonitorIndex)
        GUI_Y := this.CoordYCenterScreen(GUI_Height,CurrentMonitorIndex)

        OSK.Title := "On-Screen Keyboard"
        OSK.Show("x" GUI_X " y" GUI_Y " NA")

        this.SetTimer("MonitorKeyPresses", 30)

        Return
    }

    Hide() {
        this.Enabled := False
        OSK := Gui()
        OSK.Hide()
        this.SetTimer("MonitorKeyPresses", "off")
        return
    }

    Toggle() {
        If this.Enabled {
            this.Hide()
        }
        Else {
            this.Show()
        }
        Return
    }

    ; for centering keyboard on screen
    GetCurrentMonitorIndex() {
        CoordMode("Mouse", "Screen")
        MouseGetPos(&mx, &my)
        monitorsCount := SysGet(80)

        Loop monitorsCount{
            MonitorGet(A_Index, &monitorLeft, &monitorTop, &monitorRight, &monitorBottom)
            if (monitorLeft <= mx && mx <= monitorRight && monitorTop <= my && my <= monitorBottom){
                Return A_Index
                }
            }
        Return 1
    }

    CoordXCenterScreen(WidthOfGUI,ScreenNumber) {
        MonitorGet(ScreenNumber, &Mon1Left, &Mon1Top, &Mon1Right, &Mon1Bottom)
        return ((Mon1Right-Mon1Left - WidthOfGUI) / 2) + Mon1Left
    }

    CoordYCenterScreen(HeightofGUI,ScreenNumber) {
        MonitorGet(ScreenNumber, &Mon1Left, &Mon1Top, &Mon1Right, &Mon1Bottom)
        return (Mon1Bottom - 80 - HeightofGUI)
    }

    GetClientSize(hwnd, &w, &h) {
        VarSetStrCapacity(&rc, 16) ; V1toV2: if 'rc' is NOT a UTF-16 string, use 'rc := Buffer(16)' and replace all instances of 'StrPtr(rc)' with 'rc.Ptr'
        DllCall("GetClientRect", "uint", hwnd, "uint", StrPtr(rc))
        w := NumGet(rc, 8, "int")
        h := NumGet(rc, 12, "int")
        Return
    }

    HandleOSKClick(Key:="") {
        if not Key {
            Key := A_GuiControl
        }
        if (this.IsModifier(Key)) {
            this.SendModifier(Key)
        }
        else {
            this.SendPress(Key)
        }
        return
    }

    IsModifier(Key) {
        if (Key = "LShift" 
            or Key = "LCtrl" 
            or Key = "LAlt" 
            or Key = "LWin" 
            or Key = "RShift" 
            or Key = "RCtrl" 
            or Key = "RAlt" 
            or Key = "RWin"
            or Key = "CapsLock"
            or Key = "ScrollLock")
            return True
        else
            return False
    }

    MonitorModifiers() {
        For _, Modifier in this.Modifiers {
            this.MonitorKey(Modifier)
        }
        Return
    }


    MonitorAllKeys() {
        For _, Row in this.Layout {
            For i, Button in Row {
                this.MonitorKey(Button.1)
            }
        }
        Return
    }

    MonitorKey(Key) {
        if (Key = "CapsLock" or Key = "ScrollLock" or Key = "Pause")
            KeyOn := GetKeyState(Key, "T")
        else
            KeyOn := GetKeyState(Key)
        KeyRow := this.Keys[Key][1]
        KeyColumn := this.Keys[Key][2]
        if (KeyOn and this.Controls[KeyRow, KeyColumn].Colour != this.ToggledButtonColour) {
            this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ToggledButtonColour)
        }
        else if (not KeyOn and this.Controls[KeyRow, KeyColumn].Colour = this.ToggledButtonColour) {
            if (KeyRow = this.RowIndex and KeyColumn = this.ColumnIndex)
                this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ActiveButtonColour)
            else
                this.UpdateGraphics(this.Controls[KeyRow, KeyColumn], this.ButtonColour)
        }
        Return
    }

    SendPress(Key) {
        SentRow := this.Keys[Key][1]
        SentColumn := this.Keys[Key][2]
        OldColor := this.Controls[SentRow][SentColumn].Colour
        this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.SentButtonColour)
        SendInput("{Blind}{" Key "}")
        For _, Modifier in this.Modifiers {
            ModifierOn := GetKeyState(Modifier)
            if (ModifierOn)
                SendInput("{" Modifier " up}")
        }
        Sleep(100)
        if (SentRow = this.RowIndex and SentColumn = this.ColumnIndex)
            this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.ActiveButtonColour)
        else
            this.UpdateGraphics(this.Controls[SentRow, SentColumn], this.ButtonColour)
        Return
    }

    SendModifier(Key) {
        ModifierRow := this.Keys[Key][1]
        ModifierColumn := this.Keys[Key][2]
        if (Key = "CapsLock")
            SetCapsLockState(not GetKeyState(Key, "T"))
        else if (Key = "ScrollLock")
            SetScrollLockState(not GetKeyState(Key, "T"))
        else {
            ModifierOn := GetKeyState(Key)
            if (ModifierOn)
                SendInput("{" Key " up}")
            else 
                SendInput("{" Key " down}")
        }
        return
    }


    ChangeIndex(Direction) {
        if (not this.RowIndex) {
            this.RowIndex := 4
            this.ColumnIndex := 7
        }

        if (this.Controls[this.RowIndex, this.ColumnIndex].Colour != this.ToggledButtonColour)
            this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ButtonColour)

        this.HandleChangeIndex(Direction)

        if (Direction = "Up") {
            if this.RowIndex = 1
                this.RowIndex := this.Controls.Length
            else
                this.RowIndex := this.RowIndex - 1
            this.ColumnIndex := min(this.ColumnIndex, this.Controls[this.RowIndex].Length)
        }
        if (Direction = "Down") {
            this.RowIndex := mod(this.RowIndex, this.Controls.Length) + 1
            this.ColumnIndex := min(this.ColumnIndex, this.Controls[this.RowIndex].Length)
        }
        if (Direction = "Left") {
            if this.ColumnIndex = 1
                this.ColumnIndex := this.Controls[this.RowIndex].Length
            else
                this.ColumnIndex := this.ColumnIndex - 1
        }
        if (Direction = "Right") {
            this.ColumnIndex := mod(this.ColumnIndex, this.Controls[this.RowIndex].Length) + 1
        }

        if (this.Controls[this.RowIndex, this.ColumnIndex].Colour != this.ToggledButtonColour)
            this.UpdateGraphics(this.Controls[this.RowIndex, this.ColumnIndex], this.ActiveButtonColour)
        Return
    }

    HandleChangeIndex(Direction) {
        ; hardcoded logic to fix unusual index changes due to variable button widths
        if (this.RowIndex = 1) {
            if (this.ColumnIndex > 1 and Direction = "Down")
                this.ColumnIndex += 1
            else if (this.ColumnIndex > 12 and Direction = "Up")
                this.ColumnIndex -= 5
            else if (this.ColumnIndex > 8 and Direction = "Up")
                this.ColumnIndex -= 4
            else if (this.ColumnIndex > 3 and Direction = "Up")
                this.ColumnIndex := 4
        }
        else if (this.RowIndex = 2) {
            if (this.ColumnIndex > 1 and Direction = "Up")
                this.ColumnIndex -= 1
        }
        else if (this.RowIndex = 3) {
            if (this.ColumnIndex = 14 and Direction = "Down")
                this.ColumnIndex -= 1
            else if (this.ColumnIndex > 14 and Direction = "Down")
                this.RowIndex += 1

        }
        else if (this.RowIndex = 4) {
            if (this.ColumnIndex = 13 and Direction = "Up") 
                this.ColumnIndex += 1
            else if (this.ColumnIndex = 13 and Direction = "Down")
                this.ColumnIndex -= 1
        }
        else if (this.RowIndex = 5) {
            if (this.ColumnIndex = 13 and Direction = "Up") {
                this.RowIndex -= 1
                this.ColumnIndex += 3
            }
            else if (this.ColumnIndex = 13 and Direction = "Down")
                this.ColumnIndex -= 3
            else if (this.ColumnIndex = 12 and Direction = "Up")
                this.ColumnIndex += 1
            else if (this.ColumnIndex > 8 and Direction = "Down")
                this.ColumnIndex -= 4
            else if (this.ColumnIndex > 3 and Direction = "Down")
                this.ColumnIndex := 4
        }
        else if (this.RowIndex = 6) {
            if (this.ColumnIndex > 7 and Direction = "Down") {
                this.ColumnIndex += 5
            }
            else if (this.ColumnIndex > 4 and (Direction = "Up" or Direction = "Down")) {
                this.ColumnIndex += 4
            }
            else if (this.ColumnIndex = 4 and (Direction = "Up" or Direction = "Down")) {
                this.ColumnIndex := 6
            }
        }
        return
    }

    IsDPadKeyboard() {
        return this.RowIndex
    }

    RetrieveDPadSelected() {
        if this.IsDPadKeyboard() {
            return (AHKv1v2_Temp := keyboard.Layout[keyboard.RowIndex, keyboard.ColumnIndex].1, AHKv1v2_Temp) ; V1toV2: Wrapped Multi-statement return with parentheses
        }
        return ""
    }

    UpdateGraphics(Obj, Colour){
        ogc% Obj.Progress.Options("+C`" Colour")
        ogc% obj.Text.Options("+Redraw")
        Obj.Colour := Colour
        Return
    }
}
Error: Missing "}" before "]"

Text:	this.PrettyName := { PrintScreen: Prt Scr, ScrollLock: Scr Lk , sc029: ~, 1: 1 !…
Line:	98
File:	D:\Users\Administrator\Documents\testV2.ahk

The program will exit.

https://github.com/henrystern/ahk_on_screen_keyboard
The solution provided by autohotkey plus plus is to replace the key names of MAP with the version without double quotes, but some key names that use special symbols seem unable to do so.

Banaanae added a commit that referenced this issue Jan 9, 2025
Banaanae added a commit that referenced this issue Jan 9, 2025
when hwnd is used
see #326
@Banaanae
Copy link
Collaborator

Object literals don't allow those sorts of names now. here is the corrected map

this.PrettyName := map("PrintScreen", "Prt Scr", "ScrollLock", "Scr Lk", "sc029", "~", "1", "1 !", "2", "2 @", "3", "3 #", "4", "4 $", "5", "5 `%", "6", "6 ^", "7", "7 &&", "8", "8 *", "9", "9 (", "0", "0 )", "-", "- _", "+", "= +", "BS", "", "PgUp", "Pg Up", "PgDn", "Pg Dn", "q", "Q", "w", "W", "e", "E", "r", "R", "t", "T", "y", "Y", "u", "U", "i", "I", "o", "O", "p", "P", "[", "[ {", "]", "] )", "\", "\ |", "CapsLock", "Caps", "a", "A", "s", "S", "d", "D", "f", "F", "g", "G", "h", "H", "j", "J", "k", "K", "l", "L", "`;", "`; :", "'", "' `"", "LShift", "Shift", "z", "Z", "x", "X", "c", "C", "v", "V", "b", "B", "n", "N", "m", "M", ",", ", <", ".", ". >", "/", "/ ?", "RShift", "Shift", "LCtrl", "Ctrl", "LWin", "Win", "LAlt", "Alt", "Space", " ", "RAlt", "Alt", "RWin", "Win", "AppsKey", "App", "RCtrl", "Ctrl", "Up", "", "Down", "", "Left", "", "Right", "")

Here's also the corrected conversion
https://gist.github.com/Banaanae/97a8e7ca2c75606acacaba88b9d86c23
More work still needs to be done but it "works"

@daiaji
Copy link
Author

daiaji commented Jan 14, 2025

Thank you for your help. Previously, I used an LLM to assist with part of the migration of OSK.ahk from v1 to v2. However, I am encountering some errors. When using the physical keyboard's arrow keys to control the virtual keyboard, the selection highlight moves correctly. But when I select a key on the virtual keyboard and press Enter to trigger that key, an error occurs. The LLM does not seem to be able to resolve this issue. Could you please advise me on how to fix it?

Error: This value of type "String" has no property named "RowIndex".

243: }
246: {
▶ 247: Key := this.Layout[GuiCtrlObj.RowIndex][GuiCtrlObj.ColumnIndex][1]
248: If this.IsModifier(Key)
248: {

Call stack:
F:\OSKv2_1.ahk (247) : [OSK.Prototype.HandleOSKClick] Key := this.Layout[GuiCtrlObj.RowIndex][GuiCtrlObj.ColumnIndex][1]
> Enter

https://gist.github.com/daiaji/6dad0919bae73dbad8cd77ba8741dd5d


I'm experiencing an error with the modified code you provided. When I press the directional keys on my physical keyboard, I'm getting the following error:

Error: Too many parameters passed to function.


Specifically: OSK.Prototype.ChangeIndex


440: Return
441: }
▶	443: Exit


Call stack:
> Right

This error indicates that the OSK.Prototype.ChangeIndex function is being called with too many arguments. It seems to be occurring specifically when I press the right direction key, within a function called 'Right', and happens right before the code tries to 'return' and 'exit'. Before attempting other solutions, I'd like to try using an LLM to see if it can help me fix this.

@Banaanae
Copy link
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants