Arrow keys should move button selection in review

When reviewing cards on my laptop (which has no numerical keyboard), it would be nice if there were a way to give either of the four different answers using only the right hand in a somewhat fixed position.

When the front side of any card is shown, the “Show Answer” button is outlined in blue, telling me that I can press ENTER (or SPACE) to activate it and show the back side of the card. When I do that, the four ease of answer buttons will show, “Good” being likewise outlined in blue to show that I can press ENTER to select that option.

It would be nice if at this point the arrow keys on my keyboard could be used to move the outline to the other answer buttons (to be finalized by pressing ENTER). That way I could generally review cards using only the right hand in somewhat the same position on the keyboard, instead of moving it over to the left side of the keyboard to pick a number (or TAB) when I want to answer something besides “Good”.

This can be accomplished by the following add-on:

from aqt import gui_hooks
from aqt.reviewer import ReviewerBottomBar

def myhook(web_content, context):
    if not isinstance(context, ReviewerBottomBar):
        return
    js = """
    document.addEventListener('keydown', function(e) {
        let buttons = document.querySelectorAll('#middle tr button')
        let focused = -1;
        for(let i = 0; i < buttons.length; i++) {
            if(document.activeElement === buttons[i]) {
                focused = i;
                break;
            }
        }
        if(e.key === 'ArrowLeft') {
            let toFocus;
            if(!focused || focused === -1) {
                toFocus = buttons.length - 1;
            }
            else {
                toFocus = focused - 1;
            }
            buttons[toFocus].focus();
        }
        else if(e.key === 'ArrowRight') {
            buttons[(focused+1) % buttons.length].focus();
        }
    });
    """
    web_content.body += f"<script>{js}</script>"


gui_hooks.webview_will_set_content.append(myhook)

This has the limitation that focus needs to be on the bottom bar, which is the case after you show the answer, but won’t be if you click somewhere on the card content after that.

2 Likes

Thanks a lot for the kind reply, but I don’t know how I would turn those lines of code into an add-on.

I tried copying them into a text file and giving it an .ankiaddon extension, but Anki gives an error message on importing.

https://ankiweb.net/shared/info/850294128

This Anki add-on is a better alternative to selecting answers with the keyboard arrows

2 Likes

Put the code in a file called __init__.py in a new folder and move that folder to Anki’s addons folder.
The addons folder can be accessed from Tools > Add-ons > View Files.

1 Like

Thanks a bunch, that works perfectly! :slight_smile: I still do think it would be an intuitive behaviour to be in the main program as well, though. But I’m happy it works.

@neribr: I’m sure the add-on is good, by why is it better than using the keyboard arrows?

1 Like

This (navigation by arrow keys as described above) broke with the update to 2.1.41 – any way to make it work again?

The bottom bar is no longer focused by default.

Try this updated code:

from aqt import gui_hooks, mw
from aqt.reviewer import ReviewerBottomBar

def myhook(web_content, context):
    if not isinstance(context, ReviewerBottomBar):
        return
    js = """
    document.addEventListener('keydown', function(e) {
        let buttons = document.querySelectorAll('#middle tr button')
        let focused = -1;
        for(let i = 0; i < buttons.length; i++) {
            if(document.activeElement === buttons[i]) {
                focused = i;
                break;
            }
        }
        if(e.key === 'ArrowLeft') {
            let toFocus;
            if(!focused || focused === -1) {
                toFocus = buttons.length - 1;
            }
            else {
                toFocus = focused - 1;
            }
            buttons[toFocus].focus();
        }
        else if(e.key === 'ArrowRight') {
            buttons[(focused+1) % buttons.length].focus();
        }
    });
    """
    web_content.body += f"<script>{js}</script>"

gui_hooks.webview_will_set_content.append(myhook)
gui_hooks.reviewer_did_show_answer.append(lambda _: mw.bottomWeb.setFocus())

2 Likes

That works, thank you very much! :slight_smile:

10c10
<         let focused = -1;
---
>         let focused = 2;
36d35
< 
1 Like