input: Add to support move, select and delete by word. (#591)
Close #466 https://github.com/user-attachments/assets/48cda905-f2bf-4896-b3ae-08b7cd61467d
This commit is contained in:
parent
6fa1888ee6
commit
43f529c6d2
2 changed files with 130 additions and 1 deletions
|
|
@ -26,7 +26,7 @@ serde = "1.0.203"
|
|||
serde_json = "1"
|
||||
smallvec = "1.13.2"
|
||||
smol = "1"
|
||||
unicode-segmentation = "1.11.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
usvg = { version = "0.44.0", default-features = false, features = [
|
||||
"system-fonts",
|
||||
"text",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ actions!(
|
|||
Delete,
|
||||
DeleteToBeginningOfLine,
|
||||
DeleteToEndOfLine,
|
||||
DeleteToPreviousWordStart,
|
||||
DeleteToNextWordEnd,
|
||||
Enter,
|
||||
Up,
|
||||
Down,
|
||||
|
|
@ -58,6 +60,8 @@ actions!(
|
|||
SelectToEndOfLine,
|
||||
SelectToStart,
|
||||
SelectToEnd,
|
||||
SelectToPreviousWordStart,
|
||||
SelectToNextWordEnd,
|
||||
ShowCharacterPalette,
|
||||
Copy,
|
||||
Cut,
|
||||
|
|
@ -68,6 +72,8 @@ actions!(
|
|||
MoveToEndOfLine,
|
||||
MoveToStart,
|
||||
MoveToEnd,
|
||||
MoveToPreviousWord,
|
||||
MoveToNextWord,
|
||||
TextChanged,
|
||||
]
|
||||
);
|
||||
|
|
@ -90,6 +96,14 @@ pub fn init(cx: &mut App) {
|
|||
KeyBinding::new("cmd-backspace", DeleteToBeginningOfLine, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("cmd-delete", DeleteToEndOfLine, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-backspace", DeleteToPreviousWordStart, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-backspace", DeleteToPreviousWordStart, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-delete", DeleteToNextWordEnd, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-delete", DeleteToNextWordEnd, Some(CONTEXT)),
|
||||
KeyBinding::new("enter", Enter, Some(CONTEXT)),
|
||||
KeyBinding::new("up", Up, Some(CONTEXT)),
|
||||
KeyBinding::new("down", Down, Some(CONTEXT)),
|
||||
|
|
@ -112,6 +126,14 @@ pub fn init(cx: &mut App) {
|
|||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("shift-cmd-right", SelectToEndOfLine, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-shift-left", SelectToPreviousWordStart, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-shift-left", SelectToPreviousWordStart, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-shift-right", SelectToNextWordEnd, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-shift-right", SelectToNextWordEnd, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("ctrl-cmd-space", ShowCharacterPalette, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("cmd-a", SelectAll, Some(CONTEXT)),
|
||||
|
|
@ -146,6 +168,14 @@ pub fn init(cx: &mut App) {
|
|||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("cmd-down", MoveToEnd, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-left", MoveToPreviousWord, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("alt-right", MoveToNextWord, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-left", MoveToPreviousWord, Some(CONTEXT)),
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
KeyBinding::new("ctrl-right", MoveToNextWord, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("cmd-shift-up", SelectToStart, Some(CONTEXT)),
|
||||
#[cfg(target_os = "macos")]
|
||||
KeyBinding::new("cmd-shift-down", SelectToEnd, Some(CONTEXT)),
|
||||
|
|
@ -659,6 +689,26 @@ impl TextInput {
|
|||
self.move_to(end, window, cx);
|
||||
}
|
||||
|
||||
fn move_to_previous_word(
|
||||
&mut self,
|
||||
_: &MoveToPreviousWord,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
fn move_to_next_word(
|
||||
&mut self,
|
||||
_: &MoveToNextWord,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
fn select_to_start(&mut self, _: &SelectToStart, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.select_to(0, window, cx);
|
||||
}
|
||||
|
|
@ -688,6 +738,47 @@ impl TextInput {
|
|||
self.select_to(self.next_boundary(offset), window, cx);
|
||||
}
|
||||
|
||||
fn select_to_previous_word(
|
||||
&mut self,
|
||||
_: &SelectToPreviousWordStart,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
fn select_to_next_word(
|
||||
&mut self,
|
||||
_: &SelectToNextWordEnd,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
/// Return the start offset of the previous word.
|
||||
fn previous_start_of_word(&mut self) -> usize {
|
||||
let offset = self.selected_range.start;
|
||||
let prev_str = &self.text[..offset].to_string();
|
||||
UnicodeSegmentation::split_word_bound_indices(prev_str as &str)
|
||||
.filter(|(_, s)| !s.trim_start().is_empty())
|
||||
.next_back()
|
||||
.map(|(i, _)| i)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Return the next end offset of the next word.
|
||||
fn next_end_of_word(&mut self) -> usize {
|
||||
let offset = self.cursor_offset();
|
||||
let next_str = &self.text[offset..].to_string();
|
||||
UnicodeSegmentation::split_word_bound_indices(next_str as &str)
|
||||
.find(|(_, s)| !s.trim_start().is_empty())
|
||||
.map(|(i, s)| offset + i + s.len())
|
||||
.unwrap_or(self.text.len())
|
||||
}
|
||||
|
||||
/// Get start of line
|
||||
fn start_of_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> usize {
|
||||
if self.is_single_line() {
|
||||
|
|
@ -787,6 +878,38 @@ impl TextInput {
|
|||
self.pause_blink_cursor(cx);
|
||||
}
|
||||
|
||||
fn delete_previous_word(
|
||||
&mut self,
|
||||
_: &DeleteToPreviousWordStart,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(offset..self.cursor_offset()))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.pause_blink_cursor(cx);
|
||||
}
|
||||
|
||||
fn delete_next_word(
|
||||
&mut self,
|
||||
_: &DeleteToNextWordEnd,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(self.cursor_offset()..offset))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.pause_blink_cursor(cx);
|
||||
}
|
||||
|
||||
fn enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.is_multi_line() {
|
||||
let is_eof = self.selected_range.end == self.text.len();
|
||||
|
|
@ -1533,6 +1656,8 @@ impl Render for TextInput {
|
|||
.on_action(cx.listener(Self::delete))
|
||||
.on_action(cx.listener(Self::delete_to_beginning_of_line))
|
||||
.on_action(cx.listener(Self::delete_to_end_of_line))
|
||||
.on_action(cx.listener(Self::delete_previous_word))
|
||||
.on_action(cx.listener(Self::delete_next_word))
|
||||
.on_action(cx.listener(Self::enter))
|
||||
})
|
||||
.on_action(cx.listener(Self::left))
|
||||
|
|
@ -1548,10 +1673,14 @@ impl Render for TextInput {
|
|||
.on_action(cx.listener(Self::select_all))
|
||||
.on_action(cx.listener(Self::select_to_start_of_line))
|
||||
.on_action(cx.listener(Self::select_to_end_of_line))
|
||||
.on_action(cx.listener(Self::select_to_previous_word))
|
||||
.on_action(cx.listener(Self::select_to_next_word))
|
||||
.on_action(cx.listener(Self::home))
|
||||
.on_action(cx.listener(Self::end))
|
||||
.on_action(cx.listener(Self::move_to_start))
|
||||
.on_action(cx.listener(Self::move_to_end))
|
||||
.on_action(cx.listener(Self::move_to_previous_word))
|
||||
.on_action(cx.listener(Self::move_to_next_word))
|
||||
.on_action(cx.listener(Self::select_to_start))
|
||||
.on_action(cx.listener(Self::select_to_end))
|
||||
.on_action(cx.listener(Self::show_character_palette))
|
||||
|
|
|
|||
Loading…
Reference in a new issue