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

Add mnemonic support #429

Open
Vanadiae opened this issue May 22, 2021 · 8 comments
Open

Add mnemonic support #429

Vanadiae opened this issue May 22, 2021 · 8 comments
Labels
feature New feature or request

Comments

@Vanadiae
Copy link

(Probably part of #167)

Egui is currently nice enough to use with Tab/Shift+Tab, but it would be even nicer if there was support for mnemonics (also called accelerators sometimes) because it allows activating any UI element (e.g. button, text entry) without tabbing through all the UI to reach it. In the GTK toolkit (and maybe others? I only know GTK so I can't tell but it's likely the same for e.g. Qt), it is indicated by adding an underscore before the letter that should be used as mnemonic, so for example _Save or P_references.
I think a proper place to have the mnemonic indicated would be the WidgetInfo's label field. I suppose that if someone really wants to use a raw underscore in the label then an exception with a double underscore could be added.

A proper implementation of mnemonics should make sure of the following things:

  • Holding Alt should underline the mnemonics letter on all the widgets
  • Pressing Alt and a character should focus and activate (if applicable) the element linked to the mnemonic, if there is only one widget with this mnemonic.
  • in case multiple elements share the same mnemonic, none of them should be activated when using the mnemonic but instead the focus should switch between them on each mnemonic activation, which would allow to activate one particular element even if they share the mnemonic.
  • Caps should be treated as non caps so that _Save doesn't require pressing Alt+Shift+S but instead just Alt+S.
  • The keyboard layout and the various modifiers needed to enter the given character shouldn't break things apart. For example in my keyboard layout the 2 mnemonic requires me to press Alt+Shift+<the key where the 2 is in, in the top row of the keyboard>

Ideally it should be possible to have a standalone label that would handle the mnemonic for an other widget (like a label next to a single line entry), but I don't think that would be easily compatible with the immediate-mode nature of egui?

Please let me know if anything is unclear :)

@jgarvin
Copy link

jgarvin commented Oct 12, 2021

Currently for web navigation I use the Vimium plugin for Chrome. When I press F, all links get dynamically highlighted with a letter, and then pressing that letter will click that link. Importantly this doesn't require any deliberate accessibility effort from web developers to work. I was thinking of opening an issue for making that functionality work with egui, which I believe would involve it tricking the browser into thinking there are links wherever there are clickable widgets. I don't know what the conventions are like on native platforms for this... I've just gotten used to all apps having poor accessibility and just making voice commands that map to whatever keyboard shortcuts the app has.

So a couple questions:

  • Does the web version of this merit a separate issue?
  • Is it better for egui to just generate a corresponding key to hit on the fly in response to the user pressing a common "I desire to click something" button, like Vimium?

@akhilman
Copy link

Is it better for egui to just generate a corresponding key to hit on the fly in response to the user pressing a common "I desire to click something" button, like Vimium?

The developer-defined mnemonic is stable and does not require reading before being pressed.
Try this in gimp: Alt+F, N, Alt+O, Alt+R, R, O, P, Alt+O, and you will get same result every time.

@emilk
Copy link
Owner

emilk commented Oct 13, 2021

Mnemonics with underscore under letters would be a nice addition, but needs to be implemented separately for Button, Checkbox etc. I think I would prefer something explicit like Button::new("Save").shortcut(Key::S). The button/checkbox would then find the first matching letter to highlight if Alt is pressed, and click itself if Alt+S is pressed. It is not a lot of work to implement (now that there is a text layout engine where one can easily underline pieces of the text).

@emilk
Copy link
Owner

emilk commented Oct 13, 2021

* Does the web version of this merit a separate issue?

If we want to support Vimium etc we need to create fake anchor elements in the web view that matches the egui hyperlinks/buttons. This is a very big task, and does require a separate issue to discuss.

@emilk emilk added the feature New feature or request label Oct 13, 2021
@jgarvin
Copy link

jgarvin commented Oct 13, 2021

I think I would prefer something explicit like Button::new("Save").shortcut(Key::S).

I don’t know if this is in scope for egui, but I would guess part of the motivation for the string approach would be internationalization. Usually frameworks for it help substitute all your string literals appropriately, and different countries have different keyboards. I’m speculating though, never had to deal with it.

@akhilman
Copy link

akhilman commented Oct 14, 2021 via email

@albx79
Copy link

albx79 commented Jan 25, 2022

Java swing works the same, with an underscore in the label string used to specify the accelerator. This is good for internationalization.

@emilk
Copy link
Owner

emilk commented Jan 25, 2022

Worth noting here: keyboard shortcuts can only work for things that are actually visible on screen. If a menu isn't open, the content code is never run, so any shortcuts to a menu-item (say File -> Save) must be handled somewhere else than where the button actually is.

As for layout: if you want to have underscore mean "add an underline to the next character", then it is pretty easy to write a function that creates a button with such an underline:

/// `if mnemonic_button(ui, "_Save").clicked() { … }`
fn mnemonic_button(ui: &Ui, text: &str) -> Response {
    let underscore_pos = text.find('_').unwrap(); // TODO: error handling
    let before = &text[..underscore_pos];
    let underscore = &text[underscore_pos..underscore_pos+1];
    let after = &text[underscore_pos+1..];
    
    let font_id = TextStyle::Button.resolve(&ui);    
    let color = ui.visuals().text_color();
    let underline = Stroke::new(1.0, color);
    let text_format = TextFormat { font_id, color, ..Default::default() };

    let mut job = LayoutJob::default();
    job.append(before, 0.0, text_format);
    job.append(underscore, 0.0, TextFormat { underline, ..text_format });
    job.append(after, 0.0, text_format);

    ui.button(job)
}

perhaps good enough for a start?

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

No branches or pull requests

5 participants