From 2f161dd3d4be0ebad099a51d0dd8bd746f8794ca Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 5 Aug 2020 13:54:17 +0200 Subject: [PATCH] [text] surrender keyboard focus by clicking outside text edit area --- egui/src/context.rs | 2 +- egui/src/memory.rs | 47 +++++++++++++++++++++++++---------- egui/src/ui.rs | 8 ------ egui/src/widgets/text_edit.rs | 11 +++++--- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/egui/src/context.rs b/egui/src/context.rs index c38533ea..c55d0633 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -308,7 +308,7 @@ impl Context { /// If true, Egui is currently listening on text input (e.g. typing text in a `TextEdit`). pub fn wants_keyboard_input(&self) -> bool { - self.memory().kb_focus_id.is_some() + self.memory().interaction.kb_focus_id.is_some() } // --------------------------------------------------------------------- diff --git a/egui/src/memory.rs b/egui/src/memory.rs index dd1cb8e5..535efa32 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -18,10 +18,6 @@ pub struct Memory { #[cfg_attr(feature = "with_serde", serde(skip))] pub(crate) interaction: Interaction, - /// The widget with keyboard focus (i.e. a text input field). - #[cfg_attr(feature = "with_serde", serde(skip))] - pub(crate) kb_focus_id: Option, - // states of various types of widgets pub(crate) collapsing_headers: HashMap, pub(crate) menu_bar: HashMap, @@ -50,6 +46,9 @@ pub struct Interaction { /// A widget interested in drags that has a mouse press on it. pub drag_id: Option, + /// The widget with keyboard focus (i.e. a text input field). + pub kb_focus_id: Option, + /// 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. @@ -70,6 +69,21 @@ impl Interaction { pub fn is_using_mouse(&self) -> bool { self.click_id.is_some() || self.drag_id.is_some() } + + fn begin_frame(&mut self, prev_input: &crate::input::InputState) { + self.click_interest = false; + self.drag_interest = false; + + if !prev_input.mouse.could_be_click { + self.click_id = None; + } + + if !prev_input.mouse.down || prev_input.mouse.pos.is_none() { + // mouse was not down last frame + self.click_id = None; + self.drag_id = None; + } + } } #[derive(Clone, Debug, Default)] @@ -92,17 +106,10 @@ pub struct Areas { impl Memory { pub(crate) fn begin_frame(&mut self, prev_input: &crate::input::InputState) { - self.interaction.click_interest = false; - self.interaction.drag_interest = false; - - if !prev_input.mouse.could_be_click { - self.interaction.click_id = None; - } + self.interaction.begin_frame(prev_input); if !prev_input.mouse.down || prev_input.mouse.pos.is_none() { // mouse was not down last frame - self.interaction.click_id = None; - self.interaction.drag_id = None; let window_interaction = self.window_interaction.take(); if let Some(window_interaction) = window_interaction { @@ -120,12 +127,26 @@ impl Memory { } pub(crate) fn end_frame(&mut self) { - self.areas.end_frame() + self.areas.end_frame(); } pub fn layer_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option { self.areas.layer_at(pos, resize_interact_radius_side) } + + pub fn has_kb_focus(&self, id: Id) -> bool { + self.interaction.kb_focus_id == Some(id) + } + + pub fn request_kb_focus(&mut self, id: Id) { + self.interaction.kb_focus_id = Some(id); + } + + pub fn surrender_kb_focus(&mut self, id: Id) { + if self.interaction.kb_focus_id == Some(id) { + self.interaction.kb_focus_id = None; + } + } } impl Areas { diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 8f97d3c5..aa99564c 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -259,14 +259,6 @@ impl Ui { self.ctx() .contains_mouse(self.layer(), self.clip_rect(), rect) } - - pub fn has_kb_focus(&self, id: Id) -> bool { - self.memory().kb_focus_id == Some(id) - } - - pub fn request_kb_focus(&self, id: Id) { - self.memory().kb_focus_id = Some(id); - } } /// # `Id` creation diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index 686fa62b..5a0e6888 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -76,17 +76,20 @@ impl<'t> Widget for TextEdit<'t> { let interact = ui.interact(rect, id, Sense::click_and_drag()); // TODO: implement drag-select if interact.clicked { - ui.request_kb_focus(id); + ui.memory().request_kb_focus(id); if let Some(mouse_pos) = ui.input().mouse.pos { state.cursor = Some(galley.char_at(mouse_pos - interact.rect.min).char_idx); } + } else if ui.input().mouse.click { + // User clicked somewhere else + ui.memory().surrender_kb_focus(id); } + if interact.hovered { ui.output().cursor_icon = CursorIcon::Text; } - let has_kb_focus = ui.has_kb_focus(id); - if has_kb_focus { + if ui.memory().has_kb_focus(id) { let mut cursor = state.cursor.unwrap_or_else(|| text.chars().count()); cursor = clamp(cursor, 0..=text.chars().count()); @@ -141,7 +144,7 @@ impl<'t> Widget for TextEdit<'t> { }); } - if has_kb_focus { + if ui.memory().has_kb_focus(id) { let cursor_blink_hz = ui.style().cursor_blink_hz; let show_cursor = (ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0;