diff --git a/src/ui/widgets/editbox.rs b/src/ui/widgets/editbox.rs index 36b4bc61..c0d01f73 100644 --- a/src/ui/widgets/editbox.rs +++ b/src/ui/widgets/editbox.rs @@ -72,7 +72,7 @@ impl<'a> Editbox<'a> { &self, input_buffer: &mut Vec, clipboard: &mut dyn crate::ui::ClipboardObject, - text: &mut String, + text: &mut Vec, state: &mut EditboxState, ) { for character in input_buffer.drain(0..) { @@ -139,7 +139,7 @@ impl<'a> Editbox<'a> { } } } else { - state.insert_string(text, clipboard); + state.insert_string(text, clipboard.chars().collect()); } } } @@ -251,6 +251,7 @@ impl<'a> Editbox<'a> { } pub fn ui(self, ui: &mut Ui, text: &mut String) -> bool { + let mut text_vec = text.chars().collect::>(); let time = ui.time; let context = ui.get_active_window_context(); @@ -278,19 +279,19 @@ impl<'a> Editbox<'a> { .storage_any .get_or_default::(hash!(self.id, "cursor")); - // if text changed outside the selection range should be clamped - state.clamp_selection(text); + // if text changed outside, than the selection range should be clamped + state.clamp_selection(&text_vec); if self.select_all { - state.select_all(text); + state.select_all(&text_vec); } - if let Some(selected) = state.selected_text(text) { - *context.clipboard_selection = selected.to_owned(); + if let Some(selected) = state.selected_text(&text_vec) { + *context.clipboard_selection = selected.iter().collect(); } // in case the string was updated outside of editbox - if state.cursor > text.len() as u32 { - state.cursor = text.len() as u32; + if state.cursor > text_vec.len() as u32 { + state.cursor = text_vec.len() as u32; } let input_focused = @@ -318,7 +319,7 @@ impl<'a> Editbox<'a> { self.apply_keyboard_input( &mut context.input.input_buffer, &mut *context.clipboard, - text, + &mut text_vec, &mut state, ); } @@ -357,9 +358,12 @@ impl<'a> Editbox<'a> { let line_height = context.style.editbox_style.font_size as f32; - let size = vec2(150., line_height * text.split('\n').count() as f32); + let size = vec2( + 150., + line_height * text_vec.iter().filter(|c| **c == '\n').count() as f32, + ); - // TODO: this is very weird hardcoded text margin + // TODO: this is very weird hardcoded text_vec margin let pos = context.window.cursor.fit(size, Layout::Free(vec2(2., 2.))); context.window.painter.clip(parent_rect); @@ -376,7 +380,12 @@ impl<'a> Editbox<'a> { let mut y = 0.; let mut clicked = false; - for (n, character) in text.chars().chain(std::iter::once(' ')).enumerate() { + for (n, character) in text_vec + .iter() + .copied() + .chain(std::iter::once(' ')) + .enumerate() + { let character = if character != '\n' && self.password { '*' } else { @@ -439,12 +448,12 @@ impl<'a> Editbox<'a> { let cursor_on_current_line = (context.input.mouse_position.y - (pos.y + y + line_height / 2.)).abs() < line_height / 2. + 0.1; - let line_end = character == '\n' || n == text.len(); + let line_end = character == '\n' || n == text_vec.len(); let cursor_after_line_end = context.input.mouse_position.x > (pos.x + x); let clickable_character = character != '\n'; let cursor_on_character = (context.input.mouse_position.x - (pos.x + x)).abs() < advance / 2.; - let last_character = n == text.len(); + let last_character = n == text_vec.len(); let cursor_below_line = (context.input.mouse_position.y - (pos.y + y + line_height)) > 0.; @@ -455,9 +464,9 @@ impl<'a> Editbox<'a> { clicked = true; if context.input.click_down() { - state.click_down(time, text, n as u32); + state.click_down(time, &text_vec, n as u32); } else { - state.click_move(text, n as u32); + state.click_move(&text_vec, n as u32); } } } @@ -470,7 +479,7 @@ impl<'a> Editbox<'a> { } if context.input.click_up() && input_focused { - state.click_up(text); + state.click_up(&text_vec); } let context = ui.get_active_window_context(); @@ -479,6 +488,7 @@ impl<'a> Editbox<'a> { ui.end_window(); + *text = text_vec.iter().collect(); edited } } diff --git a/src/ui/widgets/editbox/text_editor.rs b/src/ui/widgets/editbox/text_editor.rs index 3fb09834..a9aca648 100644 --- a/src/ui/widgets/editbox/text_editor.rs +++ b/src/ui/widgets/editbox/text_editor.rs @@ -1,6 +1,6 @@ trait Command { - fn apply(&self, text_cursor: &mut u32, text: &mut String); - fn unapply(&self, text_cursor: &mut u32, text: &mut String); + fn apply(&self, text_cursor: &mut u32, text: &mut Vec); + fn unapply(&self, text_cursor: &mut u32, text: &mut Vec); } struct InsertCharacter { @@ -9,7 +9,7 @@ struct InsertCharacter { } impl InsertCharacter { - fn new(editor: &EditboxState, _text: &mut String, character: char) -> InsertCharacter { + fn new(editor: &EditboxState, _text: &mut Vec, character: char) -> InsertCharacter { InsertCharacter { cursor: editor.cursor, character, @@ -18,14 +18,14 @@ impl InsertCharacter { } impl Command for InsertCharacter { - fn apply(&self, text_cursor: &mut u32, text: &mut String) { + fn apply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor; if self.cursor <= text.len() as u32 { text.insert(self.cursor as usize, self.character); } *text_cursor += 1; } - fn unapply(&self, text_cursor: &mut u32, text: &mut String) { + fn unapply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor; if self.cursor < text.len() as u32 { text.remove(self.cursor as usize); @@ -34,12 +34,12 @@ impl Command for InsertCharacter { } struct InsertString { - data: String, + data: Vec, cursor: u32, } impl InsertString { - fn new(editor: &EditboxState, _text: &mut String, data: String) -> InsertString { + fn new(editor: &EditboxState, _text: &mut Vec, data: Vec) -> InsertString { InsertString { cursor: editor.cursor, data, @@ -48,20 +48,22 @@ impl InsertString { } impl Command for InsertString { - fn apply(&self, text_cursor: &mut u32, text: &mut String) { + fn apply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor; if self.cursor <= text.len() as u32 { - text.insert_str(self.cursor as usize, &self.data); + text.splice( + self.cursor as usize..self.cursor as usize, + self.data.clone(), + ); } *text_cursor += self.data.len() as u32; } - fn unapply(&self, text_cursor: &mut u32, text: &mut String) { + fn unapply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor; if self.cursor < text.len() as u32 { let end = (self.cursor as usize + self.data.len()).min(text.len()); - - text.replace_range(self.cursor as usize..end, ""); + text.drain(self.cursor as usize..end); } } } @@ -72,8 +74,8 @@ struct DeleteCharacter { } impl DeleteCharacter { - fn new(editor: &EditboxState, text: &mut String) -> Option { - let character = text.chars().nth(editor.cursor as usize); + fn new(editor: &EditboxState, text: &mut Vec) -> Option { + let character = text.get(editor.cursor as usize).copied(); character.map(|character| DeleteCharacter { cursor: editor.cursor, @@ -83,14 +85,14 @@ impl DeleteCharacter { } impl Command for DeleteCharacter { - fn apply(&self, text_cursor: &mut u32, text: &mut String) { + fn apply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor; if self.cursor < text.len() as u32 { text.remove(self.cursor as usize); } } - fn unapply(&self, text_cursor: &mut u32, text: &mut String) { + fn unapply(&self, text_cursor: &mut u32, text: &mut Vec) { *text_cursor = self.cursor + 1; if self.cursor <= text.len() as u32 { text.insert(self.cursor as usize, self.character); @@ -100,37 +102,37 @@ impl Command for DeleteCharacter { struct DeleteRange { range: (u32, u32), - data: String, + data: Vec, } impl DeleteRange { - fn new(text: &mut String, (start, end): (u32, u32)) -> DeleteRange { + fn new(text: &mut Vec, (start, end): (u32, u32)) -> DeleteRange { let min = start.min(end) as usize; let max = start.max(end) as usize; DeleteRange { - data: text[min..max].to_string(), + data: text[min..max].to_vec(), range: (start, end), } } } impl Command for DeleteRange { - fn apply(&self, text_cursor: &mut u32, text: &mut String) { + fn apply(&self, text_cursor: &mut u32, text: &mut Vec) { let (start, end) = self.range; let min = start.min(end) as usize; let max = start.max(end) as usize; - text.replace_range(min..max, ""); + text.drain(min..max); *text_cursor = min as u32; } - fn unapply(&self, text_cursor: &mut u32, text: &mut String) { + fn unapply(&self, text_cursor: &mut u32, text: &mut Vec) { let (start, end) = self.range; - let start = start.min(end); - text.insert_str(start as usize, &self.data); - *text_cursor = start; + let start = start.min(end) as usize; + text.splice(start..start, self.data.clone()); + *text_cursor = start as u32; } } @@ -163,7 +165,7 @@ pub struct EditboxState { } impl EditboxState { - pub fn clamp_selection<'a>(&mut self, text: &'a str) { + pub fn clamp_selection<'a>(&mut self, text: &'a Vec) { if let Some((ref mut start, ref mut end)) = &mut self.selection { if *start >= text.len() as u32 { *start = text.len() as _; @@ -174,7 +176,7 @@ impl EditboxState { } } - pub fn selected_text<'a>(&self, text: &'a str) -> Option<&'a str> { + pub fn selected_text<'a>(&self, text: &'a Vec) -> Option<&'a [char]> { if let Some((start, end)) = self.selection { let min = start.min(end) as usize; let max = start.max(end) as usize; @@ -194,21 +196,21 @@ impl EditboxState { _ => false, } } - pub fn find_line_begin(&self, text: &str) -> u32 { + pub fn find_line_begin(&self, text: &Vec) -> u32 { let mut line_position = 0; let mut cursor_tmp = self.cursor; - while cursor_tmp > 0 && text.chars().nth(cursor_tmp as usize - 1).unwrap_or('x') != '\n' { + while cursor_tmp > 0 && text.get(cursor_tmp as usize - 1).copied().unwrap_or('x') != '\n' { cursor_tmp -= 1; line_position += 1; } line_position } - pub fn find_line_end(&self, text: &str) -> u32 { + pub fn find_line_end(&self, text: &Vec) -> u32 { let mut cursor_tmp = self.cursor; while cursor_tmp < text.len() as u32 - && text.chars().nth(cursor_tmp as usize).unwrap_or('x') != '\n' + && text.get(cursor_tmp as usize).copied().unwrap_or('x') != '\n' { cursor_tmp += 1; } @@ -224,12 +226,12 @@ impl EditboxState { || character == '\"' } - pub fn find_word_begin(&self, text: &str, cursor: u32) -> u32 { + pub fn find_word_begin(&self, text: &Vec, cursor: u32) -> u32 { let mut cursor_tmp = cursor; let mut offset = 0; while cursor_tmp > 0 { - let current_char = text.chars().nth(cursor_tmp as usize - 1).unwrap_or(' '); + let current_char = text.get(cursor_tmp as usize - 1).copied().unwrap_or(' '); if Self::word_delimiter(current_char) || current_char == '\n' { break; } @@ -239,13 +241,13 @@ impl EditboxState { offset } - pub fn find_word_end(&self, text: &str, cursor: u32) -> u32 { + pub fn find_word_end(&self, text: &Vec, cursor: u32) -> u32 { let mut cursor_tmp = cursor; let mut offset = 0; let mut space_skipping = false; while cursor_tmp < text.len() as u32 { - let current_char = text.chars().nth(cursor_tmp as usize).unwrap_or(' '); + let current_char = text.get(cursor_tmp as usize).copied().unwrap_or(' '); if Self::word_delimiter(current_char) || current_char == '\n' { space_skipping = true; } @@ -258,7 +260,7 @@ impl EditboxState { offset } - pub fn insert_character(&mut self, text: &mut String, character: char) { + pub fn insert_character(&mut self, text: &mut Vec, character: char) { self.redo_stack.clear(); self.selection = None; @@ -268,7 +270,7 @@ impl EditboxState { self.undo_stack.push(Box::new(insert_command)); } - pub fn insert_string(&mut self, text: &mut String, string: String) { + pub fn insert_string(&mut self, text: &mut Vec, string: Vec) { self.redo_stack.clear(); self.selection = None; @@ -278,7 +280,7 @@ impl EditboxState { self.undo_stack.push(Box::new(insert_command)); } - pub fn delete_selected(&mut self, text: &mut String) { + pub fn delete_selected(&mut self, text: &mut Vec) { self.redo_stack.clear(); if let Some(range) = self.selection { @@ -289,7 +291,7 @@ impl EditboxState { self.selection = None; } - pub fn delete_next_character(&mut self, text: &mut String) { + pub fn delete_next_character(&mut self, text: &mut Vec) { self.redo_stack.clear(); if let Some(delete_command) = DeleteCharacter::new(self, text) { @@ -298,26 +300,26 @@ impl EditboxState { } } - pub fn delete_current_character(&mut self, text: &mut String) { + pub fn delete_current_character(&mut self, text: &mut Vec) { if self.cursor > 0 { self.cursor -= 1; self.delete_next_character(text); } } - pub fn move_cursor_next_word(&mut self, text: &str, shift: bool) { + pub fn move_cursor_next_word(&mut self, text: &Vec, shift: bool) { let next_word = self.find_word_end(text, self.cursor + 1) + 1; self.move_cursor(text, next_word as i32, shift); } - pub fn move_cursor_prev_word(&mut self, text: &str, shift: bool) { + pub fn move_cursor_prev_word(&mut self, text: &Vec, shift: bool) { if self.cursor > 1 { let prev_word = self.find_word_begin(text, self.cursor - 1) + 1; self.move_cursor(text, -(prev_word as i32), shift); } } - pub fn move_cursor(&mut self, text: &str, dx: i32, shift: bool) { + pub fn move_cursor(&mut self, text: &Vec, dx: i32, shift: bool) { let start_cursor = self.cursor; let mut end_cursor = start_cursor; @@ -339,11 +341,11 @@ impl EditboxState { } } - pub fn move_cursor_within_line(&mut self, text: &String, dx: i32, shift: bool) { + pub fn move_cursor_within_line(&mut self, text: &Vec, dx: i32, shift: bool) { assert!(dx >= 0, "not implemented"); for _ in 0..dx { - if text.chars().nth(self.cursor as usize).unwrap_or('x') == '\n' + if text.get(self.cursor as usize).copied().unwrap_or('x') == '\n' || self.cursor == text.len() as u32 { break; @@ -352,7 +354,7 @@ impl EditboxState { } } - pub fn select_all(&mut self, text: &str) { + pub fn select_all(&mut self, text: &Vec) { self.selection = Some((0, text.len() as u32)); self.click_state = ClickState::None; } @@ -362,7 +364,7 @@ impl EditboxState { self.selection = None; } - pub fn select_word(&mut self, text: &str) -> (u32, u32) { + pub fn select_word(&mut self, text: &Vec) -> (u32, u32) { let to_word_begin = self.find_word_begin(text, self.cursor) as u32; let to_word_end = self.find_word_end(text, self.cursor) as u32; let new_selection = (self.cursor - to_word_begin, self.cursor + to_word_end); @@ -371,7 +373,7 @@ impl EditboxState { new_selection } - pub fn select_line(&mut self, text: &str) -> (u32, u32) { + pub fn select_line(&mut self, text: &Vec) -> (u32, u32) { let to_line_begin = self.find_line_begin(text) as u32; let to_line_end = self.find_line_end(text) as u32; let new_selection = (self.cursor - to_line_begin, self.cursor + to_line_end); @@ -380,7 +382,7 @@ impl EditboxState { new_selection } - pub fn click_down(&mut self, time: f32, text: &str, cursor: u32) { + pub fn click_down(&mut self, time: f32, text: &Vec, cursor: u32) { self.current_click = cursor; if self.last_click == self.current_click && time - self.last_click_time < DOUBLE_CLICK_TIME @@ -418,7 +420,7 @@ impl EditboxState { self.last_click_time = time; } - pub fn click_move(&mut self, text: &str, cursor: u32) { + pub fn click_move(&mut self, text: &Vec, cursor: u32) { self.cursor = cursor; if self.cursor != self.last_click { @@ -468,7 +470,7 @@ impl EditboxState { self.last_click = cursor; } - pub fn click_up(&mut self, _text: &str) { + pub fn click_up(&mut self, _text: &Vec) { self.click_state = ClickState::None; if let Some((from, to)) = self.selection { if from != to { @@ -479,7 +481,7 @@ impl EditboxState { } } - pub fn undo(&mut self, text: &mut String) { + pub fn undo(&mut self, text: &mut Vec) { let command = self.undo_stack.pop(); if let Some(command) = command { @@ -488,7 +490,7 @@ impl EditboxState { } } - pub fn redo(&mut self, text: &mut String) { + pub fn redo(&mut self, text: &mut Vec) { let command = self.redo_stack.pop(); if let Some(command) = command {