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