Merge pull request #2865 from andymandias/expand-selection-by-word

Expandable Selection-by-Word via Mouse/Touch in `text_input`
This commit is contained in:
Héctor 2025-11-24 11:55:33 +01:00 committed by GitHub
commit 1533d18640
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -780,11 +780,14 @@ where
} else {
state.cursor.move_to(position);
}
state.is_dragging = true;
state.is_dragging = Some(Drag::Select);
}
click::Kind::Double => {
if self.is_secure {
state.cursor.select_all(&self.value);
state.is_dragging = None;
} else {
let position = find_cursor_position(
text_layout.bounds(),
@ -798,13 +801,15 @@ where
self.value.previous_start_of_word(position),
self.value.next_end_of_word(position),
);
}
state.is_dragging = false;
state.is_dragging = Some(Drag::SelectWords {
anchor: position,
});
}
}
click::Kind::Triple => {
state.cursor.select_all(&self.value);
state.is_dragging = false;
state.is_dragging = None;
}
}
@ -820,13 +825,13 @@ where
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. })
| Event::Touch(touch::Event::FingerLost { .. }) => {
state::<Renderer>(tree).is_dragging = false;
state::<Renderer>(tree).is_dragging = None;
}
Event::Mouse(mouse::Event::CursorMoved { position })
| Event::Touch(touch::Event::FingerMoved { position, .. }) => {
let state = state::<Renderer>(tree);
if state.is_dragging {
if let Some(is_dragging) = &state.is_dragging {
let text_layout = layout.children().next().unwrap();
let target = {
@ -857,9 +862,27 @@ where
let selection_before = state.cursor.selection(&value);
state
.cursor
.select_range(state.cursor.start(&value), position);
match is_dragging {
Drag::Select => {
state.cursor.select_range(
state.cursor.start(&value),
position,
);
}
Drag::SelectWords { anchor } => {
if position < *anchor {
state.cursor.select_range(
self.value.previous_start_of_word(position),
self.value.next_end_of_word(*anchor),
);
} else {
state.cursor.select_range(
self.value.previous_start_of_word(*anchor),
self.value.next_end_of_word(position),
);
}
}
}
if let Some(focus) = &mut state.is_focused {
focus.updated_at = Instant::now();
@ -1201,7 +1224,7 @@ where
}
keyboard::Key::Named(key::Named::Escape) => {
state.is_focused = None;
state.is_dragging = false;
state.is_dragging = None;
state.is_pasting = None;
state.keyboard_modifiers =
@ -1430,7 +1453,7 @@ pub struct State<P: text::Paragraph> {
placeholder: paragraph::Plain<P>,
icon: paragraph::Plain<P>,
is_focused: Option<Focus>,
is_dragging: bool,
is_dragging: Option<Drag>,
is_pasting: Option<Value>,
preedit: Option<input_method::Preedit>,
last_click: Option<mouse::Click>,
@ -1452,6 +1475,12 @@ struct Focus {
is_window_focused: bool,
}
#[derive(Debug, Clone)]
enum Drag {
Select,
SelectWords { anchor: usize },
}
impl<P: text::Paragraph> State<P> {
/// Creates a new [`State`], representing an unfocused [`TextInput`].
pub fn new() -> Self {