From 2433506c925f05bd4d4fb3cfffe9b268f8221ddd Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 18 May 2021 13:26:20 -0500 Subject: [PATCH] Add support for reporting cursor selection changes. --- egui/src/context.rs | 3 ++- egui/src/data/output.rs | 36 ++++++++++++++++++++++++++++++++++- egui/src/response.rs | 12 ++++++++++++ egui/src/widgets/text_edit.rs | 13 ++++++++++++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/egui/src/context.rs b/egui/src/context.rs index 8b20492b..f011415e 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -193,7 +193,8 @@ impl CtxRef { drag_released: false, is_pointer_button_down_on: false, interact_pointer_pos: None, - changed: false, // must be set by the widget itself + changed: false, // must be set by the widget itself + has_widget_info: false, // must be set by the widget itself }; if !enabled || !sense.focusable || !layer_id.allow_interaction() { diff --git a/egui/src/data/output.rs b/egui/src/data/output.rs index 51682755..c779fb07 100644 --- a/egui/src/data/output.rs +++ b/egui/src/data/output.rs @@ -43,6 +43,7 @@ impl Output { OutputEvent::Clicked(widget_info) | OutputEvent::DoubleClicked(widget_info) | OutputEvent::FocusGained(widget_info) + | OutputEvent::TextSelectionChanged(widget_info) | OutputEvent::ValueChanged(widget_info) => { return widget_info.description(); } @@ -213,6 +214,8 @@ pub enum OutputEvent { DoubleClicked(WidgetInfo), /// A widget gained keyboard focus (by tab key). FocusGained(WidgetInfo), + // Text selection was updated. + TextSelectionChanged(WidgetInfo), // A widget's value changed. ValueChanged(WidgetInfo), } @@ -223,6 +226,7 @@ impl std::fmt::Debug for OutputEvent { Self::Clicked(wi) => write!(f, "Clicked({:?})", wi), Self::DoubleClicked(wi) => write!(f, "DoubleClicked({:?})", wi), Self::FocusGained(wi) => write!(f, "FocusGained({:?})", wi), + Self::TextSelectionChanged(wi) => write!(f, "TextSelectionChanged({:?})", wi), Self::ValueChanged(wi) => write!(f, "ValueChanged({:?})", wi), } } @@ -238,11 +242,15 @@ pub struct WidgetInfo { /// The contents of some editable text (for `TextEdit` fields). pub text_value: Option, // The previous text value. - prev_text_value: Option, + pub prev_text_value: Option, /// The current value of checkboxes and radio buttons. pub selected: Option, /// The current value of sliders etc. pub value: Option, + // Location of primary cursor. + pub primary_cursor: Option, + // Location of secondary cursor. + pub secondary_cursor: Option, } impl std::fmt::Debug for WidgetInfo { @@ -254,6 +262,8 @@ impl std::fmt::Debug for WidgetInfo { prev_text_value, selected, value, + primary_cursor, + secondary_cursor, } = self; let mut s = f.debug_struct("WidgetInfo"); @@ -275,6 +285,12 @@ impl std::fmt::Debug for WidgetInfo { if let Some(value) = value { s.field("value", value); } + if let Some(primary_cursor) = primary_cursor { + s.field("primary_cursor", primary_cursor); + } + if let Some(secondary_cursor) = secondary_cursor { + s.field("secondary_cursor", secondary_cursor); + } s.finish() } @@ -289,6 +305,8 @@ impl WidgetInfo { prev_text_value: None, selected: None, value: None, + primary_cursor: None, + secondary_cursor: None, } } @@ -336,6 +354,20 @@ impl WidgetInfo { } } + #[allow(clippy::needless_pass_by_value)] + pub fn text_selection_changed( + primary_cursor: usize, + secondary_cursor: usize, + text_value: impl ToString, + ) -> Self { + Self { + primary_cursor: Some(primary_cursor), + secondary_cursor: Some(secondary_cursor), + text_value: Some(text_value.to_string()), + ..Self::new(WidgetType::TextEdit) + } + } + /// This can be used by a text-to-speech system to describe the widget. pub fn description(&self) -> String { let Self { @@ -345,6 +377,8 @@ impl WidgetInfo { prev_text_value: _, selected, value, + primary_cursor: _, + secondary_cursor: _, } = self; // TODO: localization diff --git a/egui/src/response.rs b/egui/src/response.rs index 0c6ebf6e..7f1edd99 100644 --- a/egui/src/response.rs +++ b/egui/src/response.rs @@ -65,6 +65,8 @@ pub struct Response { /// e.g. the slider was dragged, text was entered in a `TextEdit` etc. /// Always `false` for something like a `Button`. pub(crate) changed: bool, + /// Has a `WidgetInfo` but isn't covered by some other case (E.g. `changed`, `clicked`.) + pub(crate) has_widget_info: bool, } impl std::fmt::Debug for Response { @@ -84,6 +86,7 @@ impl std::fmt::Debug for Response { is_pointer_button_down_on, interact_pointer_pos, changed, + has_widget_info, } = self; f.debug_struct("Response") .field("layer_id", layer_id) @@ -99,6 +102,7 @@ impl std::fmt::Debug for Response { .field("is_pointer_button_down_on", is_pointer_button_down_on) .field("interact_pointer_pos", interact_pointer_pos) .field("changed", changed) + .field("has_widget_info", has_widget_info) .finish() } } @@ -437,6 +441,13 @@ impl Response { Some(OutputEvent::FocusGained(make_info())) } else if self.changed { Some(OutputEvent::ValueChanged(make_info())) + } else if self.has_widget_info { + let info = make_info(); + if info.primary_cursor.is_some() && info.secondary_cursor.is_some() { + Some(OutputEvent::TextSelectionChanged(info)) + } else { + None + } } else { None }; @@ -479,6 +490,7 @@ impl Response { || other.is_pointer_button_down_on, interact_pointer_pos: self.interact_pointer_pos.or(other.interact_pointer_pos), changed: self.changed || other.changed, + has_widget_info: self.has_widget_info || other.has_widget_info, } } } diff --git a/egui/src/widgets/text_edit.rs b/egui/src/widgets/text_edit.rs index c5b35cac..4d0485cb 100644 --- a/egui/src/widgets/text_edit.rs +++ b/egui/src/widgets/text_edit.rs @@ -665,7 +665,18 @@ impl<'t> TextEdit<'t> { ui.memory().id_data.insert(id, state); - response.widget_info(|| WidgetInfo::text_edit(&*text, &*prev_text)); + if response.changed { + response.widget_info(|| WidgetInfo::text_edit(&*text, &*prev_text)); + } else if let Some(text_cursor) = text_cursor { + response.has_widget_info = true; + response.widget_info(|| { + WidgetInfo::text_selection_changed( + text_cursor.primary.ccursor.index, + text_cursor.secondary.ccursor.index, + &*text, + ) + }); + } response } }