Add support for reporting cursor selection changes.

This commit is contained in:
Nolan Darilek 2021-05-18 13:26:20 -05:00
parent 97aa56f465
commit 2433506c92
4 changed files with 61 additions and 3 deletions

View file

@ -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() {

View file

@ -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<String>,
// The previous text value.
prev_text_value: Option<String>,
pub prev_text_value: Option<String>,
/// The current value of checkboxes and radio buttons.
pub selected: Option<bool>,
/// The current value of sliders etc.
pub value: Option<f64>,
// Location of primary cursor.
pub primary_cursor: Option<usize>,
// Location of secondary cursor.
pub secondary_cursor: Option<usize>,
}
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

View file

@ -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,
}
}
}

View file

@ -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
}
}