Check if TextEdit lost keyboard focus with response.lost_kb_focus
This commit is contained in:
parent
c999ed038a
commit
d2b5730784
6 changed files with 64 additions and 13 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue