diff options
Diffstat (limited to 'components/script/textinput.rs')
-rw-r--r-- | components/script/textinput.rs | 208 |
1 files changed, 191 insertions, 17 deletions
diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 5f04b5b187e..144be9dbd78 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -14,6 +14,7 @@ use std::cmp::{max, min}; use std::default::Default; use std::ops::Range; use std::usize; +use unicode_segmentation::UnicodeSegmentation; #[derive(Copy, Clone, PartialEq)] pub enum Selection { @@ -376,18 +377,16 @@ impl<T: ClipboardProvider> TextInput<T> { } let adjust = { let current_line = &self.lines[self.edit_point.line]; - // FIXME: We adjust by one code point, but it proably should be one grapheme cluster - // https://github.com/unicode-rs/unicode-segmentation match direction { Direction::Forward => { - match current_line[self.edit_point.index..].chars().next() { - Some(c) => c.len_utf8() as isize, + match current_line[self.edit_point.index..].graphemes(true).next() { + Some(c) => c.len() as isize, None => 1, // Going to the next line is a "one byte" offset } } Direction::Backward => { - match current_line[..self.edit_point.index].chars().next_back() { - Some(c) => -(c.len_utf8() as isize), + match current_line[..self.edit_point.index].graphemes(true).next_back() { + Some(c) => -(c.len() as isize), None => -1, // Going to the previous line is a "one byte" offset } } @@ -468,6 +467,111 @@ impl<T: ClipboardProvider> TextInput<T> { self.selection_begin = None; } + pub fn adjust_horizontal_by_word(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + let shift_increment: isize = { + let input: &str; + match direction { + Direction::Backward => { + let remaining = self.edit_point.index; + let current_line = self.edit_point.line; + let mut newline_adjustment = 0; + if remaining == 0 && current_line > 0 { + input = &self + .lines[current_line-1]; + newline_adjustment = 1; + } else { + input = &self + .lines[current_line] + [..remaining]; + } + + let mut iter = input.split_word_bounds().rev(); + let mut shift_temp: isize = 0; + loop { + match iter.next() { + None => break, + Some(x) => { + shift_temp += - (x.len() as isize); + if x.chars().any(|x| x.is_alphabetic() || x.is_numeric()) { + break; + } + } + } + } + shift_temp - newline_adjustment + } + Direction::Forward => { + let remaining = self.current_line_length() - self.edit_point.index; + let current_line = self.edit_point.line; + let mut newline_adjustment = 0; + if remaining == 0 && self.lines.len() > self.edit_point.line + 1 { + input = &self + .lines[current_line + 1]; + newline_adjustment = 1; + } else { + input = &self + .lines[current_line] + [self.edit_point.index..]; + } + + let mut iter = input.split_word_bounds(); + let mut shift_temp: isize = 0; + loop { + match iter.next() { + None => break, + Some(x) => { + shift_temp += x.len() as isize; + if x.chars().any(|x| x.is_alphabetic() || x.is_numeric()) { + break; + } + } + } + } + shift_temp + newline_adjustment + } + } + }; + + self.adjust_horizontal(shift_increment, select); + } + + pub fn adjust_horizontal_to_line_end(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + let shift: isize = { + let current_line = &self.lines[self.edit_point.line]; + match direction { + Direction::Backward => { + - (current_line[..self.edit_point.index].len() as isize) + }, + Direction::Forward => { + current_line[self.edit_point.index..].len() as isize + } + } + }; + self.perform_horizontal_adjustment(shift, select); + } + + pub fn adjust_horizontal_to_limit(&mut self, direction: Direction, select: Selection) { + if self.adjust_selection_for_horizontal_change(direction, select) { + return + } + match direction { + Direction::Backward => { + self.edit_point.line = 0; + self.edit_point.index = 0; + }, + Direction::Forward => { + self.edit_point.line = &self.lines.len() - 1; + self.edit_point.index = (&self.lines[&self.lines.len() - 1]).len(); + } + } + } + /// Process a given `KeyboardEvent` and return an action for the caller to execute. pub fn handle_keydown(&mut self, event: &KeyboardEvent) -> KeyReaction { if let Some(key) = event.get_key() { @@ -483,6 +587,32 @@ impl<T: ClipboardProvider> TextInput<T> { mods: KeyModifiers) -> KeyReaction { let maybe_select = if mods.contains(SHIFT) { Selection::Selected } else { Selection::NotSelected }; match (printable, key) { + (_, Key::B) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_by_word(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::F) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_by_word(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::A) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (_, Key::E) if mods.contains(CONTROL | ALT) => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::A) if mods == CONTROL => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::E) if mods == CONTROL => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, (Some('a'), _) if is_control_key(mods) => { self.select_all(); KeyReaction::RedrawSelection @@ -501,49 +631,85 @@ impl<T: ClipboardProvider> TextInput<T> { (Some(c), _) => { self.insert_char(c); KeyReaction::DispatchInput - } + }, + #[cfg(target_os = "macos")] + (None, Key::Home) => { + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::End) => { + KeyReaction::RedrawSelection + }, (None, Key::Delete) => { self.delete_char(Direction::Forward); KeyReaction::DispatchInput - } + }, (None, Key::Backspace) => { self.delete_char(Direction::Backward); KeyReaction::DispatchInput - } + }, + #[cfg(target_os = "macos")] + (None, Key::Left) if mods.contains(SUPER) => { + self.adjust_horizontal_to_line_end(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Right) if mods.contains(SUPER) => { + self.adjust_horizontal_to_line_end(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Up) if mods.contains(SUPER) => { + self.adjust_horizontal_to_limit(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + #[cfg(target_os = "macos")] + (None, Key::Down) if mods.contains(SUPER) => { + self.adjust_horizontal_to_limit(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, + (None, Key::Left) if mods.contains(ALT) => { + self.adjust_horizontal_by_word(Direction::Backward, maybe_select); + KeyReaction::RedrawSelection + }, + (None, Key::Right) if mods.contains(ALT) => { + self.adjust_horizontal_by_word(Direction::Forward, maybe_select); + KeyReaction::RedrawSelection + }, (None, Key::Left) => { self.adjust_horizontal_by_one(Direction::Backward, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Right) => { self.adjust_horizontal_by_one(Direction::Forward, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Up) => { self.adjust_vertical(-1, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Down) => { self.adjust_vertical(1, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::Enter) | (None, Key::KpEnter) => self.handle_return(), (None, Key::Home) => { self.edit_point.index = 0; KeyReaction::RedrawSelection - } + }, (None, Key::End) => { self.edit_point.index = self.current_line_length(); self.assert_ok_selection(); KeyReaction::RedrawSelection - } + }, (None, Key::PageUp) => { self.adjust_vertical(-28, maybe_select); KeyReaction::RedrawSelection - } + }, (None, Key::PageDown) => { self.adjust_vertical(28, maybe_select); KeyReaction::RedrawSelection - } + }, _ => KeyReaction::Nothing, } } @@ -677,4 +843,12 @@ impl<T: ClipboardProvider> TextInput<T> { selection_start as u32 } + + pub fn set_edit_point_index(&mut self, index: usize) { + let byte_size = self.lines[self.edit_point.line] + .graphemes(true) + .take(index) + .fold(0, |acc, x| acc + x.len()); + self.edit_point.index = byte_size; + } } |