emilk / egui

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
https://www.egui.rs/
Apache License 2.0
21.92k stars 1.58k forks source link

requesting default focus #1655

Open lunixbochs opened 2 years ago

lunixbochs commented 2 years ago

I'm playing with dialog prompts in egui. It would be nice to be able to request that a widget receives "default focus" upon appearing, so the user can just press enter to activate the default button without pressing tab first. (e.g. when a window first appears, focus the "ok" button)

I tried this pattern, which works ok, though there's weirdness (see the end of this comment).

let button = ui.button("OK");
{
    let mut memory = ui.memory();
    if memory.focus() == None {
        memory.request_focus(button.id);
    }                      
}

A method like ui.memory().default_focus(widget.id) that sets focus only if it's not already set, may work well as a starting point for this.

I think one alternative is tracking a "first open" flag somewhere in my own code for each UI container that cares about default focus, however that may not handle something like a multi-page wizard where the contents change without creating a new container.

Weirdness with my memory approach:

  1. Clicking in a background section will flash the widget's focus for a frame. (I think I'd prefer in this case that clicking on the UI background doesn't affect tab focus, but it's probably more nuanced than that, e.g. input boxes?)
  2. Clicking any other button unsets the focus afterwards, which re-focuses the default button and flashes both buttons for a frame.
fundon commented 1 year ago

In this case, need a flag to set the input is focused.

#[derive(Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct AddWindow {
    name: String,
    autofocus: bool,
}

impl Window for AddWindow {
    fn show(
        &mut self,
        data: Option<Message>,
    ) {
        // first rendered
        if let Some(Message::Normal) = data {
            self.autofocus = true;
        }
        egui::Window::new("the window")
            .resizable(false)
            .default_width(280.0)
            .open(true)
            .show(ctx, |ui| self.ui(ui));
    }
}

impl View for AddWindow {
    fn ui(&mut self, ui: &mut egui::Ui) {
        ui.horizontal(|ui| {
            ui.add_sized((50., 24.), egui::Label::new("Name:"));
            let resp =
                ui.add(egui::TextEdit::singleline(&mut self.name).hint_text("Write folder name"));
            // turn off `autofocus`
            if self.autofocus {
                self.autofocus = false;
                ui.memory().request_focus(resp.id);
            }
        });
    }
}
tedsteen commented 5 months ago

Any updates here, is this something you do manually or is there an egui way now?

mackler commented 5 months ago

Any updates here, is this something you do manually or is there an egui way now?

I, too, am interested in this.