Check if TextEdit lost keyboard focus with response.lost_kb_focus

This commit is contained in:
Emil Ernerfeldt 2020-11-09 18:42:54 +01:00
parent c999ed038a
commit d2b5730784
6 changed files with 64 additions and 13 deletions

View file

@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Added ⭐
* You can now check if a `TextEdit` lost keyboard focus with `response.lost_kb_focus`.
### Changed 🔧
* Pressing enter in a single-line `TextEdit` will now surrender keyboard focus for it

View file

@ -430,6 +430,12 @@ impl Context {
let hovered = self.contains_mouse(layer_id, clip_rect, interact_rect);
let has_kb_focus = id.map(|id| self.memory().has_kb_focus(id)).unwrap_or(false);
// If the the focus is lost after the call to interact,
// this will be `false`, so `TextEdit` also sets this manually.
let lost_kb_focus = id
.map(|id| self.memory().lost_kb_focus(id))
.unwrap_or(false);
if id.is_none() || sense == Sense::nothing() || !layer_id.allow_interaction() {
// Not interested or allowed input:
return Response {
@ -441,6 +447,7 @@ impl Context {
double_clicked: false,
active: false,
has_kb_focus,
lost_kb_focus,
};
}
let id = id.unwrap();
@ -466,6 +473,7 @@ impl Context {
double_clicked: false,
active: false,
has_kb_focus,
lost_kb_focus,
};
if sense.click && memory.interaction.click_id.is_none() {
@ -496,6 +504,7 @@ impl Context {
double_clicked: false,
active: false,
has_kb_focus,
lost_kb_focus,
}
}
} else if self.input.mouse.released {
@ -509,6 +518,7 @@ impl Context {
double_clicked: clicked && self.input.mouse.double_click,
active,
has_kb_focus,
lost_kb_focus,
}
} else if self.input.mouse.down {
Response {
@ -520,6 +530,7 @@ impl Context {
double_clicked: false,
active,
has_kb_focus,
lost_kb_focus,
}
} else {
Response {
@ -531,6 +542,7 @@ impl Context {
double_clicked: false,
active,
has_kb_focus,
lost_kb_focus,
}
}
}

View file

@ -133,12 +133,16 @@ impl Widgets {
ui.horizontal(|ui| {
ui.label("Single line text input:");
ui.add(
let response = ui.add(
TextEdit::new(&mut self.single_line_text_input)
.multiline(false)
.id_source("single line"),
);
}); // TODO: .on_hover_text("Enter text to edit me")
if response.lost_kb_focus {
// The user pressed enter.
}
});
ui.label("Multiline text input:");
ui.add(TextEdit::new(&mut self.multiline_text_input).id_source("multiline"));

View file

@ -71,7 +71,7 @@ pub struct Memory {
/// If the user releases the button without moving the mouse we register it as a click on `click_id`.
/// If the cursor moves too much we clear the `click_id` and start passing move events to `drag_id`.
#[derive(Clone, Debug, Default)]
pub struct Interaction {
pub(crate) struct Interaction {
/// A widget interested in clicks that has a mouse press on it.
pub click_id: Option<Id>,
@ -81,6 +81,9 @@ pub struct Interaction {
/// The widget with keyboard focus (i.e. a text input field).
pub kb_focus_id: Option<Id>,
/// What had keyboard focus previous frame?
pub kb_focus_id_previous_frame: Option<Id>,
/// HACK: windows have low priority on dragging.
/// This is so that if you drag a slider in a window,
/// the slider will steal the drag away from the window.
@ -103,6 +106,7 @@ impl Interaction {
}
fn begin_frame(&mut self, prev_input: &crate::input::InputState) {
self.kb_focus_id_previous_frame = self.kb_focus_id;
self.click_interest = false;
self.drag_interest = false;
@ -143,6 +147,11 @@ impl Memory {
self.areas.layer_id_at(pos, resize_interact_radius_side)
}
/// True if the given widget had keyboard focus last frame, but not this one.
pub fn lost_kb_focus(&self, id: Id) -> bool {
self.interaction.kb_focus_id_previous_frame == Some(id) && !self.has_kb_focus(id)
}
pub fn has_kb_focus(&self, id: Id) -> bool {
self.interaction.kb_focus_id == Some(id)
}

View file

@ -81,18 +81,36 @@ pub struct Response {
/// This widget has the keyboard focus (i.e. is receiving key pressed)
pub has_kb_focus: bool,
/// The widget had keyboard focus and lost it,
/// perhaps because the user pressed enter.
/// This is often a signal to the user to the application
/// to make use of the contents of the text field.
pub lost_kb_focus: bool,
}
impl std::fmt::Debug for Response {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self {
ctx: _,
rect,
sense,
hovered,
clicked,
double_clicked,
active,
has_kb_focus,
lost_kb_focus,
} = self;
f.debug_struct("Response")
.field("rect", &self.rect)
.field("sense", &self.sense)
.field("hovered", &self.hovered)
.field("clicked", &self.clicked)
.field("double_clicked", &self.double_clicked)
.field("active", &self.active)
.field("has_kb_focus", &self.has_kb_focus)
.field("rect", rect)
.field("sense", sense)
.field("hovered", hovered)
.field("clicked", clicked)
.field("double_clicked", double_clicked)
.field("active", active)
.field("has_kb_focus", has_kb_focus)
.field("lost_kb_focus", lost_kb_focus)
.finish()
}
}
@ -133,6 +151,7 @@ impl Response {
double_clicked: self.double_clicked || other.double_clicked,
active: self.active || other.active,
has_kb_focus: self.has_kb_focus || other.has_kb_focus,
lost_kb_focus: self.lost_kb_focus || other.lost_kb_focus,
}
}
}

View file

@ -16,8 +16,7 @@ pub(crate) struct State {
/// # let mut ui = egui::Ui::__test();
/// # let mut my_string = String::new();
/// let response = ui.add(egui::TextEdit::new(&mut my_string).multiline(false));
/// if response.has_kb_focus && ui.input().key_pressed(egui::Key::Enter) {
/// ui.memory().stop_text_input();
/// if response.lost_kb_focus {
/// // use my_string
/// }
/// ```
@ -240,7 +239,11 @@ impl<'t> Widget for TextEdit<'t> {
.unwrap_or_else(|| visuals.text_color());
painter.galley(response.rect.min, galley, text_style, text_color);
ui.memory().text_edit.insert(id, state);
response
Response {
lost_kb_focus: ui.memory().lost_kb_focus(id), // we may have lost it during the course of this function
..response
}
}
}