1use std::{borrow::Cow, ops::Range, time::Duration};
21
22use gpui::{
23 App, Bounds, ClipboardItem, Context, CursorStyle, DispatchPhase, ElementId,
24 ElementInputHandler, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable, Global,
25 GlobalElementId, KeyBinding, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
26 MouseUpEvent, PaintQuad, Pixels, Point, SharedString, Style, Task, TextRun, UTF16Selection,
27 UnderlineStyle, Window, WrappedLine, actions, div, fill, prelude::*, px, relative,
28};
29use unicode_segmentation::UnicodeSegmentation as _;
30
31use theme::{HighlightKind, Metrics, SyntaxPalette, TextStyle, Theme};
32
33actions!(
34 bezel_text_field,
35 [
36 Backspace,
37 Delete,
38 Left,
39 Right,
40 SelectLeft,
41 SelectRight,
42 SelectAll,
43 Home,
44 End,
45 SelectHome,
46 SelectEnd,
47 WordLeft,
48 WordRight,
49 SelectWordLeft,
50 SelectWordRight,
51 DeleteWordLeft,
52 DeleteWordRight,
53 DeleteToLineStart,
54 DeleteToLineEnd,
55 ShowCharacterPalette,
56 Paste,
57 Cut,
58 Copy,
59 Up,
60 Down,
61 SelectUp,
62 SelectDown,
63 InsertNewline,
64 Undo,
65 Redo,
66 ]
67);
68
69pub const DEFAULT_UNDO_LIMIT: usize = 10;
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub enum FieldEvent {
87 Changed,
89 Moved,
91}
92
93const BLINK: Duration = Duration::from_millis(500);
95
96struct CaretBlink(bool);
99
100impl Global for CaretBlink {}
101
102pub fn caret_blink(cx: &App) -> bool {
105 cx.try_global::<CaretBlink>().is_none_or(|blink| blink.0)
106}
107
108pub fn set_caret_blink(blink: bool, cx: &mut App) {
111 cx.set_global(CaretBlink(blink));
112 cx.refresh_windows();
113}
114
115const CARET_WIDTH: Pixels = px(2.);
118
119pub const KEY_CONTEXT: &str = "TextField";
121
122pub const MULTILINE_KEY_CONTEXT: &str = "TextArea";
131
132pub fn init(cx: &mut App) {
134 cx.bind_keys(bindings());
135}
136
137pub fn bindings() -> Vec<KeyBinding> {
144 let mut bindings = Vec::new();
145 let ctx = Some(KEY_CONTEXT);
146 bindings.extend([
147 KeyBinding::new("backspace", Backspace, ctx),
149 KeyBinding::new("delete", Delete, ctx),
150 KeyBinding::new("left", Left, ctx),
151 KeyBinding::new("right", Right, ctx),
152 KeyBinding::new("shift-left", SelectLeft, ctx),
153 KeyBinding::new("shift-right", SelectRight, ctx),
154 KeyBinding::new("home", Home, ctx),
155 KeyBinding::new("end", End, ctx),
156 KeyBinding::new("shift-home", SelectHome, ctx),
157 KeyBinding::new("shift-end", SelectEnd, ctx),
158 ]);
159
160 let area = Some(MULTILINE_KEY_CONTEXT);
163 bindings.extend([
164 KeyBinding::new("enter", InsertNewline, area),
165 KeyBinding::new("up", Up, area),
166 KeyBinding::new("down", Down, area),
167 KeyBinding::new("shift-up", SelectUp, area),
168 KeyBinding::new("shift-down", SelectDown, area),
169 ]);
170
171 #[cfg(target_os = "macos")]
172 bindings.extend([
173 KeyBinding::new("cmd-a", SelectAll, ctx),
174 KeyBinding::new("cmd-c", Copy, ctx),
175 KeyBinding::new("cmd-x", Cut, ctx),
176 KeyBinding::new("cmd-v", Paste, ctx),
177 KeyBinding::new("cmd-z", Undo, ctx),
178 KeyBinding::new("cmd-shift-z", Redo, ctx),
179 KeyBinding::new("ctrl-cmd-space", ShowCharacterPalette, ctx),
180 KeyBinding::new("cmd-left", Home, ctx),
182 KeyBinding::new("cmd-right", End, ctx),
183 KeyBinding::new("cmd-shift-left", SelectHome, ctx),
184 KeyBinding::new("cmd-shift-right", SelectEnd, ctx),
185 KeyBinding::new("alt-left", WordLeft, ctx),
186 KeyBinding::new("alt-right", WordRight, ctx),
187 KeyBinding::new("alt-shift-left", SelectWordLeft, ctx),
188 KeyBinding::new("alt-shift-right", SelectWordRight, ctx),
189 KeyBinding::new("cmd-backspace", DeleteToLineStart, ctx),
190 KeyBinding::new("alt-backspace", DeleteWordLeft, ctx),
191 KeyBinding::new("alt-delete", DeleteWordRight, ctx),
192 KeyBinding::new("ctrl-a", Home, ctx),
194 KeyBinding::new("ctrl-e", End, ctx),
195 KeyBinding::new("ctrl-b", Left, ctx),
196 KeyBinding::new("ctrl-f", Right, ctx),
197 KeyBinding::new("ctrl-h", Backspace, ctx),
198 KeyBinding::new("ctrl-d", Delete, ctx),
199 KeyBinding::new("ctrl-k", DeleteToLineEnd, ctx),
200 ]);
201
202 #[cfg(target_os = "macos")]
205 bindings.extend([
206 KeyBinding::new("ctrl-n", Down, area),
207 KeyBinding::new("ctrl-p", Up, area),
208 ]);
209
210 #[cfg(not(target_os = "macos"))]
211 bindings.extend([
212 KeyBinding::new("ctrl-a", SelectAll, ctx),
213 KeyBinding::new("ctrl-c", Copy, ctx),
214 KeyBinding::new("ctrl-x", Cut, ctx),
215 KeyBinding::new("ctrl-v", Paste, ctx),
216 KeyBinding::new("ctrl-left", WordLeft, ctx),
218 KeyBinding::new("ctrl-right", WordRight, ctx),
219 KeyBinding::new("ctrl-shift-left", SelectWordLeft, ctx),
220 KeyBinding::new("ctrl-shift-right", SelectWordRight, ctx),
221 KeyBinding::new("ctrl-backspace", DeleteWordLeft, ctx),
222 KeyBinding::new("ctrl-delete", DeleteWordRight, ctx),
223 KeyBinding::new("ctrl-z", Undo, ctx),
224 KeyBinding::new("ctrl-shift-z", Redo, ctx),
225 KeyBinding::new("ctrl-y", Redo, ctx),
226 ]);
227
228 bindings
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
236pub enum Case {
237 #[default]
239 Mixed,
240 Upper,
242}
243
244impl Case {
245 fn apply(self, text: &str) -> Cow<'_, str> {
248 match self {
249 Self::Mixed => Cow::Borrowed(text),
250 Self::Upper => Cow::Owned(text.to_uppercase()),
251 }
252 }
253}
254
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
262pub enum Shape {
263 #[default]
266 Line,
267 Rows(usize),
269 Grow { min: usize, max: usize },
272}
273
274impl Shape {
275 fn is_multiline(self) -> bool {
278 !matches!(self, Self::Line)
279 }
280}
281
282#[derive(Clone)]
288struct Snapshot {
289 content: SharedString,
290 selection: Range<usize>,
291 reversed: bool,
292}
293
294#[derive(Clone, Copy, PartialEq, Eq)]
297pub enum EditKind {
298 Insert,
299 Delete,
300}
301
302pub struct TextField {
305 focus_handle: FocusHandle,
306 content: SharedString,
307 placeholder: SharedString,
308 shape: Shape,
309 case: Case,
311 selected_range: Range<usize>,
312 selection_reversed: bool,
313 marked_range: Option<Range<usize>>,
315 last_layout: Vec<WrappedLine>,
318 last_bounds: Option<Bounds<Pixels>>,
319 is_selecting: bool,
320 goal_x: Option<Pixels>,
327 scroll: Point<Pixels>,
335 history: crate::history::SnapshotHistory<Snapshot>,
336 last_edit: Option<(EditKind, usize)>,
340 key_context: Option<SharedString>,
343 frame: bool,
347 metrics: Metrics,
350 caret_on: bool,
352 blink: Option<Task<()>>,
354 follow_caret: bool,
361 spans: Vec<(Range<usize>, HighlightKind)>,
364}
365
366impl EventEmitter<FieldEvent> for TextField {}
367
368impl TextField {
369 pub fn new(cx: &mut Context<Self>) -> Self {
370 Self {
371 focus_handle: cx.focus_handle().tab_stop(true),
374 frame: true,
375 content: "".into(),
376 placeholder: "".into(),
377 shape: Shape::Line,
378 case: Case::default(),
379 selected_range: 0..0,
380 selection_reversed: false,
381 marked_range: None,
382 last_layout: Vec::new(),
383 last_bounds: None,
384 is_selecting: false,
385 goal_x: None,
386 scroll: Point::default(),
387 history: crate::history::SnapshotHistory::new(DEFAULT_UNDO_LIMIT),
388 last_edit: None,
389 key_context: None,
390 metrics: TextStyle::Body.into(),
391 caret_on: true,
392 blink: None,
393 follow_caret: false,
394 spans: Vec::new(),
395 }
396 }
397
398 pub fn with_undo_limit(mut self, limit: usize) -> Self {
403 self.history.set_limit(limit);
404 self
405 }
406
407 pub fn with_placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
408 self.placeholder = placeholder.into();
409 self
410 }
411
412 pub fn with_key_context(mut self, context: impl Into<SharedString>) -> Self {
438 self.key_context = Some(context.into());
439 self
440 }
441
442 pub fn with_frame(mut self, frame: bool) -> Self {
445 self.frame = frame;
446 self
447 }
448
449 pub fn with_metrics(mut self, metrics: Metrics) -> Self {
453 self.metrics = metrics;
454 self
455 }
456
457 pub fn set_metrics(&mut self, metrics: Metrics, cx: &mut Context<Self>) {
459 if self.metrics != metrics {
460 self.metrics = metrics;
461 cx.notify();
462 }
463 }
464
465 pub fn with_shape(mut self, shape: Shape) -> Self {
466 self.shape = shape;
467 self
468 }
469
470 pub fn with_case(mut self, case: Case) -> Self {
471 self.case = case;
472 self
473 }
474
475 pub fn set_case(&mut self, case: Case) {
479 self.case = case;
480 }
481
482 pub fn case(&self) -> Case {
483 self.case
484 }
485
486 pub fn shape(&self) -> Shape {
487 self.shape
488 }
489
490 pub fn content(&self) -> &SharedString {
491 &self.content
492 }
493
494 pub fn spans(&self) -> &[(Range<usize>, HighlightKind)] {
496 &self.spans
497 }
498
499 pub fn set_content(&mut self, content: impl Into<SharedString>, cx: &mut Context<Self>) {
501 let normalized = normalize(&content.into(), self.shape);
502 self.content = self.case.apply(&normalized).into_owned().into();
503 self.spans.clear();
505 self.history.clear();
508 self.last_edit = None;
509 let end = self.content.len();
510 self.selected_range = end..end;
511 self.marked_range = None;
512 cx.emit(FieldEvent::Changed);
513 cx.notify();
514 }
515
516 pub fn clear(&mut self, cx: &mut Context<Self>) {
517 self.set_content("", cx);
518 }
519
520 pub fn set_spans(&mut self, spans: Vec<(Range<usize>, HighlightKind)>, cx: &mut Context<Self>) {
534 self.spans = spans;
535 cx.notify();
536 }
537
538 pub fn set_placeholder(
542 &mut self,
543 placeholder: impl Into<SharedString>,
544 cx: &mut Context<Self>,
545 ) {
546 self.placeholder = placeholder.into();
547 cx.notify();
548 }
549
550 fn caret_moved(&mut self) {
554 self.follow_caret = true;
555 self.blink = None;
556 }
557
558 fn start_blink(&mut self, cx: &mut Context<Self>) {
560 self.caret_on = true;
561 self.blink = Some(cx.spawn(async move |field, cx| {
562 loop {
563 cx.background_executor().timer(BLINK).await;
564 let flipped = field.update(cx, |field, cx| {
565 field.caret_on = !field.caret_on;
566 cx.notify();
567 });
568 if flipped.is_err() {
569 break;
570 }
571 }
572 }));
573 }
574
575 pub fn cursor(&self) -> usize {
581 self.cursor_offset()
582 }
583
584 pub fn offset_bounds(&self, offset: usize) -> Option<Bounds<Pixels>> {
595 self.row_bounds(self.text_origin()?, offset..offset, self.line_height())
596 }
597
598 fn row_bounds(
605 &self,
606 origin: Point<Pixels>,
607 range: Range<usize>,
608 line_height: Pixels,
609 ) -> Option<Bounds<Pixels>> {
610 let start = position_for_offset(&self.last_layout, range.start, line_height)?;
611 let end = position_for_offset(&self.last_layout, range.end, line_height)?;
612 Some(Bounds::from_corners(
613 origin + start,
614 origin + gpui::point(end.x, end.y + line_height),
615 ))
616 }
617
618 fn left(&mut self, _: &Left, _: &mut Window, cx: &mut Context<Self>) {
619 if self.selected_range.is_empty() {
620 self.move_to(self.previous_boundary(self.cursor_offset()), cx);
621 } else {
622 self.move_to(self.selected_range.start, cx)
623 }
624 }
625
626 fn right(&mut self, _: &Right, _: &mut Window, cx: &mut Context<Self>) {
627 if self.selected_range.is_empty() {
628 self.move_to(self.next_boundary(self.selected_range.end), cx);
629 } else {
630 self.move_to(self.selected_range.end, cx)
631 }
632 }
633
634 fn select_left(&mut self, _: &SelectLeft, _: &mut Window, cx: &mut Context<Self>) {
635 self.select_to(self.previous_boundary(self.cursor_offset()), cx);
636 }
637
638 fn select_right(&mut self, _: &SelectRight, _: &mut Window, cx: &mut Context<Self>) {
639 self.select_to(self.next_boundary(self.cursor_offset()), cx);
640 }
641
642 fn select_all(&mut self, _: &SelectAll, _: &mut Window, cx: &mut Context<Self>) {
643 self.move_to(0, cx);
644 self.select_to(self.content.len(), cx)
645 }
646
647 fn home(&mut self, _: &Home, _: &mut Window, cx: &mut Context<Self>) {
648 self.move_to(line_start(&self.content, self.cursor_offset()), cx);
649 }
650
651 fn end(&mut self, _: &End, _: &mut Window, cx: &mut Context<Self>) {
652 self.move_to(line_end(&self.content, self.cursor_offset()), cx);
653 }
654
655 fn select_home(&mut self, _: &SelectHome, _: &mut Window, cx: &mut Context<Self>) {
656 self.select_to(line_start(&self.content, self.cursor_offset()), cx);
657 }
658
659 fn select_end(&mut self, _: &SelectEnd, _: &mut Window, cx: &mut Context<Self>) {
660 self.select_to(line_end(&self.content, self.cursor_offset()), cx);
661 }
662
663 fn up(&mut self, _: &Up, _: &mut Window, cx: &mut Context<Self>) {
664 self.vertical(-1, false, cx);
665 }
666
667 fn down(&mut self, _: &Down, _: &mut Window, cx: &mut Context<Self>) {
668 self.vertical(1, false, cx);
669 }
670
671 fn select_up(&mut self, _: &SelectUp, _: &mut Window, cx: &mut Context<Self>) {
672 self.vertical(-1, true, cx);
673 }
674
675 fn select_down(&mut self, _: &SelectDown, _: &mut Window, cx: &mut Context<Self>) {
676 self.vertical(1, true, cx);
677 }
678
679 fn vertical(&mut self, rows: i32, extend: bool, cx: &mut Context<Self>) {
688 if self.last_layout.is_empty() {
689 return;
690 }
691 let line_height = self.line_height();
692 let Some(at) = position_for_offset(&self.last_layout, self.cursor_offset(), line_height)
693 else {
694 return;
695 };
696 let goal = self.goal_x.unwrap_or(at.x);
697 let target = at.y + line_height * rows as f32;
698 let offset = if target < px(0.) {
701 0
702 } else {
703 offset_for_position(&self.last_layout, gpui::point(goal, target), line_height)
704 };
705
706 if extend {
707 self.select_to(offset, cx);
708 } else {
709 self.move_to(offset, cx);
710 }
711 self.goal_x = Some(goal);
713 }
714
715 fn insert_newline(&mut self, _: &InsertNewline, window: &mut Window, cx: &mut Context<Self>) {
718 if self.shape.is_multiline() {
719 self.replace_text_in_range(None, "\n", window, cx);
720 }
721 }
722
723 fn word_left(&mut self, _: &WordLeft, _: &mut Window, cx: &mut Context<Self>) {
724 self.move_to(
725 previous_word_boundary(&self.content, self.cursor_offset()),
726 cx,
727 );
728 }
729
730 fn word_right(&mut self, _: &WordRight, _: &mut Window, cx: &mut Context<Self>) {
731 self.move_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
732 }
733
734 fn select_word_left(&mut self, _: &SelectWordLeft, _: &mut Window, cx: &mut Context<Self>) {
735 self.select_to(
736 previous_word_boundary(&self.content, self.cursor_offset()),
737 cx,
738 );
739 }
740
741 fn select_word_right(&mut self, _: &SelectWordRight, _: &mut Window, cx: &mut Context<Self>) {
742 self.select_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
743 }
744
745 fn delete_word_left(
749 &mut self,
750 _: &DeleteWordLeft,
751 window: &mut Window,
752 cx: &mut Context<Self>,
753 ) {
754 if self.selected_range.is_empty() {
755 self.select_to(
756 previous_word_boundary(&self.content, self.cursor_offset()),
757 cx,
758 );
759 }
760 self.replace_text_in_range(None, "", window, cx)
761 }
762
763 fn delete_word_right(
764 &mut self,
765 _: &DeleteWordRight,
766 window: &mut Window,
767 cx: &mut Context<Self>,
768 ) {
769 if self.selected_range.is_empty() {
770 self.select_to(next_word_boundary(&self.content, self.cursor_offset()), cx);
771 }
772 self.replace_text_in_range(None, "", window, cx)
773 }
774
775 fn delete_to_line_start(
776 &mut self,
777 _: &DeleteToLineStart,
778 window: &mut Window,
779 cx: &mut Context<Self>,
780 ) {
781 if self.selected_range.is_empty() {
782 self.select_to(line_start(&self.content, self.cursor_offset()), cx);
783 }
784 self.replace_text_in_range(None, "", window, cx)
785 }
786
787 fn delete_to_line_end(
788 &mut self,
789 _: &DeleteToLineEnd,
790 window: &mut Window,
791 cx: &mut Context<Self>,
792 ) {
793 if self.selected_range.is_empty() {
794 self.select_to(line_end(&self.content, self.cursor_offset()), cx);
795 }
796 self.replace_text_in_range(None, "", window, cx)
797 }
798
799 fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
800 if self.selected_range.is_empty() {
801 let prev = self.previous_boundary(self.cursor_offset());
802 if self.cursor_offset() == prev {
803 return;
804 }
805 self.select_to(prev, cx)
806 }
807 self.replace_text_in_range(None, "", window, cx)
808 }
809
810 fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
811 if self.selected_range.is_empty() {
812 let next = self.next_boundary(self.cursor_offset());
813 if self.cursor_offset() == next {
814 return;
815 }
816 self.select_to(next, cx)
817 }
818 self.replace_text_in_range(None, "", window, cx)
819 }
820
821 fn on_mouse_down(
822 &mut self,
823 event: &MouseDownEvent,
824 _window: &mut Window,
825 cx: &mut Context<Self>,
826 ) {
827 self.is_selecting = true;
828 let offset = self.index_for_mouse_position(event.position, self.line_height());
829 if event.modifiers.shift {
830 self.select_to(offset, cx);
831 } else {
832 self.move_to(offset, cx)
833 }
834 }
835
836 fn on_mouse_up(&mut self, _: &MouseUpEvent, _: &mut Window, _: &mut Context<Self>) {
837 self.is_selecting = false;
838 }
839
840 fn on_scroll_wheel(
844 &mut self,
845 event: &gpui::ScrollWheelEvent,
846 _window: &mut Window,
847 cx: &mut Context<Self>,
848 ) {
849 let delta = event.delta.pixel_delta(self.line_height());
850 self.scroll.x = (self.scroll.x - delta.x).max(px(0.));
851 self.scroll.y = (self.scroll.y - delta.y).max(px(0.));
852 cx.notify();
853 }
854
855 fn drag_to(&mut self, position: Point<Pixels>, line_height: Pixels, cx: &mut Context<Self>) {
859 if !self.is_selecting {
860 return;
861 }
862 let offset = self.index_for_mouse_position(position, line_height);
863 if offset != self.cursor_offset() {
866 self.select_to(offset, cx);
867 }
868 }
869
870 fn show_character_palette(
871 &mut self,
872 _: &ShowCharacterPalette,
873 window: &mut Window,
874 _: &mut Context<Self>,
875 ) {
876 window.show_character_palette();
877 }
878
879 fn paste(&mut self, _: &Paste, window: &mut Window, cx: &mut Context<Self>) {
880 if let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) {
881 self.replace_text_in_range(None, &normalize(&text, self.shape), window, cx);
882 }
883 }
884
885 fn copy(&mut self, _: &Copy, _: &mut Window, cx: &mut Context<Self>) {
886 if !self.selected_range.is_empty() {
887 cx.write_to_clipboard(ClipboardItem::new_string(
888 self.content[self.selected_range.clone()].to_string(),
889 ));
890 }
891 }
892
893 fn cut(&mut self, _: &Cut, window: &mut Window, cx: &mut Context<Self>) {
894 if !self.selected_range.is_empty() {
895 cx.write_to_clipboard(ClipboardItem::new_string(
896 self.content[self.selected_range.clone()].to_string(),
897 ));
898 self.replace_text_in_range(None, "", window, cx)
899 }
900 }
901
902 fn snapshot(&self) -> Snapshot {
903 Snapshot {
904 content: self.content.clone(),
905 selection: self.selected_range.clone(),
906 reversed: self.selection_reversed,
907 }
908 }
909
910 fn restore(&mut self, point: Snapshot, cx: &mut Context<Self>) {
911 self.content = point.content;
912 self.selected_range = point.selection;
913 self.selection_reversed = point.reversed;
914 self.marked_range = None;
915 self.last_edit = None;
917 self.caret_moved();
918 cx.emit(FieldEvent::Changed);
919 cx.notify();
920 }
921
922 fn push_undo(&mut self, kind: EditKind, at: usize) {
928 let before = (!joins_group(self.last_edit, kind, at)).then(|| self.snapshot());
929 self.history.record(before);
930 }
931
932 fn undo(&mut self, _: &Undo, _: &mut Window, cx: &mut Context<Self>) {
933 let current = self.snapshot();
934 if let Some(point) = self.history.undo(|| current) {
935 self.restore(point, cx);
936 }
937 }
938
939 fn redo(&mut self, _: &Redo, _: &mut Window, cx: &mut Context<Self>) {
940 let current = self.snapshot();
941 if let Some(point) = self.history.redo(|| current) {
942 self.restore(point, cx);
943 }
944 }
945
946 fn move_to(&mut self, offset: usize, cx: &mut Context<Self>) {
947 self.selected_range = offset..offset;
948 self.goal_x = None;
949 self.caret_moved();
950 cx.emit(FieldEvent::Moved);
951 cx.notify()
952 }
953
954 fn cursor_offset(&self) -> usize {
955 if self.selection_reversed {
956 self.selected_range.start
957 } else {
958 self.selected_range.end
959 }
960 }
961
962 fn line_height(&self) -> Pixels {
973 px(self.metrics.line_height())
974 }
975
976 fn text_origin(&self) -> Option<Point<Pixels>> {
979 Some(self.last_bounds?.origin - self.scroll)
980 }
981
982 fn index_for_mouse_position(&self, position: Point<Pixels>, line_height: Pixels) -> usize {
983 if self.content.is_empty() || self.last_layout.is_empty() {
984 return 0;
985 }
986 let Some(origin) = self.text_origin() else {
987 return 0;
988 };
989 offset_for_position(&self.last_layout, position - origin, line_height)
990 }
991
992 fn select_to(&mut self, offset: usize, cx: &mut Context<Self>) {
993 self.goal_x = None;
994 self.caret_moved();
995 if self.selection_reversed {
996 self.selected_range.start = offset
997 } else {
998 self.selected_range.end = offset
999 };
1000 if self.selected_range.end < self.selected_range.start {
1001 self.selection_reversed = !self.selection_reversed;
1002 self.selected_range = self.selected_range.end..self.selected_range.start;
1003 }
1004 cx.emit(FieldEvent::Moved);
1005 cx.notify()
1006 }
1007
1008 fn offset_to_utf16(&self, offset: usize) -> usize {
1009 offset_to_utf16(&self.content, offset)
1010 }
1011
1012 fn range_to_utf16(&self, range: &Range<usize>) -> Range<usize> {
1013 range_to_utf16(&self.content, range.clone())
1014 }
1015
1016 fn range_from_utf16(&self, range_utf16: &Range<usize>) -> Range<usize> {
1017 range_from_utf16(&self.content, range_utf16.clone())
1018 }
1019
1020 fn previous_boundary(&self, offset: usize) -> usize {
1021 previous_boundary(&self.content, offset)
1022 }
1023
1024 fn next_boundary(&self, offset: usize) -> usize {
1025 next_boundary(&self.content, offset)
1026 }
1027}
1028
1029pub fn offset_from_utf16(text: &str, offset: usize) -> usize {
1036 let mut utf8_offset = 0;
1037 let mut utf16_count = 0;
1038 for ch in text.chars() {
1039 if utf16_count >= offset {
1040 break;
1041 }
1042 utf16_count += ch.len_utf16();
1043 utf8_offset += ch.len_utf8();
1044 }
1045 utf8_offset
1046}
1047
1048pub fn offset_to_utf16(text: &str, offset: usize) -> usize {
1050 let mut utf16_offset = 0;
1051 let mut utf8_count = 0;
1052 for ch in text.chars() {
1053 if utf8_count >= offset {
1054 break;
1055 }
1056 utf8_count += ch.len_utf8();
1057 utf16_offset += ch.len_utf16();
1058 }
1059 utf16_offset
1060}
1061
1062pub fn range_from_utf16(text: &str, range: Range<usize>) -> Range<usize> {
1064 offset_from_utf16(text, range.start)..offset_from_utf16(text, range.end)
1065}
1066
1067pub fn range_to_utf16(text: &str, range: Range<usize>) -> Range<usize> {
1069 offset_to_utf16(text, range.start)..offset_to_utf16(text, range.end)
1070}
1071
1072pub fn composition_selection(
1074 text: &str,
1075 start: usize,
1076 selection: Option<Range<usize>>,
1077) -> Range<usize> {
1078 let range = selection
1079 .map(|range| range_from_utf16(text, range))
1080 .unwrap_or(text.len()..text.len());
1081 start + range.start..start + range.end
1082}
1083
1084pub fn previous_boundary(text: &str, offset: usize) -> usize {
1088 text.grapheme_indices(true)
1089 .rev()
1090 .find_map(|(idx, _)| (idx < offset).then_some(idx))
1091 .unwrap_or(0)
1092}
1093
1094pub fn next_boundary(text: &str, offset: usize) -> usize {
1096 text.grapheme_indices(true)
1097 .find_map(|(idx, _)| (idx > offset).then_some(idx))
1098 .unwrap_or(text.len())
1099}
1100
1101pub fn joins_group(last: Option<(EditKind, usize)>, kind: EditKind, at: usize) -> bool {
1109 last.is_some_and(|(last_kind, offset)| last_kind == kind && at == offset)
1110}
1111
1112pub fn normalize(text: &str, shape: Shape) -> String {
1122 let text = text.replace("\r\n", "\n").replace('\r', "\n");
1123 if shape.is_multiline() {
1124 text
1125 } else {
1126 text.replace('\n', " ")
1127 }
1128}
1129
1130pub fn line_start(text: &str, offset: usize) -> usize {
1139 text[..offset].rfind('\n').map_or(0, |at| at + 1)
1140}
1141
1142pub fn line_end(text: &str, offset: usize) -> usize {
1144 text[offset..]
1145 .find('\n')
1146 .map_or(text.len(), |at| offset + at)
1147}
1148
1149fn is_word(segment: &str) -> bool {
1152 segment.chars().any(char::is_alphanumeric)
1153}
1154
1155pub fn previous_word_boundary(text: &str, offset: usize) -> usize {
1163 text.split_word_bound_indices()
1164 .filter(|(start, _)| *start < offset)
1165 .rfind(|(_, segment)| is_word(segment))
1166 .map(|(start, _)| start)
1167 .unwrap_or(0)
1168}
1169
1170pub fn next_word_boundary(text: &str, offset: usize) -> usize {
1172 text.split_word_bound_indices()
1173 .filter(|(start, segment)| start + segment.len() > offset)
1174 .find(|(_, segment)| is_word(segment))
1175 .map(|(start, segment)| start + segment.len())
1176 .unwrap_or(text.len())
1177}
1178
1179fn display_text(field: &TextField) -> (SharedString, bool) {
1182 if field.content.is_empty() {
1183 (field.placeholder.clone(), true)
1184 } else {
1185 (field.content.clone(), false)
1186 }
1187}
1188
1189pub fn coloured(
1204 text: &str,
1205 spans: &[(Range<usize>, HighlightKind)],
1206 base: &TextRun,
1207 palette: &SyntaxPalette,
1208) -> Vec<TextRun> {
1209 if spans.is_empty() {
1210 return vec![TextRun {
1211 len: text.len(),
1212 ..base.clone()
1213 }];
1214 }
1215 let mut runs = Vec::with_capacity(spans.len() * 2 + 1);
1216 let mut at = 0;
1217 for (range, kind) in spans {
1218 if range.start < at
1219 || range.end <= range.start
1220 || range.end > text.len()
1221 || !text.is_char_boundary(range.start)
1222 || !text.is_char_boundary(range.end)
1223 {
1224 continue;
1225 }
1226 if range.start > at {
1227 runs.push(TextRun {
1228 len: range.start - at,
1229 ..base.clone()
1230 });
1231 }
1232 runs.push(TextRun {
1233 len: range.end - range.start,
1234 color: palette.color(*kind),
1235 ..base.clone()
1236 });
1237 at = range.end;
1238 }
1239 if at < text.len() {
1240 runs.push(TextRun {
1241 len: text.len() - at,
1242 ..base.clone()
1243 });
1244 }
1245 runs
1246}
1247
1248pub fn underlined(runs: Vec<TextRun>, marked: &Range<usize>) -> Vec<TextRun> {
1252 let mut out = Vec::with_capacity(runs.len() + 2);
1253 let mut at = 0;
1254 for run in runs {
1255 let end = at + run.len;
1256 for (start, stop, mark) in [
1257 (at, end.min(marked.start), false),
1258 (at.max(marked.start), end.min(marked.end), true),
1259 (at.max(marked.end), end, false),
1260 ] {
1261 if stop <= start {
1262 continue;
1263 }
1264 out.push(TextRun {
1265 len: stop - start,
1266 underline: mark.then(|| UnderlineStyle {
1267 color: Some(run.color),
1268 thickness: px(1.0),
1269 wavy: false,
1270 }),
1271 ..run.clone()
1272 });
1273 }
1274 at = end;
1275 }
1276 out
1277}
1278
1279fn lines_from(lines: &[WrappedLine]) -> impl Iterator<Item = (usize, &WrappedLine)> {
1288 lines.iter().scan(0usize, |start, line| {
1289 let at = *start;
1290 *start = at + line.len() + 1;
1291 Some((at, line))
1292 })
1293}
1294
1295fn rows(lines: &[WrappedLine], line_height: Pixels) -> Vec<(Range<usize>, Pixels)> {
1300 let mut out = Vec::new();
1301 let mut top = px(0.);
1302 for (start, line) in lines_from(lines) {
1303 let mut row_start = start;
1304 for boundary in line.wrap_boundaries() {
1305 let at = start + line.runs()[boundary.run_ix].glyphs[boundary.glyph_ix].index;
1306 out.push((row_start..at, top));
1307 row_start = at;
1308 top += line_height;
1309 }
1310 out.push((row_start..start + line.len(), top));
1311 top += line_height;
1312 }
1313 out
1314}
1315
1316fn position_for_offset(
1318 lines: &[WrappedLine],
1319 offset: usize,
1320 line_height: Pixels,
1321) -> Option<Point<Pixels>> {
1322 let mut top = px(0.);
1323 for (start, line) in lines_from(lines) {
1324 if offset <= start + line.len() {
1325 let local = line.position_for_index(offset.saturating_sub(start), line_height)?;
1326 return Some(gpui::point(local.x, local.y + top));
1327 }
1328 top += line.size(line_height).height;
1329 }
1330 None
1331}
1332
1333fn offset_for_position(
1335 lines: &[WrappedLine],
1336 position: Point<Pixels>,
1337 line_height: Pixels,
1338) -> usize {
1339 let mut top = px(0.);
1340 let mut last = 0;
1341 for (start, line) in lines_from(lines) {
1342 let height = line.size(line_height).height;
1343 last = start + line.len();
1344 if position.y < top + height {
1345 let local = gpui::point(position.x, position.y - top);
1346 let (Ok(index) | Err(index)) = line.closest_index_for_position(local, line_height);
1347 return start + index;
1348 }
1349 top += height;
1350 }
1351 last
1352}
1353
1354fn selection_rows(
1360 lines: &[WrappedLine],
1361 range: &Range<usize>,
1362 line_height: Pixels,
1363) -> Vec<Bounds<Pixels>> {
1364 rows(lines, line_height)
1365 .into_iter()
1366 .filter(|(row, _)| range.start <= row.end && range.end >= row.start)
1367 .filter_map(|(row, top)| {
1368 let left = if range.start <= row.start {
1369 px(0.)
1370 } else {
1371 position_for_offset(lines, range.start, line_height)?.x
1372 };
1373 let right = position_for_offset(lines, range.end.min(row.end), line_height)?.x;
1374 (right > left).then(|| {
1375 Bounds::from_corners(
1376 gpui::point(left, top),
1377 gpui::point(right, top + line_height),
1378 )
1379 })
1380 })
1381 .collect()
1382}
1383
1384impl EntityInputHandler for TextField {
1385 fn text_for_range(
1386 &mut self,
1387 range_utf16: Range<usize>,
1388 actual_range: &mut Option<Range<usize>>,
1389 _window: &mut Window,
1390 _cx: &mut Context<Self>,
1391 ) -> Option<String> {
1392 let range = self.range_from_utf16(&range_utf16);
1393 actual_range.replace(self.range_to_utf16(&range));
1394 Some(self.content[range].to_string())
1395 }
1396
1397 fn selected_text_range(
1398 &mut self,
1399 _ignore_disabled_input: bool,
1400 _window: &mut Window,
1401 _cx: &mut Context<Self>,
1402 ) -> Option<UTF16Selection> {
1403 Some(UTF16Selection {
1404 range: self.range_to_utf16(&self.selected_range),
1405 reversed: self.selection_reversed,
1406 })
1407 }
1408
1409 fn marked_text_range(
1410 &self,
1411 _window: &mut Window,
1412 _cx: &mut Context<Self>,
1413 ) -> Option<Range<usize>> {
1414 self.marked_range
1415 .as_ref()
1416 .map(|range| self.range_to_utf16(range))
1417 }
1418
1419 fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
1420 self.marked_range = None;
1421 }
1422
1423 fn replace_text_in_range(
1424 &mut self,
1425 range_utf16: Option<Range<usize>>,
1426 new_text: &str,
1427 _: &mut Window,
1428 cx: &mut Context<Self>,
1429 ) {
1430 let range = range_utf16
1431 .as_ref()
1432 .map(|range_utf16| self.range_from_utf16(range_utf16))
1433 .or(self.marked_range.clone())
1434 .unwrap_or(self.selected_range.clone());
1435
1436 let kind = if new_text.is_empty() {
1441 EditKind::Delete
1442 } else {
1443 EditKind::Insert
1444 };
1445 self.push_undo(
1448 kind,
1449 if new_text.is_empty() {
1450 range.end
1451 } else {
1452 range.start
1453 },
1454 );
1455
1456 let new_text = self.case.apply(new_text);
1460 self.content =
1461 (self.content[0..range.start].to_owned() + &new_text + &self.content[range.end..])
1462 .into();
1463 self.selected_range = range.start + new_text.len()..range.start + new_text.len();
1464 self.marked_range.take();
1465 self.last_edit = Some((kind, self.selected_range.end));
1466 self.caret_moved();
1467 cx.emit(FieldEvent::Changed);
1468 cx.notify();
1469 }
1470
1471 fn replace_and_mark_text_in_range(
1472 &mut self,
1473 range_utf16: Option<Range<usize>>,
1474 new_text: &str,
1475 new_selected_range_utf16: Option<Range<usize>>,
1476 _window: &mut Window,
1477 cx: &mut Context<Self>,
1478 ) {
1479 let range = range_utf16
1480 .as_ref()
1481 .map(|range_utf16| self.range_from_utf16(range_utf16))
1482 .or(self.marked_range.clone())
1483 .unwrap_or(self.selected_range.clone());
1484
1485 let new_text = self.case.apply(new_text);
1488 self.content =
1489 (self.content[0..range.start].to_owned() + &new_text + &self.content[range.end..])
1490 .into();
1491 self.marked_range =
1492 (!new_text.is_empty()).then(|| range.start..range.start + new_text.len());
1493 self.selected_range =
1494 composition_selection(&new_text, range.start, new_selected_range_utf16);
1495 self.selection_reversed = false;
1496
1497 self.caret_moved();
1498 cx.emit(FieldEvent::Changed);
1499 cx.notify();
1500 }
1501
1502 fn bounds_for_range(
1503 &mut self,
1504 range_utf16: Range<usize>,
1505 bounds: Bounds<Pixels>,
1506 _window: &mut Window,
1507 _cx: &mut Context<Self>,
1508 ) -> Option<Bounds<Pixels>> {
1509 let range = self.range_from_utf16(&range_utf16);
1510 self.row_bounds(bounds.origin - self.scroll, range, self.line_height())
1515 }
1516
1517 fn character_index_for_point(
1518 &mut self,
1519 point: Point<Pixels>,
1520 _window: &mut Window,
1521 _cx: &mut Context<Self>,
1522 ) -> Option<usize> {
1523 self.last_bounds?.localize(&point)?;
1524 let origin = self.text_origin()?;
1525 let offset = offset_for_position(&self.last_layout, point - origin, self.line_height());
1526 Some(self.offset_to_utf16(offset))
1527 }
1528}
1529
1530impl Focusable for TextField {
1531 fn focus_handle(&self, _: &App) -> FocusHandle {
1532 self.focus_handle.clone()
1533 }
1534}
1535
1536impl Render for TextField {
1537 fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
1538 if self.focus_handle.is_focused(_window) && caret_blink(cx) {
1541 if self.blink.is_none() {
1542 self.start_blink(cx);
1543 }
1544 } else {
1545 self.blink = None;
1546 self.caret_on = true;
1547 }
1548 let theme = Theme::of(cx);
1549 let mut key_context = gpui::KeyContext::default();
1550 key_context.add(KEY_CONTEXT);
1551 if self.shape.is_multiline() {
1552 key_context.add(MULTILINE_KEY_CONTEXT);
1553 }
1554 if let Some(extra) = self.key_context.clone() {
1555 key_context.add(extra);
1556 }
1557 div()
1558 .key_context(key_context)
1559 .track_focus(&self.focus_handle(cx))
1560 .cursor(CursorStyle::IBeam)
1561 .on_action(cx.listener(Self::backspace))
1562 .on_action(cx.listener(Self::delete))
1563 .on_action(cx.listener(Self::left))
1564 .on_action(cx.listener(Self::right))
1565 .on_action(cx.listener(Self::select_left))
1566 .on_action(cx.listener(Self::select_right))
1567 .on_action(cx.listener(Self::select_all))
1568 .on_action(cx.listener(Self::home))
1569 .on_action(cx.listener(Self::end))
1570 .on_action(cx.listener(Self::select_home))
1571 .on_action(cx.listener(Self::select_end))
1572 .on_action(cx.listener(Self::word_left))
1573 .on_action(cx.listener(Self::word_right))
1574 .on_action(cx.listener(Self::select_word_left))
1575 .on_action(cx.listener(Self::select_word_right))
1576 .on_action(cx.listener(Self::up))
1577 .on_action(cx.listener(Self::down))
1578 .on_action(cx.listener(Self::select_up))
1579 .on_action(cx.listener(Self::select_down))
1580 .on_action(cx.listener(Self::insert_newline))
1581 .on_action(cx.listener(Self::undo))
1582 .on_action(cx.listener(Self::redo))
1583 .on_action(cx.listener(Self::delete_word_left))
1584 .on_action(cx.listener(Self::delete_word_right))
1585 .on_action(cx.listener(Self::delete_to_line_start))
1586 .on_action(cx.listener(Self::delete_to_line_end))
1587 .on_action(cx.listener(Self::show_character_palette))
1588 .on_action(cx.listener(Self::paste))
1589 .on_action(cx.listener(Self::cut))
1590 .on_action(cx.listener(Self::copy))
1591 .on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
1592 .on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up))
1593 .on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_mouse_up))
1594 .on_scroll_wheel(cx.listener(Self::on_scroll_wheel))
1595 .w_full()
1596 .when(self.frame, |field| {
1597 field
1598 .px(px(10.0))
1599 .py(px(7.0))
1600 .rounded(px(Theme::button_radius()))
1601 .bg(theme.input_bg)
1602 .border_1()
1603 .border_color(if self.focus_handle.is_focused(_window) {
1604 theme.ring
1605 } else {
1606 theme.border
1607 })
1608 })
1609 .text_size(px(self.metrics.size()))
1610 .font_weight(self.metrics.weight)
1611 .line_height(px(self.metrics.line_height()))
1612 .text_color(theme.text)
1613 .child(TextFieldElement { field: cx.entity() })
1614 }
1615}
1616
1617struct TextFieldElement {
1621 field: Entity<TextField>,
1622}
1623
1624struct FieldPrepaint {
1625 lines: Vec<WrappedLine>,
1626 origin: Point<Pixels>,
1628 cursor: Option<PaintQuad>,
1629 selection: Vec<PaintQuad>,
1631}
1632
1633impl IntoElement for TextFieldElement {
1634 type Element = Self;
1635
1636 fn into_element(self) -> Self::Element {
1637 self
1638 }
1639}
1640
1641impl Element for TextFieldElement {
1642 type RequestLayoutState = ();
1643 type PrepaintState = FieldPrepaint;
1644
1645 fn id(&self) -> Option<ElementId> {
1646 None
1647 }
1648
1649 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
1650 None
1651 }
1652
1653 fn request_layout(
1654 &mut self,
1655 _id: Option<&GlobalElementId>,
1656 _inspector_id: Option<&gpui::InspectorElementId>,
1657 window: &mut Window,
1658 cx: &mut App,
1659 ) -> (LayoutId, ()) {
1660 let mut style = Style::default();
1661 style.size.width = relative(1.).into();
1662 let field = self.field.read(cx);
1663 let line_height = field.line_height();
1666 let shape = field.shape;
1667
1668 let (min, max) = match shape {
1669 Shape::Line => {
1670 style.size.height = line_height.into();
1671 return (window.request_layout(style, [], cx), ());
1672 }
1673 Shape::Rows(rows) => {
1674 style.size.height = (line_height * rows.max(1) as f32).into();
1675 return (window.request_layout(style, [], cx), ());
1676 }
1677 Shape::Grow { min, max } => (min.max(1), max.max(min.max(1))),
1681 };
1682
1683 let text = display_text(field).0;
1684 let id = window.request_measured_layout(style, move |known, available, window, _cx| {
1685 let text_style = window.text_style();
1686 let font_size = text_style.font_size.to_pixels(window.rem_size());
1687 let wrap_width = known.width.or(match available.width {
1692 gpui::AvailableSpace::Definite(width) => Some(width),
1693 _ => None,
1694 });
1695 let run = TextRun {
1696 len: text.len(),
1697 font: text_style.font(),
1698 color: text_style.color,
1699 background_color: None,
1700 underline: None,
1701 strikethrough: None,
1702 };
1703 let count = window
1704 .text_system()
1705 .shape_text(text.clone(), font_size, &[run], wrap_width, None)
1706 .map(|lines| {
1707 lines
1708 .iter()
1709 .map(|line| line.wrap_boundaries().len() + 1)
1710 .sum::<usize>()
1711 })
1712 .unwrap_or(1);
1713 gpui::size(
1714 wrap_width.unwrap_or(px(0.)),
1715 line_height * count.clamp(min, max) as f32,
1716 )
1717 });
1718 (id, ())
1719 }
1720
1721 fn prepaint(
1722 &mut self,
1723 _id: Option<&GlobalElementId>,
1724 _inspector_id: Option<&gpui::InspectorElementId>,
1725 bounds: Bounds<Pixels>,
1726 _request_layout: &mut Self::RequestLayoutState,
1727 window: &mut Window,
1728 cx: &mut App,
1729 ) -> FieldPrepaint {
1730 let theme = Theme::of(cx).clone();
1731 let field = self.field.read(cx);
1732 let selected_range = field.selected_range.clone();
1733 let cursor = field.cursor_offset();
1734 let shape = field.shape;
1735 let marked_range = field.marked_range.clone();
1736 let scrolled = field.scroll;
1737 let follow_caret = field.follow_caret;
1738 let style = window.text_style();
1739
1740 let (text, is_placeholder) = display_text(field);
1741 let text_color = if is_placeholder {
1742 theme.text_faint
1743 } else {
1744 style.color
1745 };
1746
1747 let run = TextRun {
1748 len: text.len(),
1749 font: style.font(),
1750 color: text_color,
1751 background_color: None,
1752 underline: None,
1753 strikethrough: None,
1754 };
1755 let runs = if is_placeholder {
1759 vec![run]
1760 } else {
1761 coloured(&text, &field.spans, &run, &theme.syntax)
1762 };
1763 let runs = match marked_range.as_ref() {
1764 Some(marked) => underlined(runs, marked),
1765 None => runs,
1766 };
1767
1768 let font_size = style.font_size.to_pixels(window.rem_size());
1769 let line_height = field.line_height();
1770 let wrap_width = shape.is_multiline().then_some(bounds.size.width);
1773 let lines = window
1774 .text_system()
1775 .shape_text(text, font_size, &runs, wrap_width, None)
1776 .map(|lines| lines.into_vec())
1777 .unwrap_or_default();
1778
1779 let content_height: Pixels = lines.iter().map(|l| l.size(line_height).height).sum();
1788 let content_width = lines.iter().map(|l| l.width()).fold(px(0.), Pixels::max);
1789 let max = gpui::point(
1790 (content_width - bounds.size.width).max(px(0.)),
1791 (content_height - bounds.size.height).max(px(0.)),
1792 );
1793 let mut scroll = gpui::point(
1794 scrolled.x.clamp(px(0.), max.x),
1795 scrolled.y.clamp(px(0.), max.y),
1796 );
1797 if follow_caret && let Some(at) = position_for_offset(&lines, cursor, line_height) {
1798 if at.y < scroll.y {
1799 scroll.y = at.y;
1800 } else if at.y + line_height > scroll.y + bounds.size.height {
1801 scroll.y = at.y + line_height - bounds.size.height;
1802 }
1803 if at.x < scroll.x {
1806 scroll.x = at.x;
1807 } else if at.x + CARET_WIDTH > scroll.x + bounds.size.width {
1808 scroll.x = at.x + CARET_WIDTH - bounds.size.width;
1809 }
1810 scroll.x = scroll.x.clamp(px(0.), max.x);
1811 scroll.y = scroll.y.clamp(px(0.), max.y);
1812 }
1813 self.field.update(cx, |field, _| {
1814 field.scroll = scroll;
1815 field.follow_caret = false;
1816 });
1817 let origin = bounds.origin - scroll;
1818
1819 let (selection, cursor) = if selected_range.is_empty() {
1820 let at = position_for_offset(&lines, cursor, line_height).unwrap_or_default();
1821 (
1822 Vec::new(),
1823 Some(fill(
1824 Bounds::new(
1827 origin + at + gpui::point(px(0.), (line_height - font_size) / 2.),
1828 gpui::size(CARET_WIDTH, font_size),
1829 ),
1830 theme.caret,
1831 )),
1832 )
1833 } else {
1834 (
1835 selection_rows(&lines, &selected_range, line_height)
1836 .into_iter()
1837 .map(|rect| {
1838 fill(
1839 Bounds::new(origin + rect.origin, rect.size),
1840 theme.selection,
1841 )
1842 })
1843 .collect(),
1844 None,
1845 )
1846 };
1847
1848 FieldPrepaint {
1849 lines,
1850 origin,
1851 cursor,
1852 selection,
1853 }
1854 }
1855
1856 fn paint(
1857 &mut self,
1858 _id: Option<&GlobalElementId>,
1859 _inspector_id: Option<&gpui::InspectorElementId>,
1860 bounds: Bounds<Pixels>,
1861 _request_layout: &mut Self::RequestLayoutState,
1862 prepaint: &mut Self::PrepaintState,
1863 window: &mut Window,
1864 cx: &mut App,
1865 ) {
1866 let focus_handle = self.field.read(cx).focus_handle.clone();
1867 let caret_on = self.field.read(cx).caret_on;
1868 window.handle_input(
1869 &focus_handle,
1870 ElementInputHandler::new(bounds, self.field.clone()),
1871 cx,
1872 );
1873 let line_height = self.field.read(cx).line_height();
1874 let dragged = self.field.clone();
1884 window.on_mouse_event(move |event: &MouseMoveEvent, phase, _window, cx| {
1885 if phase != DispatchPhase::Bubble || !event.dragging() {
1890 return;
1891 }
1892 dragged.update(cx, |field, cx| {
1893 field.drag_to(event.position, line_height, cx);
1894 });
1895 });
1896 let lines = std::mem::take(&mut prepaint.lines);
1897 let selection = std::mem::take(&mut prepaint.selection);
1898 let cursor = prepaint.cursor.take();
1899 let origin = prepaint.origin;
1900
1901 window.with_content_mask(Some(gpui::ContentMask::new(bounds)), |window| {
1904 for selection in selection {
1905 window.paint_quad(selection);
1906 }
1907
1908 let mut top = origin;
1909 for line in &lines {
1910 line.paint(top, line_height, gpui::TextAlign::Left, None, window, cx)
1911 .ok();
1912 top.y += line.size(line_height).height;
1913 }
1914
1915 if focus_handle.is_focused(window)
1918 && caret_on
1919 && let Some(cursor) = cursor
1920 {
1921 window.paint_quad(cursor);
1922 }
1923 });
1924
1925 self.field.update(cx, |field, _| {
1926 field.last_layout = lines;
1927 field.last_bounds = Some(bounds);
1928 });
1929 }
1930}