Search code examples
autohotkeykeymapping

How to realize MacOS-like (EN Intl) german umlauts behavior?


I get a new Windows 10 Notebook, while I ran a MBP for long time. On MacOs I use an US Intl kezmap and I really like the shortcuts to write the german umlauts.

On MacOS I use {RAlt} + {u} followed by {a},{o},{u} or {s} to write the corresponding german Umlaut. To write a upper case umlaut I have to press {Shift} + {a},{o} or {u}.

I try to write an AHK script to reproduce this behavior, but unfortunately I have no success.

What I currently have created is this.

;Umlaut
#Persistent

    RAlt & a::
        GetKeyState, state, Shift
        if state = U
        SendInput {ASC 0228}            ;RAlt+a = lower case a-umlaut
        else Send, {ASC 0196}           ;RAlt+Shift+a = UPPER CASE A-umlaut
    return

    RAlt & o:: 
        GetKeyState, state, Shift
        if state = U
        SendInput {ASC 0246}            ;RAlt+o = lower case o-umlaut
        else Send, {ASC 0214}           ;RAlt+Shift+o = UPPER CASE O-umlaut
    return

    RAlt & u:: 
        GetKeyState, state, Shift
        if state = U
        SendInput {ASC 0252}            ;RAlt+u = lower case u-umlaut
        else Send, {ASC 0220}           ;RAlt+Shift+u = UPPER CASE U-umlaut
    return

    RAlt & s:: Send, {ASC 0223}     ;RAlt+s = RAlt+s, Eszett
    return

It works, but I really want the key combos described above. How can I reach this goal?


Solution

  • First to fix the code seen in the original post, it's quite legacy and uses a lot of weird approaches. To list and fix all of them:

    #Persistent is doing nothing for us. It's useless here.

    The RAlt & key:: type hotkeys labels should use modifiers, and look like this >!key::.
    The >! modifier means right alt.

    Usage of the legacy GetKeyState command should be replaced by the GetKeyState() function.
    Though, this isn't actually needed at all. The shift hotkey modifier + should just be used like so:

    >!a::SendInput, ä
    >!+a::SendInput, Ä
    >!o::SendInput, ö
    >!+o::SendInput, Ö
    >!u::SendInput, ü
    >!+u::SendInput, Ü
    >!s::SendInput, ß
    

    I also ditched the using the ASC codes, since I see no reason to use them.
    I guess it would make the script file more flexible to be saved under a more primitive encoding, but eh.

    So this was the code in your post done right.


    And now to implement what you were actually looking to do:
    Pressing RAlt + u enters a umlaut typing mode.

    Easiest way of doing this, that I can think of, is just using creating context sensitive hotkeys with #If.
    Normally I wouldn't recommend the usage of #If in AHK v1, due to its downsides (as documented in the documentation), but it'll be more than fine for a little script like this. And it makes this a lot easier and neater.

    So first lets create a RAlt + u hotkey, that sets the state of some variable to true:

    >!u::UmlautTypingMode := true
    
    Then we can create context sensitive hotkeys by always first checking the value of that variable:
    >!u::UmlautTypingMode := true
    
    #If, UmlautTypingMode ;start context sensitive hotkeys (if the variable value evaluates to true)
    a::
        SendInput, ä
        UmlautTypingMode := false
    return
    +a::
        SendInput, Ä
        UmlautTypingMode := false
    return
    o::
        SendInput, ö
        UmlautTypingMode := false
    return
    +o::
        SendInput, Ö
        UmlautTypingMode := false
    return
    <!u::return
    u::
        SendInput, ü
        UmlautTypingMode := false
    return
    +u::
        SendInput, Ü
        UmlautTypingMode := false
    return
    s::
        SendInput, ß
        UmlautTypingMode := false
    return
    #If ;end context sensitive hotkeys
    

    As you can see, we unfortunately had to get rid of those sweet clean one-liner hotkeys, since false every time, and end code execution with a return.
    Of course this could be compacted with some more advanced AHK, but I'll keep it simple.
    And the small little <!u::return line inside the there is to make the initial RAlt + u press not produce a ü.


    So that should be it. Works fine on my end at least.
    Though, I've just now realized that with "{RAlt} + {u} followed by {a},{o},{u} or {s}" you may have meant that you'd keep typing umlauts for as long as RAlt is held down. If this was the case, the code needs some adjusting.
    Right now you press and release RAlt + u to enter the umlaut typing mode.


    Finally, as a bonus, here's the the code compacted down to a one-liner.
    I know, it's stupid, I just couldn't resist, I love making these haha.

    >!u::UmlautTypingMode := true
    #If, UmlautTypingMode
    a::
    +a::
    o::
    +o::
    u::
    +u::
    s::SendInput, % ((A_ThisHotkey = "s"), UmlautTypingMode := false) ? "ß" : ("¨" ((A_ThisHotkey ~= "\+") ? Format("{:U}", SubStr(A_ThisHotkey, 0)) : SubStr(A_ThisHotkey, 0)))
    >!u::return
    #If
    

    Also, no idea if this will work on different keyboard layouts.
    Works for me at least.


    EDIT:
    A timeout requested was the comments. They're very easy and convenient to do with a timer. So, we change our >!u::UmlautTypingMode := true hotkey to this:

    >!u::
        UmlautTypingMode := true
        SetTimer, Timeout, -2000
    return
    

    Negative number in the timer's period means it runs just once after 2000ms.

    And now we of course have to also create the label (or function, I'll use a label here to keep it more simple) called Timeout (it's not a built in thing, we could've used any name here).

    Timeout:
        UmlautTypingMode := false
    return
    

    This label can be placed for example at the bottom of the script.