1use iced::Task;
4use iced::widget::operation::{focus, select_all};
5
6use super::command::{
7 Command, CompositeCommand, DeleteCharCommand, DeleteForwardCommand,
8 InsertCharCommand, InsertNewlineCommand, ReplaceTextCommand,
9};
10use super::{
11 ArrowDirection, CURSOR_BLINK_INTERVAL, CodeEditor, ImePreedit, Message,
12};
13
14impl CodeEditor {
15 fn finish_edit_operation(&mut self) {
28 self.reset_cursor_blink();
29 self.refresh_search_matches_if_needed();
30 self.buffer_revision = self.buffer_revision.wrapping_add(1);
33 *self.visual_lines_cache.borrow_mut() = None;
34 self.content_cache.clear();
35 self.overlay_cache.clear();
36 self.enqueue_lsp_change();
37 }
38
39 fn finish_navigation_operation(&mut self) {
47 self.reset_cursor_blink();
48 self.overlay_cache.clear();
49 }
50
51 fn ensure_grouping_started(&mut self, label: &str) {
60 if !self.is_grouping {
61 self.history.begin_group(label);
62 self.is_grouping = true;
63 }
64 }
65
66 fn end_grouping_if_active(&mut self) {
72 if self.is_grouping {
73 self.history.end_group();
74 self.is_grouping = false;
75 }
76 }
77
78 fn delete_selection_if_present(&mut self) -> bool {
84 if self.selection_start.is_some() && self.selection_end.is_some() {
85 self.delete_selection();
86 self.finish_edit_operation();
87 true
88 } else {
89 false
90 }
91 }
92
93 fn handle_character_input_msg(&mut self, ch: char) -> Task<Message> {
112 if !self.has_focus() {
114 return Task::none();
115 }
116
117 self.ensure_grouping_started("Typing");
119
120 let (line, col) = self.cursor;
121 let mut cmd = InsertCharCommand::new(line, col, ch, self.cursor);
122 cmd.execute(&mut self.buffer, &mut self.cursor);
123 self.history.push(Box::new(cmd));
124
125 self.finish_edit_operation();
126
127 if ch.is_alphanumeric() || ch == '_' || ch == '.' {
129 self.lsp_flush_pending_changes();
130 self.lsp_request_completion();
131 }
132
133 self.scroll_to_cursor()
134 }
135
136 fn handle_tab(&mut self) -> Task<Message> {
143 self.ensure_grouping_started("Tab");
146
147 let (line, col) = self.cursor;
148 for i in 0..4 {
150 let current_col = col + i;
151 let mut cmd = InsertCharCommand::new(
152 line,
153 current_col,
154 ' ',
155 (line, current_col),
156 );
157 cmd.execute(&mut self.buffer, &mut self.cursor);
158 self.history.push(Box::new(cmd));
159 }
160
161 self.finish_edit_operation();
162 self.scroll_to_cursor()
163 }
164
165 fn handle_focus_navigation_tab(&mut self) -> Task<Message> {
171 if !self.search_state.is_open {
173 self.has_canvas_focus = false;
175 self.show_cursor = false;
176
177 Task::none()
181 } else {
182 Task::none()
183 }
184 }
185
186 fn handle_focus_navigation_shift_tab(&mut self) -> Task<Message> {
192 if !self.search_state.is_open {
194 self.has_canvas_focus = false;
196 self.show_cursor = false;
197
198 Task::none()
202 } else {
203 Task::none()
204 }
205 }
206
207 fn handle_enter(&mut self) -> Task<Message> {
213 self.end_grouping_if_active();
215
216 let (line, col) = self.cursor;
217 let mut cmd = InsertNewlineCommand::new(line, col, self.cursor);
218 cmd.execute(&mut self.buffer, &mut self.cursor);
219 self.history.push(Box::new(cmd));
220
221 self.finish_edit_operation();
222 self.scroll_to_cursor()
223 }
224
225 fn handle_backspace(&mut self) -> Task<Message> {
238 self.end_grouping_if_active();
240
241 if self.delete_selection_if_present() {
243 return self.scroll_to_cursor();
244 }
245
246 let (line, col) = self.cursor;
248 let mut cmd =
249 DeleteCharCommand::new(&self.buffer, line, col, self.cursor);
250 cmd.execute(&mut self.buffer, &mut self.cursor);
251 self.history.push(Box::new(cmd));
252
253 self.finish_edit_operation();
254 self.scroll_to_cursor()
255 }
256
257 fn handle_delete(&mut self) -> Task<Message> {
266 self.end_grouping_if_active();
268
269 if self.delete_selection_if_present() {
271 return self.scroll_to_cursor();
272 }
273
274 let (line, col) = self.cursor;
276 let mut cmd =
277 DeleteForwardCommand::new(&self.buffer, line, col, self.cursor);
278 cmd.execute(&mut self.buffer, &mut self.cursor);
279 self.history.push(Box::new(cmd));
280
281 self.finish_edit_operation();
282 Task::none()
283 }
284
285 fn handle_delete_selection(&mut self) -> Task<Message> {
293 self.end_grouping_if_active();
295
296 if self.selection_start.is_some() && self.selection_end.is_some() {
297 self.delete_selection();
298 self.finish_edit_operation();
299 self.scroll_to_cursor()
300 } else {
301 Task::none()
302 }
303 }
304
305 fn handle_arrow_key(
320 &mut self,
321 direction: ArrowDirection,
322 shift_pressed: bool,
323 ) -> Task<Message> {
324 self.end_grouping_if_active();
326
327 if shift_pressed {
328 if self.selection_start.is_none() {
330 self.selection_start = Some(self.cursor);
331 }
332 self.move_cursor(direction);
333 self.selection_end = Some(self.cursor);
334 } else {
335 self.clear_selection();
337 self.move_cursor(direction);
338 }
339 self.finish_navigation_operation();
340 self.scroll_to_cursor()
341 }
342
343 fn handle_home(&mut self, shift_pressed: bool) -> Task<Message> {
356 if shift_pressed {
357 if self.selection_start.is_none() {
359 self.selection_start = Some(self.cursor);
360 }
361 self.cursor.1 = 0; self.selection_end = Some(self.cursor);
363 } else {
364 self.clear_selection();
366 self.cursor.1 = 0;
367 }
368 self.finish_navigation_operation();
369 self.scroll_to_cursor()
370 }
371
372 fn handle_end(&mut self, shift_pressed: bool) -> Task<Message> {
385 let line = self.cursor.0;
386 let line_len = self.buffer.line_len(line);
387
388 if shift_pressed {
389 if self.selection_start.is_none() {
391 self.selection_start = Some(self.cursor);
392 }
393 self.cursor.1 = line_len; self.selection_end = Some(self.cursor);
395 } else {
396 self.clear_selection();
398 self.cursor.1 = line_len;
399 }
400 self.finish_navigation_operation();
401 self.scroll_to_cursor()
402 }
403
404 fn handle_ctrl_home(&mut self) -> Task<Message> {
412 self.clear_selection();
414 self.cursor = (0, 0);
415 self.finish_navigation_operation();
416 self.scroll_to_cursor()
417 }
418
419 fn handle_ctrl_end(&mut self) -> Task<Message> {
427 self.clear_selection();
429 let last_line = self.buffer.line_count().saturating_sub(1);
430 let last_col = self.buffer.line_len(last_line);
431 self.cursor = (last_line, last_col);
432 self.finish_navigation_operation();
433 self.scroll_to_cursor()
434 }
435
436 fn handle_page_up(&mut self) -> Task<Message> {
444 self.page_up();
445 self.finish_navigation_operation();
446 self.scroll_to_cursor()
447 }
448
449 fn handle_page_down(&mut self) -> Task<Message> {
457 self.page_down();
458 self.finish_navigation_operation();
459 self.scroll_to_cursor()
460 }
461
462 fn handle_goto_position(
473 &mut self,
474 line: usize,
475 col: usize,
476 ) -> Task<Message> {
477 self.end_grouping_if_active();
479 self.set_cursor(line, col)
480 }
481
482 fn handle_mouse_click_msg(&mut self, point: iced::Point) -> Task<Message> {
498 self.request_focus();
500
501 self.has_canvas_focus = true;
503
504 self.end_grouping_if_active();
506
507 self.handle_mouse_click(point);
508 self.reset_cursor_blink();
509 self.clear_selection();
511 self.is_dragging = true;
512 self.selection_start = Some(self.cursor);
513
514 self.show_cursor = true;
516
517 Task::none()
518 }
519
520 fn handle_mouse_drag_msg(&mut self, point: iced::Point) -> Task<Message> {
530 if self.is_dragging {
531 let before_cursor = self.cursor;
532 let before_selection_end = self.selection_end;
533 self.handle_mouse_drag(point);
534 if self.cursor != before_cursor
535 || self.selection_end != before_selection_end
536 {
537 self.overlay_cache.clear();
540 }
541 }
542 Task::none()
543 }
544
545 fn handle_mouse_release_msg(&mut self) -> Task<Message> {
551 self.is_dragging = false;
552 Task::none()
553 }
554
555 fn handle_paste_msg(&mut self, text: &str) -> Task<Message> {
572 self.end_grouping_if_active();
574
575 if text.is_empty() {
577 iced::clipboard::read().and_then(|clipboard_text| {
579 Task::done(Message::Paste(clipboard_text))
580 })
581 } else {
582 self.paste_text(text);
584 self.finish_edit_operation();
585 self.scroll_to_cursor()
586 }
587 }
588
589 fn handle_undo_msg(&mut self) -> Task<Message> {
599 self.end_grouping_if_active();
601
602 if self.history.undo(&mut self.buffer, &mut self.cursor) {
603 self.clear_selection();
604 self.finish_edit_operation();
605 self.scroll_to_cursor()
606 } else {
607 Task::none()
608 }
609 }
610
611 fn handle_redo_msg(&mut self) -> Task<Message> {
617 if self.history.redo(&mut self.buffer, &mut self.cursor) {
618 self.clear_selection();
619 self.finish_edit_operation();
620 self.scroll_to_cursor()
621 } else {
622 Task::none()
623 }
624 }
625
626 fn handle_open_search_msg(&mut self) -> Task<Message> {
636 self.search_state.open_search();
637 self.overlay_cache.clear();
638
639 Task::batch([
641 focus(self.search_state.search_input_id.clone()),
642 select_all(self.search_state.search_input_id.clone()),
643 ])
644 }
645
646 fn handle_open_search_replace_msg(&mut self) -> Task<Message> {
652 self.search_state.open_replace();
653 self.overlay_cache.clear();
654
655 Task::batch([
657 focus(self.search_state.search_input_id.clone()),
658 select_all(self.search_state.search_input_id.clone()),
659 ])
660 }
661
662 fn handle_close_search_msg(&mut self) -> Task<Message> {
668 self.search_state.close();
669 self.overlay_cache.clear();
670 Task::none()
671 }
672
673 fn handle_search_query_changed_msg(
683 &mut self,
684 query: &str,
685 ) -> Task<Message> {
686 self.search_state.set_query(query.to_string(), &self.buffer);
687 self.overlay_cache.clear();
688
689 if let Some(match_pos) = self.search_state.current_match() {
691 self.cursor = (match_pos.line, match_pos.col);
692 self.clear_selection();
693 return self.scroll_to_cursor();
694 }
695 Task::none()
696 }
697
698 fn handle_replace_query_changed_msg(
708 &mut self,
709 replace_text: &str,
710 ) -> Task<Message> {
711 self.search_state.set_replace_with(replace_text.to_string());
712 Task::none()
713 }
714
715 fn handle_toggle_case_sensitive_msg(&mut self) -> Task<Message> {
721 self.search_state.toggle_case_sensitive(&self.buffer);
722 self.overlay_cache.clear();
723
724 if let Some(match_pos) = self.search_state.current_match() {
726 self.cursor = (match_pos.line, match_pos.col);
727 self.clear_selection();
728 return self.scroll_to_cursor();
729 }
730 Task::none()
731 }
732
733 fn handle_find_next_msg(&mut self) -> Task<Message> {
739 if !self.search_state.matches.is_empty() {
740 self.search_state.next_match();
741 if let Some(match_pos) = self.search_state.current_match() {
742 self.cursor = (match_pos.line, match_pos.col);
743 self.clear_selection();
744 self.overlay_cache.clear();
745 return self.scroll_to_cursor();
746 }
747 }
748 Task::none()
749 }
750
751 fn handle_find_previous_msg(&mut self) -> Task<Message> {
757 if !self.search_state.matches.is_empty() {
758 self.search_state.previous_match();
759 if let Some(match_pos) = self.search_state.current_match() {
760 self.cursor = (match_pos.line, match_pos.col);
761 self.clear_selection();
762 self.overlay_cache.clear();
763 return self.scroll_to_cursor();
764 }
765 }
766 Task::none()
767 }
768
769 fn handle_replace_next_msg(&mut self) -> Task<Message> {
775 if let Some(match_pos) = self.search_state.current_match() {
777 let query_len = self.search_state.query.chars().count();
778 let replace_text = self.search_state.replace_with.clone();
779
780 let mut cmd = ReplaceTextCommand::new(
782 &self.buffer,
783 (match_pos.line, match_pos.col),
784 query_len,
785 replace_text,
786 self.cursor,
787 );
788 cmd.execute(&mut self.buffer, &mut self.cursor);
789 self.history.push(Box::new(cmd));
790
791 self.search_state.update_matches(&self.buffer);
793
794 if !self.search_state.matches.is_empty()
796 && let Some(next_match) = self.search_state.current_match()
797 {
798 self.cursor = (next_match.line, next_match.col);
799 }
800
801 self.clear_selection();
802 self.finish_edit_operation();
803 return self.scroll_to_cursor();
804 }
805 Task::none()
806 }
807
808 fn handle_replace_all_msg(&mut self) -> Task<Message> {
814 let all_matches = super::search::find_matches(
816 &self.buffer,
817 &self.search_state.query,
818 self.search_state.case_sensitive,
819 None, );
821
822 if !all_matches.is_empty() {
823 let query_len = self.search_state.query.chars().count();
824 let replace_text = self.search_state.replace_with.clone();
825
826 let mut composite =
828 CompositeCommand::new("Replace All".to_string());
829
830 for match_pos in all_matches.iter().rev() {
832 let cmd = ReplaceTextCommand::new(
833 &self.buffer,
834 (match_pos.line, match_pos.col),
835 query_len,
836 replace_text.clone(),
837 self.cursor,
838 );
839 composite.add(Box::new(cmd));
840 }
841
842 composite.execute(&mut self.buffer, &mut self.cursor);
844 self.history.push(Box::new(composite));
845
846 self.search_state.update_matches(&self.buffer);
848 self.clear_selection();
849 self.finish_edit_operation();
850 self.scroll_to_cursor()
851 } else {
852 Task::none()
853 }
854 }
855
856 fn handle_search_dialog_tab_msg(&mut self) -> Task<Message> {
862 self.search_state.focus_next_field();
864
865 match self.search_state.focused_field {
867 crate::canvas_editor::search::SearchFocusedField::Search => {
868 focus(self.search_state.search_input_id.clone())
869 }
870 crate::canvas_editor::search::SearchFocusedField::Replace => {
871 focus(self.search_state.replace_input_id.clone())
872 }
873 }
874 }
875
876 fn handle_search_dialog_shift_tab_msg(&mut self) -> Task<Message> {
882 self.search_state.focus_previous_field();
884
885 match self.search_state.focused_field {
887 crate::canvas_editor::search::SearchFocusedField::Search => {
888 focus(self.search_state.search_input_id.clone())
889 }
890 crate::canvas_editor::search::SearchFocusedField::Replace => {
891 focus(self.search_state.replace_input_id.clone())
892 }
893 }
894 }
895
896 fn handle_canvas_focus_gained_msg(&mut self) -> Task<Message> {
906 self.has_canvas_focus = true;
907 self.focus_locked = false; self.show_cursor = true;
909 self.reset_cursor_blink();
910 self.overlay_cache.clear();
911 Task::none()
912 }
913
914 fn handle_canvas_focus_lost_msg(&mut self) -> Task<Message> {
920 self.has_canvas_focus = false;
921 self.focus_locked = true; self.show_cursor = false;
923 self.ime_preedit = None;
924 self.overlay_cache.clear();
925 Task::none()
926 }
927
928 fn handle_ime_opened_msg(&mut self) -> Task<Message> {
936 self.ime_preedit = None;
937 self.overlay_cache.clear();
938 Task::none()
939 }
940
941 fn handle_ime_preedit_msg(
954 &mut self,
955 content: &str,
956 selection: &Option<std::ops::Range<usize>>,
957 ) -> Task<Message> {
958 if content.is_empty() {
959 self.ime_preedit = None;
960 } else {
961 self.ime_preedit = Some(ImePreedit {
962 content: content.to_string(),
963 selection: selection.clone(),
964 });
965 }
966
967 self.overlay_cache.clear();
968 Task::none()
969 }
970
971 fn handle_ime_commit_msg(&mut self, text: &str) -> Task<Message> {
983 self.ime_preedit = None;
984
985 if text.is_empty() {
986 self.overlay_cache.clear();
987 return Task::none();
988 }
989
990 self.ensure_grouping_started("Typing");
991
992 self.paste_text(text);
993 self.finish_edit_operation();
994 self.scroll_to_cursor()
995 }
996
997 fn handle_ime_closed_msg(&mut self) -> Task<Message> {
1005 self.ime_preedit = None;
1006 self.overlay_cache.clear();
1007 Task::none()
1008 }
1009
1010 fn handle_tick_msg(&mut self) -> Task<Message> {
1022 if self.has_focus()
1024 && self.last_blink.elapsed() >= CURSOR_BLINK_INTERVAL
1025 {
1026 self.cursor_visible = !self.cursor_visible;
1027 self.last_blink = super::Instant::now();
1028 self.overlay_cache.clear();
1029 }
1030
1031 if !self.has_focus() {
1033 self.show_cursor = false;
1034 }
1035
1036 Task::none()
1037 }
1038
1039 fn handle_scrolled_msg(
1053 &mut self,
1054 viewport: iced::widget::scrollable::Viewport,
1055 ) -> Task<Message> {
1056 let new_scroll = viewport.absolute_offset().y;
1065 let new_height = viewport.bounds().height;
1066 let new_width = viewport.bounds().width;
1067 let scroll_changed = (self.viewport_scroll - new_scroll).abs() > 0.1;
1068 let visible_lines_count =
1069 (new_height / self.line_height).ceil() as usize + 2;
1070 let first_visible_line =
1071 (new_scroll / self.line_height).floor() as usize;
1072 let last_visible_line = first_visible_line + visible_lines_count;
1073 let margin = visible_lines_count
1074 * crate::canvas_editor::CACHE_WINDOW_MARGIN_MULTIPLIER;
1075 let window_start = first_visible_line.saturating_sub(margin);
1076 let window_end = last_visible_line + margin;
1077 let need_rewindow =
1081 if self.cache_window_end_line > self.cache_window_start_line {
1082 let lower_boundary_trigger = self.cache_window_start_line > 0
1083 && first_visible_line
1084 < self
1085 .cache_window_start_line
1086 .saturating_add(visible_lines_count / 2);
1087 let upper_boundary_trigger = last_visible_line
1088 > self
1089 .cache_window_end_line
1090 .saturating_sub(visible_lines_count / 2);
1091 lower_boundary_trigger || upper_boundary_trigger
1092 } else {
1093 true
1094 };
1095 if (self.viewport_height - new_height).abs() > 1.0
1098 || (self.viewport_width - new_width).abs() > 1.0
1099 || (scroll_changed && need_rewindow)
1100 {
1101 self.cache_window_start_line = window_start;
1102 self.cache_window_end_line = window_end;
1103 self.last_first_visible_line = first_visible_line;
1104 self.content_cache.clear();
1105 self.overlay_cache.clear();
1106 }
1107 self.viewport_scroll = new_scroll;
1108 self.viewport_height = new_height;
1109 self.viewport_width = new_width;
1110 Task::none()
1111 }
1112
1113 fn handle_horizontal_scrolled_msg(
1126 &mut self,
1127 viewport: iced::widget::scrollable::Viewport,
1128 ) -> Task<Message> {
1129 let new_x = viewport.absolute_offset().x;
1130 if (self.horizontal_scroll_offset - new_x).abs() > 0.1 {
1131 self.horizontal_scroll_offset = new_x;
1132 self.content_cache.clear();
1133 self.overlay_cache.clear();
1134 }
1135 Task::none()
1136 }
1137
1138 pub fn update(&mut self, message: &Message) -> Task<Message> {
1151 match message {
1152 Message::CharacterInput(ch) => self.handle_character_input_msg(*ch),
1154 Message::Tab => self.handle_tab(),
1155 Message::Enter => self.handle_enter(),
1156
1157 Message::Backspace => self.handle_backspace(),
1159 Message::Delete => self.handle_delete(),
1160 Message::DeleteSelection => self.handle_delete_selection(),
1161
1162 Message::ArrowKey(direction, shift) => {
1164 self.handle_arrow_key(*direction, *shift)
1165 }
1166 Message::Home(shift) => self.handle_home(*shift),
1167 Message::End(shift) => self.handle_end(*shift),
1168 Message::CtrlHome => self.handle_ctrl_home(),
1169 Message::CtrlEnd => self.handle_ctrl_end(),
1170 Message::GotoPosition(line, col) => {
1171 self.handle_goto_position(*line, *col)
1172 }
1173 Message::PageUp => self.handle_page_up(),
1174 Message::PageDown => self.handle_page_down(),
1175
1176 Message::MouseClick(point) => self.handle_mouse_click_msg(*point),
1178 Message::MouseDrag(point) => self.handle_mouse_drag_msg(*point),
1179 Message::MouseHover(point) => self.handle_mouse_drag_msg(*point),
1180 Message::MouseRelease => self.handle_mouse_release_msg(),
1181
1182 Message::Copy => self.copy_selection(),
1184 Message::Paste(text) => self.handle_paste_msg(text),
1185
1186 Message::Undo => self.handle_undo_msg(),
1188 Message::Redo => self.handle_redo_msg(),
1189
1190 Message::OpenSearch => self.handle_open_search_msg(),
1192 Message::OpenSearchReplace => self.handle_open_search_replace_msg(),
1193 Message::CloseSearch => self.handle_close_search_msg(),
1194 Message::SearchQueryChanged(query) => {
1195 self.handle_search_query_changed_msg(query)
1196 }
1197 Message::ReplaceQueryChanged(text) => {
1198 self.handle_replace_query_changed_msg(text)
1199 }
1200 Message::ToggleCaseSensitive => {
1201 self.handle_toggle_case_sensitive_msg()
1202 }
1203 Message::FindNext => self.handle_find_next_msg(),
1204 Message::FindPrevious => self.handle_find_previous_msg(),
1205 Message::ReplaceNext => self.handle_replace_next_msg(),
1206 Message::ReplaceAll => self.handle_replace_all_msg(),
1207 Message::SearchDialogTab => self.handle_search_dialog_tab_msg(),
1208 Message::SearchDialogShiftTab => {
1209 self.handle_search_dialog_shift_tab_msg()
1210 }
1211 Message::FocusNavigationTab => self.handle_focus_navigation_tab(),
1212 Message::FocusNavigationShiftTab => {
1213 self.handle_focus_navigation_shift_tab()
1214 }
1215
1216 Message::CanvasFocusGained => self.handle_canvas_focus_gained_msg(),
1218 Message::CanvasFocusLost => self.handle_canvas_focus_lost_msg(),
1219 Message::ImeOpened => self.handle_ime_opened_msg(),
1220 Message::ImePreedit(content, selection) => {
1221 self.handle_ime_preedit_msg(content, selection)
1222 }
1223 Message::ImeCommit(text) => self.handle_ime_commit_msg(text),
1224 Message::ImeClosed => self.handle_ime_closed_msg(),
1225
1226 Message::Tick => self.handle_tick_msg(),
1228 Message::Scrolled(viewport) => self.handle_scrolled_msg(*viewport),
1229 Message::HorizontalScrolled(viewport) => {
1230 self.handle_horizontal_scrolled_msg(*viewport)
1231 }
1232
1233 Message::JumpClick(_point) => Task::none(),
1237 }
1238 }
1239}
1240
1241#[cfg(test)]
1242mod tests {
1243 use super::*;
1244 use crate::canvas_editor::ArrowDirection;
1245
1246 #[test]
1247 fn test_horizontal_scroll_initial_state() {
1248 let editor = CodeEditor::new("short line", "rs");
1249 assert!(
1250 (editor.horizontal_scroll_offset - 0.0).abs() < f32::EPSILON,
1251 "Initial horizontal scroll offset should be 0"
1252 );
1253 }
1254
1255 #[test]
1256 fn test_set_wrap_enabled_resets_horizontal_offset() {
1257 let mut editor = CodeEditor::new("long line", "rs");
1258 editor.wrap_enabled = false;
1259 editor.horizontal_scroll_offset = 100.0;
1261
1262 editor.set_wrap_enabled(true);
1264 assert!(
1265 (editor.horizontal_scroll_offset - 0.0).abs() < f32::EPSILON,
1266 "Horizontal scroll offset should be reset when wrap is re-enabled"
1267 );
1268 }
1269
1270 #[test]
1271 fn test_canvas_focus_lost() {
1272 let mut editor = CodeEditor::new("test", "rs");
1273 editor.has_canvas_focus = true;
1274
1275 let _ = editor.update(&Message::CanvasFocusLost);
1276
1277 assert!(!editor.has_canvas_focus);
1278 assert!(!editor.show_cursor);
1279 assert!(editor.focus_locked, "Focus should be locked when lost");
1280 }
1281
1282 #[test]
1283 fn test_canvas_focus_gained_resets_lock() {
1284 let mut editor = CodeEditor::new("test", "rs");
1285 editor.has_canvas_focus = false;
1286 editor.focus_locked = true;
1287
1288 let _ = editor.update(&Message::CanvasFocusGained);
1289
1290 assert!(editor.has_canvas_focus);
1291 assert!(
1292 !editor.focus_locked,
1293 "Focus lock should be reset when focus is gained"
1294 );
1295 }
1296
1297 #[test]
1298 fn test_focus_lock_state() {
1299 let mut editor = CodeEditor::new("test", "rs");
1300
1301 assert!(!editor.focus_locked);
1303
1304 let _ = editor.update(&Message::CanvasFocusLost);
1306 assert!(editor.focus_locked, "Focus should be locked when lost");
1307
1308 editor.request_focus();
1310 let _ = editor.update(&Message::CanvasFocusGained);
1311 assert!(!editor.focus_locked, "Focus should be unlocked when regained");
1312
1313 editor.focus_locked = true;
1315 editor.reset_focus_lock();
1316 assert!(!editor.focus_locked, "Focus lock should be resetable");
1317 }
1318
1319 #[test]
1320 fn test_reset_focus_lock() {
1321 let mut editor = CodeEditor::new("test", "rs");
1322 editor.focus_locked = true;
1323
1324 editor.reset_focus_lock();
1325
1326 assert!(!editor.focus_locked);
1327 }
1328
1329 #[test]
1330 fn test_home_key() {
1331 let mut editor = CodeEditor::new("hello world", "py");
1332 editor.cursor = (0, 5); let _ = editor.update(&Message::Home(false));
1334 assert_eq!(editor.cursor, (0, 0));
1335 }
1336
1337 #[test]
1338 fn test_end_key() {
1339 let mut editor = CodeEditor::new("hello world", "py");
1340 editor.cursor = (0, 0);
1341 let _ = editor.update(&Message::End(false));
1342 assert_eq!(editor.cursor, (0, 11)); }
1344
1345 #[test]
1346 fn test_arrow_key_with_shift_creates_selection() {
1347 let mut editor = CodeEditor::new("hello world", "py");
1348 editor.cursor = (0, 0);
1349
1350 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, true));
1352 assert!(editor.selection_start.is_some());
1353 assert!(editor.selection_end.is_some());
1354 }
1355
1356 #[test]
1357 fn test_arrow_key_without_shift_clears_selection() {
1358 let mut editor = CodeEditor::new("hello world", "py");
1359 editor.selection_start = Some((0, 0));
1360 editor.selection_end = Some((0, 5));
1361
1362 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Right, false));
1364 assert_eq!(editor.selection_start, None);
1365 assert_eq!(editor.selection_end, None);
1366 }
1367
1368 #[test]
1369 fn test_typing_with_selection() {
1370 let mut editor = CodeEditor::new("hello world", "py");
1371 editor.request_focus();
1373 editor.has_canvas_focus = true;
1374 editor.focus_locked = false;
1375
1376 editor.selection_start = Some((0, 0));
1377 editor.selection_end = Some((0, 5));
1378
1379 let _ = editor.update(&Message::CharacterInput('X'));
1380 assert_eq!(editor.buffer.line(0), "Xhello world");
1383 }
1384
1385 #[test]
1386 fn test_ctrl_home() {
1387 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1388 editor.cursor = (2, 5); let _ = editor.update(&Message::CtrlHome);
1390 assert_eq!(editor.cursor, (0, 0)); }
1392
1393 #[test]
1394 fn test_ctrl_end() {
1395 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1396 editor.cursor = (0, 0); let _ = editor.update(&Message::CtrlEnd);
1398 assert_eq!(editor.cursor, (2, 5)); }
1400
1401 #[test]
1402 fn test_ctrl_home_clears_selection() {
1403 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1404 editor.cursor = (2, 5);
1405 editor.selection_start = Some((0, 0));
1406 editor.selection_end = Some((2, 5));
1407
1408 let _ = editor.update(&Message::CtrlHome);
1409 assert_eq!(editor.cursor, (0, 0));
1410 assert_eq!(editor.selection_start, None);
1411 assert_eq!(editor.selection_end, None);
1412 }
1413
1414 #[test]
1415 fn test_ctrl_end_clears_selection() {
1416 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1417 editor.cursor = (0, 0);
1418 editor.selection_start = Some((0, 0));
1419 editor.selection_end = Some((1, 3));
1420
1421 let _ = editor.update(&Message::CtrlEnd);
1422 assert_eq!(editor.cursor, (2, 5));
1423 assert_eq!(editor.selection_start, None);
1424 assert_eq!(editor.selection_end, None);
1425 }
1426
1427 #[test]
1428 fn test_goto_position_sets_cursor_and_clears_selection() {
1429 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1430 editor.selection_start = Some((0, 0));
1431 editor.selection_end = Some((1, 2));
1432
1433 let _ = editor.update(&Message::GotoPosition(1, 3));
1434
1435 assert_eq!(editor.cursor, (1, 3));
1436 assert_eq!(editor.selection_start, None);
1437 assert_eq!(editor.selection_end, None);
1438 }
1439
1440 #[test]
1441 fn test_goto_position_clamps_out_of_range() {
1442 let mut editor = CodeEditor::new("a\nbb", "py");
1443
1444 let _ = editor.update(&Message::GotoPosition(99, 99));
1445
1446 assert_eq!(editor.cursor, (1, 2));
1448 }
1449
1450 #[test]
1451 fn test_scroll_sets_initial_cache_window() {
1452 let content =
1453 (0..200).map(|i| format!("line{}\n", i)).collect::<String>();
1454 let mut editor = CodeEditor::new(&content, "py");
1455
1456 let height = 400.0;
1458 let width = 800.0;
1459 let scroll = 0.0;
1460
1461 let visible_lines_count =
1463 (height / editor.line_height).ceil() as usize + 2;
1464 let first_visible_line = (scroll / editor.line_height).floor() as usize;
1465 let last_visible_line = first_visible_line + visible_lines_count;
1466 let margin = visible_lines_count * 2;
1467 let window_start = first_visible_line.saturating_sub(margin);
1468 let window_end = last_visible_line + margin;
1469
1470 editor.viewport_height = height;
1472 editor.viewport_width = width;
1473 editor.viewport_scroll = -1.0;
1474 let scroll_changed = (editor.viewport_scroll - scroll).abs() > 0.1;
1475 let need_rewindow = true;
1476 if (editor.viewport_height - height).abs() > 1.0
1477 || (editor.viewport_width - width).abs() > 1.0
1478 || (scroll_changed && need_rewindow)
1479 {
1480 editor.cache_window_start_line = window_start;
1481 editor.cache_window_end_line = window_end;
1482 editor.last_first_visible_line = first_visible_line;
1483 }
1484 editor.viewport_scroll = scroll;
1485
1486 assert_eq!(editor.last_first_visible_line, first_visible_line);
1487 assert!(editor.cache_window_end_line > editor.cache_window_start_line);
1488 assert_eq!(editor.cache_window_start_line, window_start);
1489 assert_eq!(editor.cache_window_end_line, window_end);
1490 }
1491
1492 #[test]
1493 fn test_small_scroll_keeps_window() {
1494 let content =
1495 (0..200).map(|i| format!("line{}\n", i)).collect::<String>();
1496 let mut editor = CodeEditor::new(&content, "py");
1497 let height = 400.0;
1498 let width = 800.0;
1499 let initial_scroll = 0.0;
1500 let visible_lines_count =
1501 (height / editor.line_height).ceil() as usize + 2;
1502 let first_visible_line =
1503 (initial_scroll / editor.line_height).floor() as usize;
1504 let last_visible_line = first_visible_line + visible_lines_count;
1505 let margin = visible_lines_count * 2;
1506 let window_start = first_visible_line.saturating_sub(margin);
1507 let window_end = last_visible_line + margin;
1508 editor.cache_window_start_line = window_start;
1509 editor.cache_window_end_line = window_end;
1510 editor.viewport_height = height;
1511 editor.viewport_width = width;
1512 editor.viewport_scroll = initial_scroll;
1513
1514 let small_scroll =
1516 editor.line_height * (visible_lines_count as f32 / 4.0);
1517 let first_visible_line2 =
1518 (small_scroll / editor.line_height).floor() as usize;
1519 let last_visible_line2 = first_visible_line2 + visible_lines_count;
1520 let lower_boundary_trigger = editor.cache_window_start_line > 0
1521 && first_visible_line2
1522 < editor
1523 .cache_window_start_line
1524 .saturating_add(visible_lines_count / 2);
1525 let upper_boundary_trigger = last_visible_line2
1526 > editor
1527 .cache_window_end_line
1528 .saturating_sub(visible_lines_count / 2);
1529 let need_rewindow = lower_boundary_trigger || upper_boundary_trigger;
1530
1531 assert!(!need_rewindow, "Small scroll should be inside the window");
1532 assert_eq!(editor.cache_window_start_line, window_start);
1534 assert_eq!(editor.cache_window_end_line, window_end);
1535 }
1536
1537 #[test]
1538 fn test_large_scroll_rewindows() {
1539 let content =
1540 (0..1000).map(|i| format!("line{}\n", i)).collect::<String>();
1541 let mut editor = CodeEditor::new(&content, "py");
1542 let height = 400.0;
1543 let width = 800.0;
1544 let initial_scroll = 0.0;
1545 let visible_lines_count =
1546 (height / editor.line_height).ceil() as usize + 2;
1547 let first_visible_line =
1548 (initial_scroll / editor.line_height).floor() as usize;
1549 let last_visible_line = first_visible_line + visible_lines_count;
1550 let margin = visible_lines_count * 2;
1551 editor.cache_window_start_line =
1552 first_visible_line.saturating_sub(margin);
1553 editor.cache_window_end_line = last_visible_line + margin;
1554 editor.viewport_height = height;
1555 editor.viewport_width = width;
1556 editor.viewport_scroll = initial_scroll;
1557
1558 let large_scroll =
1560 editor.line_height * ((visible_lines_count * 4) as f32);
1561 let first_visible_line2 =
1562 (large_scroll / editor.line_height).floor() as usize;
1563 let last_visible_line2 = first_visible_line2 + visible_lines_count;
1564 let window_start2 = first_visible_line2.saturating_sub(margin);
1565 let window_end2 = last_visible_line2 + margin;
1566 let need_rewindow = first_visible_line2
1567 < editor
1568 .cache_window_start_line
1569 .saturating_add(visible_lines_count / 2)
1570 || last_visible_line2
1571 > editor
1572 .cache_window_end_line
1573 .saturating_sub(visible_lines_count / 2);
1574 assert!(need_rewindow, "Large scroll should trigger window update");
1575
1576 editor.cache_window_start_line = window_start2;
1578 editor.cache_window_end_line = window_end2;
1579 editor.last_first_visible_line = first_visible_line2;
1580
1581 assert_eq!(editor.cache_window_start_line, window_start2);
1582 assert_eq!(editor.cache_window_end_line, window_end2);
1583 assert_eq!(editor.last_first_visible_line, first_visible_line2);
1584 }
1585
1586 #[test]
1587 fn test_delete_selection_message() {
1588 let mut editor = CodeEditor::new("hello world", "py");
1589 editor.cursor = (0, 0);
1590 editor.selection_start = Some((0, 0));
1591 editor.selection_end = Some((0, 5));
1592
1593 let _ = editor.update(&Message::DeleteSelection);
1594 assert_eq!(editor.buffer.line(0), " world");
1595 assert_eq!(editor.cursor, (0, 0));
1596 assert_eq!(editor.selection_start, None);
1597 assert_eq!(editor.selection_end, None);
1598 }
1599
1600 #[test]
1601 fn test_delete_selection_multiline() {
1602 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1603 editor.cursor = (0, 2);
1604 editor.selection_start = Some((0, 2));
1605 editor.selection_end = Some((2, 2));
1606
1607 let _ = editor.update(&Message::DeleteSelection);
1608 assert_eq!(editor.buffer.line(0), "line3");
1609 assert_eq!(editor.cursor, (0, 2));
1610 assert_eq!(editor.selection_start, None);
1611 }
1612
1613 #[test]
1614 fn test_delete_selection_no_selection() {
1615 let mut editor = CodeEditor::new("hello world", "py");
1616 editor.cursor = (0, 5);
1617
1618 let _ = editor.update(&Message::DeleteSelection);
1619 assert_eq!(editor.buffer.line(0), "hello world");
1621 assert_eq!(editor.cursor, (0, 5));
1622 }
1623
1624 #[test]
1625 #[allow(clippy::unwrap_used)]
1626 fn test_ime_preedit_and_commit_chinese() {
1627 let mut editor = CodeEditor::new("", "py");
1628 let _ = editor.update(&Message::ImeOpened);
1630 assert!(editor.ime_preedit.is_none());
1631
1632 let content = "安全与合规".to_string();
1634 let selection = Some(0..3); let _ = editor
1636 .update(&Message::ImePreedit(content.clone(), selection.clone()));
1637
1638 assert!(editor.ime_preedit.is_some());
1639 assert_eq!(
1640 editor.ime_preedit.as_ref().unwrap().content.clone(),
1641 content
1642 );
1643 assert_eq!(
1644 editor.ime_preedit.as_ref().unwrap().selection.clone(),
1645 selection
1646 );
1647
1648 let _ = editor.update(&Message::ImeCommit("安全与合规".to_string()));
1650 assert!(editor.ime_preedit.is_none());
1651 assert_eq!(editor.buffer.line(0), "安全与合规");
1652 assert_eq!(editor.cursor, (0, "安全与合规".chars().count()));
1653 }
1654
1655 #[test]
1656 fn test_undo_char_insert() {
1657 let mut editor = CodeEditor::new("hello", "py");
1658 editor.request_focus();
1660 editor.has_canvas_focus = true;
1661 editor.focus_locked = false;
1662
1663 editor.cursor = (0, 5);
1664
1665 let _ = editor.update(&Message::CharacterInput('!'));
1667 assert_eq!(editor.buffer.line(0), "hello!");
1668 assert_eq!(editor.cursor, (0, 6));
1669
1670 editor.history.end_group();
1672 let _ = editor.update(&Message::Undo);
1673 assert_eq!(editor.buffer.line(0), "hello");
1674 assert_eq!(editor.cursor, (0, 5));
1675 }
1676
1677 #[test]
1678 fn test_undo_redo_char_insert() {
1679 let mut editor = CodeEditor::new("hello", "py");
1680 editor.request_focus();
1682 editor.has_canvas_focus = true;
1683 editor.focus_locked = false;
1684
1685 editor.cursor = (0, 5);
1686
1687 let _ = editor.update(&Message::CharacterInput('!'));
1689 editor.history.end_group();
1690
1691 let _ = editor.update(&Message::Undo);
1693 assert_eq!(editor.buffer.line(0), "hello");
1694
1695 let _ = editor.update(&Message::Redo);
1697 assert_eq!(editor.buffer.line(0), "hello!");
1698 assert_eq!(editor.cursor, (0, 6));
1699 }
1700
1701 #[test]
1702 fn test_undo_backspace() {
1703 let mut editor = CodeEditor::new("hello", "py");
1704 editor.cursor = (0, 5);
1705
1706 let _ = editor.update(&Message::Backspace);
1708 assert_eq!(editor.buffer.line(0), "hell");
1709 assert_eq!(editor.cursor, (0, 4));
1710
1711 let _ = editor.update(&Message::Undo);
1713 assert_eq!(editor.buffer.line(0), "hello");
1714 assert_eq!(editor.cursor, (0, 5));
1715 }
1716
1717 #[test]
1718 fn test_undo_newline() {
1719 let mut editor = CodeEditor::new("hello world", "py");
1720 editor.cursor = (0, 5);
1721
1722 let _ = editor.update(&Message::Enter);
1724 assert_eq!(editor.buffer.line(0), "hello");
1725 assert_eq!(editor.buffer.line(1), " world");
1726 assert_eq!(editor.cursor, (1, 0));
1727
1728 let _ = editor.update(&Message::Undo);
1730 assert_eq!(editor.buffer.line(0), "hello world");
1731 assert_eq!(editor.cursor, (0, 5));
1732 }
1733
1734 #[test]
1735 fn test_undo_grouped_typing() {
1736 let mut editor = CodeEditor::new("hello", "py");
1737 editor.request_focus();
1739 editor.has_canvas_focus = true;
1740 editor.focus_locked = false;
1741
1742 editor.cursor = (0, 5);
1743
1744 let _ = editor.update(&Message::CharacterInput(' '));
1746 let _ = editor.update(&Message::CharacterInput('w'));
1747 let _ = editor.update(&Message::CharacterInput('o'));
1748 let _ = editor.update(&Message::CharacterInput('r'));
1749 let _ = editor.update(&Message::CharacterInput('l'));
1750 let _ = editor.update(&Message::CharacterInput('d'));
1751
1752 assert_eq!(editor.buffer.line(0), "hello world");
1753
1754 editor.history.end_group();
1756
1757 let _ = editor.update(&Message::Undo);
1759 assert_eq!(editor.buffer.line(0), "hello");
1760 assert_eq!(editor.cursor, (0, 5));
1761 }
1762
1763 #[test]
1764 fn test_navigation_ends_grouping() {
1765 let mut editor = CodeEditor::new("hello", "py");
1766 editor.request_focus();
1768 editor.has_canvas_focus = true;
1769 editor.focus_locked = false;
1770
1771 editor.cursor = (0, 5);
1772
1773 let _ = editor.update(&Message::CharacterInput('!'));
1775 assert!(editor.is_grouping);
1776
1777 let _ = editor.update(&Message::ArrowKey(ArrowDirection::Left, false));
1779 assert!(!editor.is_grouping);
1780
1781 let _ = editor.update(&Message::CharacterInput('?'));
1783 assert!(editor.is_grouping);
1784
1785 editor.history.end_group();
1786
1787 let _ = editor.update(&Message::Undo);
1789 assert_eq!(editor.buffer.line(0), "hello!");
1790
1791 let _ = editor.update(&Message::Undo);
1792 assert_eq!(editor.buffer.line(0), "hello");
1793 }
1794
1795 #[test]
1796 fn test_edit_increments_revision_and_clears_visual_lines_cache() {
1797 let mut editor = CodeEditor::new("hello", "rs");
1798 editor.request_focus();
1799 editor.has_canvas_focus = true;
1800 editor.focus_locked = false;
1801 editor.cursor = (0, 5);
1802
1803 let _ = editor.visual_lines_cached(800.0);
1804 assert!(
1805 editor.visual_lines_cache.borrow().is_some(),
1806 "visual_lines_cached should populate the cache"
1807 );
1808
1809 let previous_revision = editor.buffer_revision;
1810
1811 let _ = editor.update(&Message::CharacterInput('!'));
1812 assert_eq!(
1813 editor.buffer_revision,
1814 previous_revision.wrapping_add(1),
1815 "buffer_revision should change on buffer edits"
1816 );
1817 assert!(
1821 editor
1822 .visual_lines_cache
1823 .borrow()
1824 .as_ref()
1825 .is_none_or(|c| c.key.buffer_revision == editor.buffer_revision),
1826 "buffer edits should not leave stale data in the visual lines cache"
1827 );
1828 }
1829
1830 #[test]
1831 fn test_multiple_undo_redo() {
1832 let mut editor = CodeEditor::new("a", "py");
1833 editor.request_focus();
1835 editor.has_canvas_focus = true;
1836 editor.focus_locked = false;
1837
1838 editor.cursor = (0, 1);
1839
1840 let _ = editor.update(&Message::CharacterInput('b'));
1842 editor.history.end_group();
1843
1844 let _ = editor.update(&Message::CharacterInput('c'));
1845 editor.history.end_group();
1846
1847 let _ = editor.update(&Message::CharacterInput('d'));
1848 editor.history.end_group();
1849
1850 assert_eq!(editor.buffer.line(0), "abcd");
1851
1852 let _ = editor.update(&Message::Undo);
1854 assert_eq!(editor.buffer.line(0), "abc");
1855
1856 let _ = editor.update(&Message::Undo);
1857 assert_eq!(editor.buffer.line(0), "ab");
1858
1859 let _ = editor.update(&Message::Undo);
1860 assert_eq!(editor.buffer.line(0), "a");
1861
1862 let _ = editor.update(&Message::Redo);
1864 assert_eq!(editor.buffer.line(0), "ab");
1865
1866 let _ = editor.update(&Message::Redo);
1867 assert_eq!(editor.buffer.line(0), "abc");
1868
1869 let _ = editor.update(&Message::Redo);
1870 assert_eq!(editor.buffer.line(0), "abcd");
1871 }
1872
1873 #[test]
1874 fn test_delete_key_with_selection() {
1875 let mut editor = CodeEditor::new("hello world", "py");
1876 editor.selection_start = Some((0, 0));
1877 editor.selection_end = Some((0, 5));
1878 editor.cursor = (0, 5);
1879
1880 let _ = editor.update(&Message::Delete);
1881
1882 assert_eq!(editor.buffer.line(0), " world");
1883 assert_eq!(editor.cursor, (0, 0));
1884 assert_eq!(editor.selection_start, None);
1885 assert_eq!(editor.selection_end, None);
1886 }
1887
1888 #[test]
1889 fn test_delete_key_without_selection() {
1890 let mut editor = CodeEditor::new("hello", "py");
1891 editor.cursor = (0, 0);
1892
1893 let _ = editor.update(&Message::Delete);
1894
1895 assert_eq!(editor.buffer.line(0), "ello");
1897 assert_eq!(editor.cursor, (0, 0));
1898 }
1899
1900 #[test]
1901 fn test_backspace_with_selection() {
1902 let mut editor = CodeEditor::new("hello world", "py");
1903 editor.selection_start = Some((0, 6));
1904 editor.selection_end = Some((0, 11));
1905 editor.cursor = (0, 11);
1906
1907 let _ = editor.update(&Message::Backspace);
1908
1909 assert_eq!(editor.buffer.line(0), "hello ");
1910 assert_eq!(editor.cursor, (0, 6));
1911 assert_eq!(editor.selection_start, None);
1912 assert_eq!(editor.selection_end, None);
1913 }
1914
1915 #[test]
1916 fn test_backspace_without_selection() {
1917 let mut editor = CodeEditor::new("hello", "py");
1918 editor.cursor = (0, 5);
1919
1920 let _ = editor.update(&Message::Backspace);
1921
1922 assert_eq!(editor.buffer.line(0), "hell");
1924 assert_eq!(editor.cursor, (0, 4));
1925 }
1926
1927 #[test]
1928 fn test_delete_multiline_selection() {
1929 let mut editor = CodeEditor::new("line1\nline2\nline3", "py");
1930 editor.selection_start = Some((0, 2));
1931 editor.selection_end = Some((2, 2));
1932 editor.cursor = (2, 2);
1933
1934 let _ = editor.update(&Message::Delete);
1935
1936 assert_eq!(editor.buffer.line(0), "line3");
1937 assert_eq!(editor.cursor, (0, 2));
1938 assert_eq!(editor.selection_start, None);
1939 }
1940
1941 #[test]
1942 fn test_canvas_focus_gained() {
1943 let mut editor = CodeEditor::new("hello world", "py");
1944 assert!(!editor.has_canvas_focus);
1945 assert!(!editor.show_cursor);
1946
1947 let _ = editor.update(&Message::CanvasFocusGained);
1948
1949 assert!(editor.has_canvas_focus);
1950 assert!(editor.show_cursor);
1951 }
1952
1953 #[test]
1954 fn test_mouse_click_gains_focus() {
1955 let mut editor = CodeEditor::new("hello world", "py");
1956 editor.has_canvas_focus = false;
1957 editor.show_cursor = false;
1958
1959 let _ =
1960 editor.update(&Message::MouseClick(iced::Point::new(100.0, 10.0)));
1961
1962 assert!(editor.has_canvas_focus);
1963 assert!(editor.show_cursor);
1964 }
1965}