Browse Source

[text] surrender keyboard focus by clicking outside text edit area

pull/15/head
Emil Ernerfeldt 4 years ago
parent
commit
2f161dd3d4
  1. 2
      egui/src/context.rs
  2. 47
      egui/src/memory.rs
  3. 8
      egui/src/ui.rs
  4. 11
      egui/src/widgets/text_edit.rs

2
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`). /// If true, Egui is currently listening on text input (e.g. typing text in a `TextEdit`).
pub fn wants_keyboard_input(&self) -> bool { pub fn wants_keyboard_input(&self) -> bool {
self.memory().kb_focus_id.is_some() self.memory().interaction.kb_focus_id.is_some()
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------

47
egui/src/memory.rs

@ -18,10 +18,6 @@ pub struct Memory {
#[cfg_attr(feature = "with_serde", serde(skip))] #[cfg_attr(feature = "with_serde", serde(skip))]
pub(crate) interaction: Interaction, 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<Id>,
// states of various types of widgets // states of various types of widgets
pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>, pub(crate) collapsing_headers: HashMap<Id, collapsing_header::State>,
pub(crate) menu_bar: HashMap<Id, menu::BarState>, pub(crate) menu_bar: HashMap<Id, menu::BarState>,
@ -50,6 +46,9 @@ pub struct Interaction {
/// A widget interested in drags that has a mouse press on it. /// A widget interested in drags that has a mouse press on it.
pub drag_id: Option<Id>, pub drag_id: Option<Id>,
/// The widget with keyboard focus (i.e. a text input field).
pub kb_focus_id: 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.
@ -70,6 +69,21 @@ impl Interaction {
pub fn is_using_mouse(&self) -> bool { pub fn is_using_mouse(&self) -> bool {
self.click_id.is_some() || self.drag_id.is_some() 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)] #[derive(Clone, Debug, Default)]
@ -92,17 +106,10 @@ pub struct Areas {
impl Memory { impl Memory {
pub(crate) fn begin_frame(&mut self, prev_input: &crate::input::InputState) { pub(crate) fn begin_frame(&mut self, prev_input: &crate::input::InputState) {
self.interaction.click_interest = false; self.interaction.begin_frame(prev_input);
self.interaction.drag_interest = false;
if !prev_input.mouse.could_be_click {
self.interaction.click_id = None;
}
if !prev_input.mouse.down || prev_input.mouse.pos.is_none() { if !prev_input.mouse.down || prev_input.mouse.pos.is_none() {
// mouse was not down last frame // mouse was not down last frame
self.interaction.click_id = None;
self.interaction.drag_id = None;
let window_interaction = self.window_interaction.take(); let window_interaction = self.window_interaction.take();
if let Some(window_interaction) = window_interaction { if let Some(window_interaction) = window_interaction {
@ -120,12 +127,26 @@ impl Memory {
} }
pub(crate) fn end_frame(&mut self) { 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<Layer> { pub fn layer_at(&self, pos: Pos2, resize_interact_radius_side: f32) -> Option<Layer> {
self.areas.layer_at(pos, resize_interact_radius_side) 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 { impl Areas {

8
egui/src/ui.rs

@ -259,14 +259,6 @@ impl Ui {
self.ctx() self.ctx()
.contains_mouse(self.layer(), self.clip_rect(), rect) .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 /// # `Id` creation

11
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 let interact = ui.interact(rect, id, Sense::click_and_drag()); // TODO: implement drag-select
if interact.clicked { if interact.clicked {
ui.request_kb_focus(id); ui.memory().request_kb_focus(id);
if let Some(mouse_pos) = ui.input().mouse.pos { if let Some(mouse_pos) = ui.input().mouse.pos {
state.cursor = Some(galley.char_at(mouse_pos - interact.rect.min).char_idx); 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 { if interact.hovered {
ui.output().cursor_icon = CursorIcon::Text; 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()); let mut cursor = state.cursor.unwrap_or_else(|| text.chars().count());
cursor = clamp(cursor, 0..=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 cursor_blink_hz = ui.style().cursor_blink_hz;
let show_cursor = let show_cursor =
(ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0; (ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0;

Loading…
Cancel
Save