1#[cfg(not(feature = "std"))]
4use alloc::{
5 string::{String, ToString},
6 vec::Vec,
7};
8
9#[cfg(feature = "swash")]
10use std::cmp;
11
12use core::iter::once;
13use unicode_segmentation::UnicodeSegmentation;
14
15#[cfg(feature = "swash")]
16use crate::Color;
17use crate::{
18 Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem,
19 Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping,
20};
21
22#[derive(Debug, Clone)]
24pub struct Editor<'buffer> {
25 buffer_ref: BufferRef<'buffer>,
26 cursor: Cursor,
27 cursor_x_opt: Option<i32>,
28 selection: Selection,
29 cursor_moved: bool,
30 auto_indent: bool,
31 change: Option<Change>,
32}
33
34fn cursor_glyph_opt(cursor: &Cursor, run: &LayoutRun) -> Option<(usize, f32)> {
35 if cursor.line == run.line_i {
36 for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
37 if cursor.index == glyph.start {
38 return Some((glyph_i, 0.0));
39 } else if cursor.index > glyph.start && cursor.index < glyph.end {
40 let mut before = 0;
42 let mut total = 0;
43
44 let cluster = &run.text[glyph.start..glyph.end];
45 for (i, _) in cluster.grapheme_indices(true) {
46 if glyph.start + i < cursor.index {
47 before += 1;
48 }
49 total += 1;
50 }
51
52 let offset = glyph.w * (before as f32) / (total as f32);
53 return Some((glyph_i, offset));
54 }
55 }
56 match run.glyphs.last() {
57 Some(glyph) => {
58 if cursor.index == glyph.end {
59 return Some((run.glyphs.len(), 0.0));
60 }
61 }
62 None => {
63 return Some((0, 0.0));
64 }
65 }
66 }
67 None
68}
69
70fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> {
71 let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?;
72 let x = run.glyphs.get(cursor_glyph).map_or_else(
73 || {
74 run.glyphs.last().map_or(0, |glyph| {
75 if glyph.level.is_rtl() {
76 glyph.x as i32
77 } else {
78 (glyph.x + glyph.w) as i32
79 }
80 })
81 },
82 |glyph| {
83 if glyph.level.is_rtl() {
84 (glyph.x + glyph.w - cursor_glyph_offset) as i32
85 } else {
86 (glyph.x + cursor_glyph_offset) as i32
87 }
88 },
89 );
90
91 Some((x, run.line_top as i32))
92}
93
94impl<'buffer> Editor<'buffer> {
95 pub fn new(buffer: impl Into<BufferRef<'buffer>>) -> Self {
97 Self {
98 buffer_ref: buffer.into(),
99 cursor: Cursor::default(),
100 cursor_x_opt: None,
101 selection: Selection::None,
102 cursor_moved: false,
103 auto_indent: false,
104 change: None,
105 }
106 }
107
108 #[cfg(feature = "swash")]
110 #[allow(clippy::too_many_arguments)]
111 pub fn draw<F>(
112 &self,
113 font_system: &mut FontSystem,
114 cache: &mut crate::SwashCache,
115 text_color: Color,
116 cursor_color: Color,
117 selection_color: Color,
118 selected_text_color: Color,
119 mut f: F,
120 ) where
121 F: FnMut(i32, i32, u32, u32, Color),
122 {
123 let selection_bounds = self.selection_bounds();
124 self.with_buffer(|buffer| {
125 for run in buffer.layout_runs() {
126 let line_i = run.line_i;
127 let line_y = run.line_y;
128 let line_top = run.line_top;
129 let line_height = run.line_height;
130
131 if let Some((start, end)) = selection_bounds {
133 if line_i >= start.line && line_i <= end.line {
134 let mut range_opt = None;
135 for glyph in run.glyphs {
136 let cluster = &run.text[glyph.start..glyph.end];
138 let total = cluster.grapheme_indices(true).count();
139 let mut c_x = glyph.x;
140 let c_w = glyph.w / total as f32;
141 for (i, c) in cluster.grapheme_indices(true) {
142 let c_start = glyph.start + i;
143 let c_end = glyph.start + i + c.len();
144 if (start.line != line_i || c_end > start.index)
145 && (end.line != line_i || c_start < end.index)
146 {
147 range_opt = match range_opt.take() {
148 Some((min, max)) => Some((
149 cmp::min(min, c_x as i32),
150 cmp::max(max, (c_x + c_w) as i32),
151 )),
152 None => Some((c_x as i32, (c_x + c_w) as i32)),
153 };
154 } else if let Some((min, max)) = range_opt.take() {
155 f(
156 min,
157 line_top as i32,
158 cmp::max(0, max - min) as u32,
159 line_height as u32,
160 selection_color,
161 );
162 }
163 c_x += c_w;
164 }
165 }
166
167 if run.glyphs.is_empty() && end.line > line_i {
168 range_opt = Some((0, buffer.size().0.unwrap_or(0.0) as i32));
170 }
171
172 if let Some((mut min, mut max)) = range_opt.take() {
173 if end.line > line_i {
174 if run.rtl {
176 min = 0;
177 } else {
178 max = buffer.size().0.unwrap_or(0.0) as i32;
179 }
180 }
181 f(
182 min,
183 line_top as i32,
184 cmp::max(0, max - min) as u32,
185 line_height as u32,
186 selection_color,
187 );
188 }
189 }
190 }
191
192 if let Some((x, y)) = cursor_position(&self.cursor, &run) {
194 f(x, y, 1, line_height as u32, cursor_color);
195 }
196
197 for glyph in run.glyphs {
198 let physical_glyph = glyph.physical((0., 0.), 1.0);
199
200 let mut glyph_color = glyph.color_opt.map_or(text_color, |some| some);
201 if text_color != selected_text_color {
202 if let Some((start, end)) = selection_bounds {
203 if line_i >= start.line
204 && line_i <= end.line
205 && (start.line != line_i || glyph.end > start.index)
206 && (end.line != line_i || glyph.start < end.index)
207 {
208 glyph_color = selected_text_color;
209 }
210 }
211 }
212
213 cache.with_pixels(
214 font_system,
215 physical_glyph.cache_key,
216 glyph_color,
217 |x, y, color| {
218 f(
219 physical_glyph.x + x,
220 line_y as i32 + physical_glyph.y + y,
221 1,
222 1,
223 color,
224 );
225 },
226 );
227 }
228 }
229 });
230 }
231}
232
233impl<'buffer> Edit<'buffer> for Editor<'buffer> {
234 fn buffer_ref(&self) -> &BufferRef<'buffer> {
235 &self.buffer_ref
236 }
237
238 fn buffer_ref_mut(&mut self) -> &mut BufferRef<'buffer> {
239 &mut self.buffer_ref
240 }
241
242 fn cursor(&self) -> Cursor {
243 self.cursor
244 }
245
246 fn set_cursor(&mut self, cursor: Cursor) {
247 if self.cursor != cursor {
248 self.cursor = cursor;
249 self.cursor_moved = true;
250 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
251 }
252 }
253
254 fn selection(&self) -> Selection {
255 self.selection
256 }
257
258 fn set_selection(&mut self, selection: Selection) {
259 if self.selection != selection {
260 self.selection = selection;
261 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
262 }
263 }
264
265 fn auto_indent(&self) -> bool {
266 self.auto_indent
267 }
268
269 fn set_auto_indent(&mut self, auto_indent: bool) {
270 self.auto_indent = auto_indent;
271 }
272
273 fn tab_width(&self) -> u16 {
274 self.with_buffer(super::super::buffer::Buffer::tab_width)
275 }
276
277 fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
278 self.with_buffer_mut(|buffer| buffer.set_tab_width(font_system, tab_width));
279 }
280
281 fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
282 if self.cursor_moved {
283 let cursor = self.cursor;
284 self.with_buffer_mut(|buffer| buffer.shape_until_cursor(font_system, cursor, prune));
285 self.cursor_moved = false;
286 } else {
287 self.with_buffer_mut(|buffer| buffer.shape_until_scroll(font_system, prune));
288 }
289 }
290
291 fn delete_range(&mut self, start: Cursor, end: Cursor) {
292 let change_item = self.with_buffer_mut(|buffer| {
293 let mut change_lines = Vec::new();
295
296 let end_line_opt = if end.line > start.line {
298 let after = buffer.lines[end.line].split_off(end.index);
300
301 let removed = buffer.lines.remove(end.line);
303 change_lines.insert(0, removed.text().to_string());
304
305 Some(after)
306 } else {
307 None
308 };
309
310 for line_i in (start.line + 1..end.line).rev() {
312 let removed = buffer.lines.remove(line_i);
313 change_lines.insert(0, removed.text().to_string());
314 }
315
316 {
318 let after_opt = if start.line == end.line {
320 Some(buffer.lines[start.line].split_off(end.index))
321 } else {
322 None
323 };
324
325 let removed = buffer.lines[start.line].split_off(start.index);
327 change_lines.insert(0, removed.text().to_string());
328
329 if let Some(after) = after_opt {
331 buffer.lines[start.line].append(&after);
332 }
333
334 if let Some(end_line) = end_line_opt {
336 buffer.lines[start.line].append(&end_line);
337 }
338 }
339
340 ChangeItem {
341 start,
342 end,
343 text: change_lines.join("\n"),
344 insert: false,
345 }
346 });
347
348 if let Some(ref mut change) = self.change {
349 change.items.push(change_item);
350 }
351 }
352
353 fn insert_at(
354 &mut self,
355 mut cursor: Cursor,
356 data: &str,
357 attrs_list: Option<AttrsList>,
358 ) -> Cursor {
359 let mut remaining_split_len = data.len();
360 if remaining_split_len == 0 {
361 return cursor;
362 }
363
364 let change_item = self.with_buffer_mut(|buffer| {
365 let start = cursor;
367
368 while cursor.line >= buffer.lines.len() {
370 let ending = buffer
371 .lines
372 .last()
373 .map(super::super::buffer_line::BufferLine::ending)
374 .unwrap_or_default();
375 let line = BufferLine::new(
376 String::new(),
377 ending,
378 AttrsList::new(&attrs_list.as_ref().map_or_else(
379 || {
380 buffer
381 .lines
382 .last()
383 .map_or_else(Attrs::new, |line| line.attrs_list().defaults())
384 },
385 |x| x.defaults(),
386 )),
387 Shaping::Advanced,
388 );
389 buffer.lines.push(line);
390 }
391
392 let line: &mut BufferLine = &mut buffer.lines[cursor.line];
393 let insert_line = cursor.line + 1;
394 let ending = line.ending();
395
396 let after: BufferLine = line.split_off(cursor.index);
398 let after_len = after.text().len();
399
400 let mut final_attrs = attrs_list.unwrap_or_else(|| {
402 AttrsList::new(&line.attrs_list().get_span(cursor.index.saturating_sub(1)))
403 });
404
405 let addendum = once("").filter(|_| data.ends_with('\n'));
409 let mut lines_iter = data.split_inclusive('\n').chain(addendum);
410 if let Some(data_line) = lines_iter.next() {
411 let mut these_attrs = final_attrs.split_off(data_line.len());
412 remaining_split_len -= data_line.len();
413 core::mem::swap(&mut these_attrs, &mut final_attrs);
414 line.append(&BufferLine::new(
415 data_line
416 .strip_suffix(char::is_control)
417 .unwrap_or(data_line),
418 ending,
419 these_attrs,
420 Shaping::Advanced,
421 ));
422 } else {
423 panic!("str::lines() did not yield any elements");
424 }
425 if let Some(data_line) = lines_iter.next_back() {
426 remaining_split_len -= data_line.len();
427 let mut tmp = BufferLine::new(
428 data_line
429 .strip_suffix(char::is_control)
430 .unwrap_or(data_line),
431 ending,
432 final_attrs.split_off(remaining_split_len),
433 Shaping::Advanced,
434 );
435 tmp.append(&after);
436 buffer.lines.insert(insert_line, tmp);
437 cursor.line += 1;
438 } else {
439 line.append(&after);
440 }
441 for data_line in lines_iter.rev() {
442 remaining_split_len -= data_line.len();
443 let tmp = BufferLine::new(
444 data_line
445 .strip_suffix(char::is_control)
446 .unwrap_or(data_line),
447 ending,
448 final_attrs.split_off(remaining_split_len),
449 Shaping::Advanced,
450 );
451 buffer.lines.insert(insert_line, tmp);
452 cursor.line += 1;
453 }
454
455 assert_eq!(remaining_split_len, 0);
456
457 cursor.index = buffer.lines[cursor.line].text().len() - after_len;
459
460 ChangeItem {
461 start,
462 end: cursor,
463 text: data.to_string(),
464 insert: true,
465 }
466 });
467
468 if let Some(ref mut change) = self.change {
469 change.items.push(change_item);
470 }
471
472 cursor
473 }
474
475 fn copy_selection(&self) -> Option<String> {
476 let (start, end) = self.selection_bounds()?;
477 self.with_buffer(|buffer| {
478 let mut selection = String::new();
479 {
481 if start.line == end.line {
483 selection.push_str(&buffer.lines[start.line].text()[start.index..end.index]);
484 } else {
485 selection.push_str(&buffer.lines[start.line].text()[start.index..]);
486 selection.push('\n');
487 }
488 }
489
490 for line_i in start.line + 1..end.line {
492 selection.push_str(buffer.lines[line_i].text());
493 selection.push('\n');
494 }
495
496 if end.line > start.line {
498 selection.push_str(&buffer.lines[end.line].text()[..end.index]);
500 }
501
502 Some(selection)
503 })
504 }
505
506 fn delete_selection(&mut self) -> bool {
507 let Some((start, end)) = self.selection_bounds() else {
508 return false;
509 };
510
511 self.cursor = start;
513
514 self.selection = Selection::None;
516
517 self.delete_range(start, end);
519
520 true
521 }
522
523 fn apply_change(&mut self, change: &Change) -> bool {
524 if let Some(pending) = self.change.take() {
526 if !pending.items.is_empty() {
527 log::warn!("pending change caused apply_change to be ignored!");
529 self.change = Some(pending);
530 return false;
531 }
532 }
533
534 for item in &change.items {
535 if item.insert {
537 self.cursor = self.insert_at(item.start, &item.text, None);
538 } else {
539 self.cursor = item.start;
540 self.delete_range(item.start, item.end);
541 }
542 }
543 true
544 }
545
546 fn start_change(&mut self) {
547 if self.change.is_none() {
548 self.change = Some(Change::default());
549 }
550 }
551
552 fn finish_change(&mut self) -> Option<Change> {
553 self.change.take()
554 }
555
556 fn action(&mut self, font_system: &mut FontSystem, action: Action) {
557 let old_cursor = self.cursor;
558
559 match action {
560 Action::Motion(motion) => {
561 let cursor = self.cursor;
562 let cursor_x_opt = self.cursor_x_opt;
563 if let Some((new_cursor, new_cursor_x_opt)) = self.with_buffer_mut(|buffer| {
564 buffer.cursor_motion(font_system, cursor, cursor_x_opt, motion)
565 }) {
566 self.cursor = new_cursor;
567 self.cursor_x_opt = new_cursor_x_opt;
568 }
569 }
570 Action::Escape => {
571 match self.selection {
572 Selection::None => {}
573 _ => self.with_buffer_mut(|buffer| buffer.set_redraw(true)),
574 }
575 self.selection = Selection::None;
576 }
577 Action::Insert(character) => {
578 if character.is_control() && !['\t', '\n', '\u{92}'].contains(&character) {
579 log::debug!("Refusing to insert control character {character:?}");
581 } else if character == '\n' {
582 self.action(font_system, Action::Enter);
583 } else {
584 let mut str_buf = [0u8; 8];
585 let str_ref = character.encode_utf8(&mut str_buf);
586 self.insert_string(str_ref, None);
587 }
588 }
589 Action::Enter => {
590 if self.auto_indent {
592 let mut string = String::from("\n");
593 self.with_buffer(|buffer| {
594 let line = &buffer.lines[self.cursor.line];
595 let text = line.text();
596 for c in text.chars() {
597 if c.is_whitespace() {
598 string.push(c);
599 } else {
600 break;
601 }
602 }
603 });
604 self.insert_string(&string, None);
605 } else {
606 self.insert_string("\n", None);
607 }
608
609 let line_i = self.cursor.line;
611 self.with_buffer_mut(|buffer| {
612 buffer.line_layout(font_system, line_i);
613 });
614 }
615 Action::Backspace => {
616 if self.delete_selection() {
617 } else {
619 let end = self.cursor;
621
622 if self.cursor.index > 0 {
623 self.cursor.index = self.with_buffer(|buffer| {
625 buffer.lines[self.cursor.line].text()[..self.cursor.index]
626 .char_indices()
627 .next_back()
628 .map_or(0, |(i, _)| i)
629 });
630 } else if self.cursor.line > 0 {
631 self.cursor.line -= 1;
633 self.cursor.index =
634 self.with_buffer(|buffer| buffer.lines[self.cursor.line].text().len());
635 }
636
637 if self.cursor != end {
638 self.delete_range(self.cursor, end);
640 }
641 }
642 }
643 Action::Delete => {
644 if self.delete_selection() {
645 } else {
647 let mut start = self.cursor;
649 let mut end = self.cursor;
650
651 self.with_buffer(|buffer| {
652 if start.index < buffer.lines[start.line].text().len() {
653 let line = &buffer.lines[start.line];
654
655 let range_opt = line
656 .text()
657 .grapheme_indices(true)
658 .take_while(|(i, _)| *i <= start.index)
659 .last()
660 .map(|(i, c)| i..(i + c.len()));
661
662 if let Some(range) = range_opt {
663 start.index = range.start;
664 end.index = range.end;
665 }
666 } else if start.line + 1 < buffer.lines.len() {
667 end.line += 1;
668 end.index = 0;
669 }
670 });
671
672 if start != end {
673 self.cursor = start;
674 self.delete_range(start, end);
675 }
676 }
677 }
678 Action::Indent => {
679 let (start, end) = match self.selection_bounds() {
681 Some(some) => some,
682 None => (self.cursor, self.cursor),
683 };
684
685 let tab_width: usize = self.tab_width().into();
687 for line_i in start.line..=end.line {
688 let mut after_whitespace = 0;
690 let mut required_indent = 0;
691 self.with_buffer(|buffer| {
692 let line = &buffer.lines[line_i];
693 let text = line.text();
694
695 if self.selection == Selection::None {
696 let whitespace_length = match line.text()[0..self.cursor.index]
698 .chars()
699 .rev()
700 .position(|c| !c.is_whitespace())
701 {
702 Some(length) => length,
703 None => self.cursor.index,
705 };
706 required_indent = tab_width - (whitespace_length % tab_width);
707 after_whitespace = self.cursor.index;
708 } else {
709 for (count, (index, c)) in text.char_indices().enumerate() {
711 if !c.is_whitespace() {
712 after_whitespace = index;
713 required_indent = tab_width - (count % tab_width);
714 break;
715 }
716 }
717 }
718 });
719
720 self.insert_at(
721 Cursor::new(line_i, after_whitespace),
722 &" ".repeat(required_indent),
723 None,
724 );
725
726 if self.cursor.line == line_i {
728 if self.cursor.index < after_whitespace {
730 self.cursor.index = after_whitespace;
731 }
732 self.cursor.index += required_indent;
733 }
734
735 match self.selection {
737 Selection::None => {}
738 Selection::Normal(ref mut select)
739 | Selection::Line(ref mut select)
740 | Selection::Word(ref mut select) => {
741 if select.line == line_i && select.index >= after_whitespace {
742 select.index += required_indent;
743 }
744 }
745 }
746 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
748 }
749 }
750 Action::Unindent => {
751 let (start, end) = match self.selection_bounds() {
753 Some(some) => some,
754 None => (self.cursor, self.cursor),
755 };
756
757 let tab_width: usize = self.tab_width().into();
759 for line_i in start.line..=end.line {
760 let mut last_indent = 0;
762 let mut after_whitespace = 0;
763 self.with_buffer(|buffer| {
764 let line = &buffer.lines[line_i];
765 let text = line.text();
766 after_whitespace = text.len();
768 for (count, (index, c)) in text.char_indices().enumerate() {
769 if !c.is_whitespace() {
770 after_whitespace = index;
771 break;
772 }
773 if count % tab_width == 0 {
774 last_indent = index;
775 }
776 }
777 });
778
779 if last_indent == after_whitespace {
781 continue;
782 }
783
784 self.delete_range(
786 Cursor::new(line_i, last_indent),
787 Cursor::new(line_i, after_whitespace),
788 );
789
790 if self.cursor.line == line_i && self.cursor.index > last_indent {
792 self.cursor.index -= after_whitespace - last_indent;
793 }
794
795 match self.selection {
797 Selection::None => {}
798 Selection::Normal(ref mut select)
799 | Selection::Line(ref mut select)
800 | Selection::Word(ref mut select) => {
801 if select.line == line_i && select.index > last_indent {
802 select.index -= after_whitespace - last_indent;
803 }
804 }
805 }
806
807 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
809 }
810 }
811 Action::Click { x, y } => {
812 self.set_selection(Selection::None);
813
814 if let Some(new_cursor) = self.with_buffer(|buffer| buffer.hit(x as f32, y as f32))
815 {
816 if new_cursor != self.cursor {
817 self.cursor = new_cursor;
818 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
819 }
820 }
821 }
822 Action::DoubleClick { x, y } => {
823 self.set_selection(Selection::None);
824
825 if let Some(new_cursor) = self.with_buffer(|buffer| buffer.hit(x as f32, y as f32))
826 {
827 if new_cursor != self.cursor {
828 self.cursor = new_cursor;
829 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
830 }
831 self.selection = Selection::Word(self.cursor);
832 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
833 }
834 }
835 Action::TripleClick { x, y } => {
836 self.set_selection(Selection::None);
837
838 if let Some(new_cursor) = self.with_buffer(|buffer| buffer.hit(x as f32, y as f32))
839 {
840 if new_cursor != self.cursor {
841 self.cursor = new_cursor;
842 }
843 self.selection = Selection::Line(self.cursor);
844 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
845 }
846 }
847 Action::Drag { x, y } => {
848 if self.selection == Selection::None {
849 self.selection = Selection::Normal(self.cursor);
850 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
851 }
852
853 if let Some(new_cursor) = self.with_buffer(|buffer| buffer.hit(x as f32, y as f32))
854 {
855 if new_cursor != self.cursor {
856 self.cursor = new_cursor;
857 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
858 }
859 }
860 }
861 Action::Scroll { lines } => {
862 self.with_buffer_mut(|buffer| {
863 let mut scroll = buffer.scroll();
864 scroll.vertical += lines as f32 * buffer.metrics().line_height;
866 buffer.set_scroll(scroll);
867 });
868 }
869 }
870
871 if old_cursor != self.cursor {
872 self.cursor_moved = true;
873 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
874
875 }
891 }
892
893 fn cursor_position(&self) -> Option<(i32, i32)> {
894 self.with_buffer(|buffer| {
895 buffer
896 .layout_runs()
897 .find_map(|run| cursor_position(&self.cursor, &run))
898 })
899 }
900}
901
902impl BorrowedWithFontSystem<'_, Editor<'_>> {
903 #[cfg(feature = "swash")]
904 pub fn draw<F>(
905 &mut self,
906 cache: &mut crate::SwashCache,
907 text_color: Color,
908 cursor_color: Color,
909 selection_color: Color,
910 selected_text_color: Color,
911 f: F,
912 ) where
913 F: FnMut(i32, i32, u32, u32, Color),
914 {
915 self.inner.draw(
916 self.font_system,
917 cache,
918 text_color,
919 cursor_color,
920 selection_color,
921 selected_text_color,
922 f,
923 );
924 }
925}