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
|
## Unreleased
|
||||||
|
|
||||||
|
### Added ⭐
|
||||||
|
|
||||||
|
* You can now check if a `TextEdit` lost keyboard focus with `response.lost_kb_focus`.
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
|
|
||||||
* Pressing enter in a single-line `TextEdit` will now surrender keyboard focus for it
|
* 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 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);
|
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() {
|
if id.is_none() || sense == Sense::nothing() || !layer_id.allow_interaction() {
|
||||||
// Not interested or allowed input:
|
// Not interested or allowed input:
|
||||||
return Response {
|
return Response {
|
||||||
|
@ -441,6 +447,7 @@ impl Context {
|
||||||
double_clicked: false,
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let id = id.unwrap();
|
let id = id.unwrap();
|
||||||
|
@ -466,6 +473,7 @@ impl Context {
|
||||||
double_clicked: false,
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
};
|
};
|
||||||
|
|
||||||
if sense.click && memory.interaction.click_id.is_none() {
|
if sense.click && memory.interaction.click_id.is_none() {
|
||||||
|
@ -496,6 +504,7 @@ impl Context {
|
||||||
double_clicked: false,
|
double_clicked: false,
|
||||||
active: false,
|
active: false,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.input.mouse.released {
|
} else if self.input.mouse.released {
|
||||||
|
@ -509,6 +518,7 @@ impl Context {
|
||||||
double_clicked: clicked && self.input.mouse.double_click,
|
double_clicked: clicked && self.input.mouse.double_click,
|
||||||
active,
|
active,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
}
|
}
|
||||||
} else if self.input.mouse.down {
|
} else if self.input.mouse.down {
|
||||||
Response {
|
Response {
|
||||||
|
@ -520,6 +530,7 @@ impl Context {
|
||||||
double_clicked: false,
|
double_clicked: false,
|
||||||
active,
|
active,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Response {
|
Response {
|
||||||
|
@ -531,6 +542,7 @@ impl Context {
|
||||||
double_clicked: false,
|
double_clicked: false,
|
||||||
active,
|
active,
|
||||||
has_kb_focus,
|
has_kb_focus,
|
||||||
|
lost_kb_focus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,12 +133,16 @@ impl Widgets {
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Single line text input:");
|
ui.label("Single line text input:");
|
||||||
ui.add(
|
let response = ui.add(
|
||||||
TextEdit::new(&mut self.single_line_text_input)
|
TextEdit::new(&mut self.single_line_text_input)
|
||||||
.multiline(false)
|
.multiline(false)
|
||||||
.id_source("single line"),
|
.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.label("Multiline text input:");
|
||||||
ui.add(TextEdit::new(&mut self.multiline_text_input).id_source("multiline"));
|
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 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`.
|
/// If the cursor moves too much we clear the `click_id` and start passing move events to `drag_id`.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Interaction {
|
pub(crate) struct Interaction {
|
||||||
/// A widget interested in clicks that has a mouse press on it.
|
/// A widget interested in clicks that has a mouse press on it.
|
||||||
pub click_id: Option<Id>,
|
pub click_id: Option<Id>,
|
||||||
|
|
||||||
|
@ -81,6 +81,9 @@ pub struct Interaction {
|
||||||
/// The widget with keyboard focus (i.e. a text input field).
|
/// The widget with keyboard focus (i.e. a text input field).
|
||||||
pub kb_focus_id: Option<Id>,
|
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.
|
/// HACK: windows have low priority on dragging.
|
||||||
/// This is so that if you drag a slider in a window,
|
/// This is so that if you drag a slider in a window,
|
||||||
/// the slider will steal the drag away from the 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) {
|
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.click_interest = false;
|
||||||
self.drag_interest = false;
|
self.drag_interest = false;
|
||||||
|
|
||||||
|
@ -143,6 +147,11 @@ impl Memory {
|
||||||
self.areas.layer_id_at(pos, resize_interact_radius_side)
|
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 {
|
pub fn has_kb_focus(&self, id: Id) -> bool {
|
||||||
self.interaction.kb_focus_id == Some(id)
|
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)
|
/// This widget has the keyboard focus (i.e. is receiving key pressed)
|
||||||
pub has_kb_focus: bool,
|
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 {
|
impl std::fmt::Debug for Response {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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")
|
f.debug_struct("Response")
|
||||||
.field("rect", &self.rect)
|
.field("rect", rect)
|
||||||
.field("sense", &self.sense)
|
.field("sense", sense)
|
||||||
.field("hovered", &self.hovered)
|
.field("hovered", hovered)
|
||||||
.field("clicked", &self.clicked)
|
.field("clicked", clicked)
|
||||||
.field("double_clicked", &self.double_clicked)
|
.field("double_clicked", double_clicked)
|
||||||
.field("active", &self.active)
|
.field("active", active)
|
||||||
.field("has_kb_focus", &self.has_kb_focus)
|
.field("has_kb_focus", has_kb_focus)
|
||||||
|
.field("lost_kb_focus", lost_kb_focus)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +151,7 @@ impl Response {
|
||||||
double_clicked: self.double_clicked || other.double_clicked,
|
double_clicked: self.double_clicked || other.double_clicked,
|
||||||
active: self.active || other.active,
|
active: self.active || other.active,
|
||||||
has_kb_focus: self.has_kb_focus || other.has_kb_focus,
|
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 ui = egui::Ui::__test();
|
||||||
/// # let mut my_string = String::new();
|
/// # let mut my_string = String::new();
|
||||||
/// let response = ui.add(egui::TextEdit::new(&mut my_string).multiline(false));
|
/// let response = ui.add(egui::TextEdit::new(&mut my_string).multiline(false));
|
||||||
/// if response.has_kb_focus && ui.input().key_pressed(egui::Key::Enter) {
|
/// if response.lost_kb_focus {
|
||||||
/// ui.memory().stop_text_input();
|
|
||||||
/// // use my_string
|
/// // use my_string
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -240,7 +239,11 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
.unwrap_or_else(|| visuals.text_color());
|
.unwrap_or_else(|| visuals.text_color());
|
||||||
painter.galley(response.rect.min, galley, text_style, text_color);
|
painter.galley(response.rect.min, galley, text_style, text_color);
|
||||||
ui.memory().text_edit.insert(id, state);
|
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