Skip to main content

ui/input/
element.rs

1//! Shaping and painting: runs, rows, selection and caret.
2
3use super::*;
4use crate::AppExt as _;
5
6/// What gets painted, and whether it is the placeholder — which is the only
7/// reason the colour differs.
8pub(super) fn display_text(field: &TextField) -> (chip::Chipped, bool) {
9    if field.content.is_empty() {
10        (chip::Chipped::plain(field.placeholder.clone()), true)
11    } else {
12        (chip::chipped(&field.content, &field.chips), false)
13    }
14}
15
16/// `runs` with each chip in `chipped` painted: the glyph's slot empty, the
17/// title underlined, both in `color` whatever the spans said.
18fn chip_runs(mut runs: Vec<TextRun>, chipped: &chip::Chipped, color: gpui::Hsla) -> Vec<TextRun> {
19    for ((slot, _), title) in chipped.slots.iter().zip(&chipped.titles) {
20        runs = caret::recoloured(runs, &(slot.start..title.end), color);
21        runs = caret::recoloured(runs, slot, gpui::transparent_black());
22        runs = underlined(runs, title);
23    }
24    runs
25}
26
27/// One run per span, the text between them in the field's own colour.
28///
29/// The runs a field paints [`TextField::set_spans`] as, public for the same
30/// reason [`next_boundary`] is: anything shaping the same text with the same
31/// palette wants the same answer, and this is where it is decided.
32///
33/// The spans are whatever the caller last handed over, which may describe text
34/// this frame no longer holds — see [`TextField::set_spans`]. A range that runs
35/// past the end, overlaps the one before it, or cuts a character in half is
36/// dropped rather than shifting the runs after it: shaping needs the lengths to
37/// add up to the text, and a colour that is wrong for one frame is cheaper than
38/// a panic.
39///
40/// `base.len` is ignored: every run is measured off `text`.
41pub fn coloured(
42    text: &str,
43    spans: &[(Range<usize>, HighlightKind)],
44    base: &TextRun,
45    palette: &SyntaxPalette,
46) -> Vec<TextRun> {
47    if spans.is_empty() {
48        return vec![TextRun {
49            len: text.len(),
50            ..base.clone()
51        }];
52    }
53    let mut runs = Vec::with_capacity(spans.len() * 2 + 1);
54    let mut at = 0;
55    for (range, kind) in spans {
56        if range.start < at
57            || range.end <= range.start
58            || range.end > text.len()
59            || !text.is_char_boundary(range.start)
60            || !text.is_char_boundary(range.end)
61        {
62            continue;
63        }
64        if range.start > at {
65            runs.push(TextRun {
66                len: range.start - at,
67                ..base.clone()
68            });
69        }
70        runs.push(TextRun {
71            len: range.end - range.start,
72            color: palette.color(*kind),
73            ..base.clone()
74        });
75        at = range.end;
76    }
77    if at < text.len() {
78        runs.push(TextRun {
79            len: text.len() - at,
80            ..base.clone()
81        });
82    }
83    runs
84}
85
86/// The IME composition range underlined, so the user can see what is still
87/// provisional. Each run is cut at the range's edges and the pieces inside it
88/// take the line, in whatever colour they already had.
89pub fn underlined(runs: Vec<TextRun>, marked: &Range<usize>) -> Vec<TextRun> {
90    let mut out = Vec::with_capacity(runs.len() + 2);
91    let mut at = 0;
92    for run in runs {
93        let end = at + run.len;
94        for (start, stop, mark) in [
95            (at, end.min(marked.start), false),
96            (at.max(marked.start), end.min(marked.end), true),
97            (at.max(marked.end), end, false),
98        ] {
99            if stop <= start {
100                continue;
101            }
102            out.push(TextRun {
103                len: stop - start,
104                underline: mark.then(|| UnderlineStyle {
105                    color: Some(run.color),
106                    thickness: px(1.0),
107                    wavy: false,
108                }),
109                ..run.clone()
110            });
111        }
112        at = end;
113    }
114    out
115}
116
117// ---------------------------------------------------------------------------
118// Line geometry. `shape_text` returns one `WrappedLine` per hard newline, each
119// wrapping into rows of its own; gpui resolves positions *within* a line, so
120// everything here is walking that list and nothing re-implements shaping.
121// ---------------------------------------------------------------------------
122
123/// Each shaped line with the byte offset it starts at. `shape_text` splits on
124/// `\n` and drops the separator, so each line starts one byte past the last.
125pub(super) fn lines_from(lines: &[WrappedLine]) -> impl Iterator<Item = (usize, &WrappedLine)> {
126    lines.iter().scan(0usize, |start, line| {
127        let at = *start;
128        *start = at + line.len() + 1;
129        Some((at, line))
130    })
131}
132
133/// Every visual row: the byte range it covers and its top edge, relative to the
134/// text origin. A wrap boundary resolves to a byte index exactly the way
135/// `WrappedLineLayout::position_for_index` does it internally — that mapping is
136/// not exposed, and selection needs one quad per row.
137pub(super) fn rows(lines: &[WrappedLine], line_height: Pixels) -> Vec<(Range<usize>, Pixels)> {
138    let mut out = Vec::new();
139    let mut top = px(0.);
140    for (start, line) in lines_from(lines) {
141        let mut row_start = start;
142        for boundary in line.wrap_boundaries() {
143            let at = start + line.runs()[boundary.run_ix].glyphs[boundary.glyph_ix].index;
144            out.push((row_start..at, top));
145            row_start = at;
146            top += line_height;
147        }
148        out.push((row_start..start + line.len(), top));
149        top += line_height;
150    }
151    out
152}
153
154/// Byte offset → position relative to the text origin.
155pub(super) fn position_for_offset(
156    lines: &[WrappedLine],
157    offset: usize,
158    line_height: Pixels,
159) -> Option<Point<Pixels>> {
160    let mut top = px(0.);
161    for (start, line) in lines_from(lines) {
162        if offset <= start + line.len() {
163            let local = line.position_for_index(offset.saturating_sub(start), line_height)?;
164            return Some(gpui::point(local.x, local.y + top));
165        }
166        top += line.size(line_height).height;
167    }
168    None
169}
170
171/// Position relative to the text origin → the closest byte offset.
172pub(super) fn offset_for_position(
173    lines: &[WrappedLine],
174    position: Point<Pixels>,
175    line_height: Pixels,
176) -> usize {
177    let mut top = px(0.);
178    let mut last = 0;
179    for (start, line) in lines_from(lines) {
180        let height = line.size(line_height).height;
181        last = start + line.len();
182        if position.y < top + height {
183            let local = gpui::point(position.x, position.y - top);
184            let (Ok(index) | Err(index)) = line.closest_index_for_position(local, line_height);
185            return start + index;
186        }
187        top += height;
188    }
189    last
190}
191
192/// The char boundary at or before `offset`, clamped to the end of `text`.
193pub(super) fn floor_boundary(text: &str, offset: usize) -> usize {
194    let mut offset = offset.min(text.len());
195    while !text.is_char_boundary(offset) {
196        offset -= 1;
197    }
198    offset
199}
200
201/// The selection as one rect per visual row, relative to the text origin.
202///
203/// A row's left edge is always x=0, so a continuation row is taken from there
204/// rather than by looking the offset up — at a soft wrap the two rows share a
205/// byte offset, and the lookup resolves it to the end of the earlier row.
206pub(super) fn selection_rows(
207    lines: &[WrappedLine],
208    range: &Range<usize>,
209    line_height: Pixels,
210) -> Vec<Bounds<Pixels>> {
211    rows(lines, line_height)
212        .into_iter()
213        .filter(|(row, _)| range.start <= row.end && range.end >= row.start)
214        .filter_map(|(row, top)| {
215            let left = if range.start <= row.start {
216                px(0.)
217            } else {
218                position_for_offset(lines, range.start, line_height)?.x
219            };
220            let right = position_for_offset(lines, range.end.min(row.end), line_height)?.x;
221            (right > left).then(|| {
222                Bounds::from_corners(
223                    gpui::point(left, top),
224                    gpui::point(right, top + line_height),
225                )
226            })
227        })
228        .collect()
229}
230
231impl Render for TextField {
232    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
233        // The only place the blink starts: `caret_moved` drops the task, so the
234        // next render brings it back in phase, solid beat first.
235        if self.focus_handle.is_focused(_window) && cx.caret_blink() {
236            if self.blink.is_none() {
237                self.start_blink(cx);
238            }
239        } else {
240            self.blink = None;
241            self.caret_on = true;
242        }
243        let theme = Theme::of(cx);
244        let mut key_context = gpui::KeyContext::default();
245        key_context.add(KEY_CONTEXT);
246        if self.shape.is_multiline() {
247            key_context.add(MULTILINE_KEY_CONTEXT);
248        }
249        if let Some(extra) = self.key_context.clone() {
250            key_context.add(extra);
251        }
252        div()
253            .key_context(key_context)
254            .track_focus(&self.focus_handle(cx))
255            .cursor(CursorStyle::IBeam)
256            .on_action(cx.listener(Self::backspace))
257            .on_action(cx.listener(Self::delete))
258            .on_action(cx.listener(Self::left))
259            .on_action(cx.listener(Self::right))
260            .on_action(cx.listener(Self::select_left))
261            .on_action(cx.listener(Self::select_right))
262            .on_action(cx.listener(Self::select_all))
263            .on_action(cx.listener(Self::home))
264            .on_action(cx.listener(Self::end))
265            .on_action(cx.listener(Self::select_home))
266            .on_action(cx.listener(Self::select_end))
267            .on_action(cx.listener(Self::word_left))
268            .on_action(cx.listener(Self::word_right))
269            .on_action(cx.listener(Self::select_word_left))
270            .on_action(cx.listener(Self::select_word_right))
271            .on_action(cx.listener(Self::up))
272            .on_action(cx.listener(Self::down))
273            .on_action(cx.listener(Self::select_up))
274            .on_action(cx.listener(Self::select_down))
275            .on_action(cx.listener(Self::insert_newline))
276            .on_action(cx.listener(Self::undo))
277            .on_action(cx.listener(Self::redo))
278            .on_action(cx.listener(Self::delete_word_left))
279            .on_action(cx.listener(Self::delete_word_right))
280            .on_action(cx.listener(Self::delete_to_line_start))
281            .on_action(cx.listener(Self::delete_to_line_end))
282            .on_action(cx.listener(Self::show_character_palette))
283            .on_action(cx.listener(Self::paste))
284            .on_action(cx.listener(Self::cut))
285            .on_action(cx.listener(Self::copy))
286            .on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
287            .on_mouse_down(MouseButton::Right, cx.listener(Self::on_right_mouse_down))
288            .on_mouse_up(MouseButton::Left, cx.listener(Self::on_mouse_up))
289            .on_mouse_up_out(MouseButton::Left, cx.listener(Self::on_mouse_up))
290            .on_scroll_wheel(cx.listener(Self::on_scroll_wheel))
291            .w_full()
292            .when(self.frame, |field| {
293                field
294                    .px(px(10.0))
295                    .py(px(7.0))
296                    .rounded(px(Theme::button_radius()))
297                    .bg(theme.input_bg)
298                    .border_1()
299                    .border_color(if self.focus_handle.contains_focused(_window, cx) {
300                        theme.ring
301                    } else {
302                        theme.border
303                    })
304            })
305            .text_size(px(self.metrics.size()))
306            .font_weight(self.metrics.weight)
307            .line_height(px(self.metrics.line_height()))
308            .text_color(theme.text)
309            .child(TextFieldElement { field: cx.entity() })
310            .children(self.edit_menu(_window, cx))
311    }
312}
313
314/// Paints the shaped lines plus selection and caret. A custom element because
315/// all three are geometry derived from the shaped text, which only exists after
316/// layout.
317pub(super) struct TextFieldElement {
318    field: Entity<TextField>,
319}
320
321pub(super) struct FieldPrepaint {
322    lines: Vec<WrappedLine>,
323    shown: Shown,
324    /// Each chip's glyph and the slot it is painted in, in `lines`' offsets.
325    glyphs: Vec<(Range<usize>, icons::Icon)>,
326    glyph_color: gpui::Hsla,
327    font_size: Pixels,
328    /// Top-left of the text, which is the box moved up by the scroll offset.
329    origin: Point<Pixels>,
330    cursor: Option<PaintQuad>,
331    covered: Option<Covered>,
332    /// One quad per visual row each match covers.
333    matches: Vec<PaintQuad>,
334    /// One quad per visual row the selection covers.
335    selection: Vec<PaintQuad>,
336    /// The text drawn again over the selection and the matches.
337    overdraws: Vec<overdraw::Overdraw>,
338}
339
340/// The glyph a solid block caret covers, for [`caret::paint_covered`].
341struct Covered {
342    layout: std::sync::Arc<gpui::LineLayout>,
343    /// Within `layout`'s line.
344    range: Range<usize>,
345    /// The top left of its slot.
346    at: Point<Pixels>,
347    color: gpui::Hsla,
348}
349
350impl IntoElement for TextFieldElement {
351    type Element = Self;
352
353    fn into_element(self) -> Self::Element {
354        self
355    }
356}
357
358impl Element for TextFieldElement {
359    type RequestLayoutState = ();
360    type PrepaintState = FieldPrepaint;
361
362    fn id(&self) -> Option<ElementId> {
363        None
364    }
365
366    fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
367        None
368    }
369
370    fn request_layout(
371        &mut self,
372        _id: Option<&GlobalElementId>,
373        _inspector_id: Option<&gpui::InspectorElementId>,
374        window: &mut Window,
375        cx: &mut App,
376    ) -> (LayoutId, ()) {
377        let mut style = Style::default();
378        style.size.width = relative(1.).into();
379        let field = self.field.read(cx);
380        // The field's own, here as everywhere: sizing the box off one height
381        // and hit-testing it at another is how a click lands on the wrong row.
382        let line_height = field.line_height();
383        let shape = field.shape;
384
385        let (min, max) = match shape {
386            Shape::Line => {
387                style.size.height = line_height.into();
388                return (window.request_layout(style, [], cx), ());
389            }
390            Shape::Rows(rows) => {
391                style.size.height = (line_height * rows.max(1) as f32).into();
392                return (window.request_layout(style, [], cx), ());
393            }
394            // Growing needs the row count, and the row count needs shaping at
395            // the width layout is still deciding — which is exactly what a
396            // measured layout is for.
397            Shape::Grow { min, max } => (min.max(1), max.max(min.max(1))),
398        };
399
400        let text = display_text(field).0.text;
401        // Measuring runs after the element's text style is popped, where the
402        // window reports gpui's default font instead.
403        let text_style = window.text_style();
404        let font_size = text_style.font_size.to_pixels(window.rem_size());
405        let id = window.request_measured_layout(style, move |known, available, window, _cx| {
406            // Prefer the width layout has already settled on. Taffy also probes
407            // with min/max-content, where there is no width to wrap against —
408            // and counting rows off unwrapped text under-reports them, which
409            // would size the box for fewer lines than it goes on to paint.
410            let wrap_width = known.width.or(match available.width {
411                gpui::AvailableSpace::Definite(width) => Some(width),
412                _ => None,
413            });
414            let run = TextRun {
415                len: text.len(),
416                font: text_style.font(),
417                color: text_style.color,
418                background_color: None,
419                underline: None,
420                strikethrough: None,
421            };
422            let count = window
423                .text_system()
424                .shape_text(text.clone(), font_size, &[run], wrap_width, None)
425                .map(|lines| {
426                    lines
427                        .iter()
428                        .map(|line| line.wrap_boundaries().len() + 1)
429                        .sum::<usize>()
430                })
431                .unwrap_or(1);
432            gpui::size(
433                wrap_width.unwrap_or(px(0.)),
434                line_height * count.clamp(min, max) as f32,
435            )
436        });
437        (id, ())
438    }
439
440    fn prepaint(
441        &mut self,
442        _id: Option<&GlobalElementId>,
443        _inspector_id: Option<&gpui::InspectorElementId>,
444        bounds: Bounds<Pixels>,
445        _request_layout: &mut Self::RequestLayoutState,
446        window: &mut Window,
447        cx: &mut App,
448    ) -> FieldPrepaint {
449        let theme = Theme::of(cx).clone();
450        let field = self.field.read(cx);
451        let (chipped, is_placeholder) = display_text(field);
452        // Everything from here on is in the offsets of the text painted.
453        let shown = chipped.shown.clone();
454        let selected_range = shown.range(&field.selected_range);
455        let cursor = shown.at(field.cursor_offset());
456        let shape = field.shape;
457        let marked_range = field.marked_range.as_ref().map(|range| shown.range(range));
458        let matches: Vec<_> = field
459            .matches
460            .iter()
461            .filter(|range| {
462                range.start <= range.end
463                    && field.content.is_char_boundary(range.start)
464                    && field.content.is_char_boundary(range.end)
465            })
466            .map(|range| shown.range(range))
467            .collect();
468        let scrolled = field.scroll;
469        let caret_shape = cx.caret_shape();
470        let caret_height = cx.caret_height();
471        // A single line out of focus shows its start, wherever its caret is.
472        let at_start = !shape.is_multiline() && !field.focus_handle.contains_focused(window, cx);
473        let follow_caret =
474            !at_start && (field.follow_caret || caret_shape != field.last_caret_shape);
475        let style = window.text_style();
476
477        let text = chipped.text.clone();
478        let text_color = if is_placeholder {
479            theme.text_faint
480        } else {
481            style.color
482        };
483
484        let run = TextRun {
485            len: text.len(),
486            font: style.font(),
487            color: text_color,
488            background_color: None,
489            underline: None,
490            strikethrough: None,
491        };
492        // Syntax first, then the IME composition range underlined over the top
493        // of it: both cut the text into runs, and the underline has to land on
494        // whatever colour is already there.
495        let runs = if is_placeholder {
496            vec![run]
497        } else {
498            let spans: Vec<_> = field
499                .spans
500                .iter()
501                .map(|(range, kind)| (shown.range(range), *kind))
502                .collect();
503            chip_runs(
504                coloured(&text, &spans, &run, &theme.syntax),
505                &chipped,
506                text_color,
507            )
508        };
509        let runs = match marked_range.as_ref() {
510            Some(marked) => underlined(runs, marked),
511            None => runs,
512        };
513        // In the order they are painted.
514        let match_wash = theme.warning.opacity(0.25);
515        let focused = field.focus_handle.contains_focused(window, cx);
516        let layers: Vec<_> = (focused && !selected_range.is_empty())
517            .then(|| (selected_range.clone(), theme.selection))
518            .into_iter()
519            .chain(matches.iter().map(|range| (range.clone(), match_wash)))
520            .collect();
521        let overdraws = match is_placeholder {
522            true => Vec::new(),
523            false => overdraw::overdraws(&runs, theme.bg, &layers),
524        };
525
526        let font_size = style.font_size.to_pixels(window.rem_size());
527        let line_height = field.line_height();
528        // A single line never wraps: it scrolls sideways instead, so shaping it
529        // against the field's width would fold it into rows nothing can reach.
530        let wrap_width = shape.is_multiline().then_some(bounds.size.width);
531        let lines = window
532            .text_system()
533            .shape_text(text.clone(), font_size, &runs, wrap_width, None)
534            .map(|lines| lines.into_vec())
535            .unwrap_or_default();
536
537        // Clamp every frame, not just when scrolling: the content this is
538        // measured against shrinks under it — delete the last line while parked
539        // at the bottom and an unclamped offset leaves the box showing nothing.
540        //
541        // A wide caret needs trailing room even at the end of the longest row.
542        // Keep the bar's existing scroll extent unchanged.
543        let content_height: Pixels = lines.iter().map(|l| l.size(line_height).height).sum();
544        let content_width = lines.iter().map(|l| l.width()).fold(px(0.), Pixels::max);
545        // The slot a wide caret takes where no character follows it — the end
546        // of the text, a soft-wrap boundary: the font's `ch` width.
547        let slot = (caret_shape != CaretShape::Bar)
548            .then(|| caret::zero_width(style.font(), font_size, window));
549        // A placeholder is not text: over it the caret takes the empty slot.
550        let follows = (!is_placeholder)
551            .then(|| {
552                lines_from(&lines)
553                    .find_map(|(start, line)| {
554                        (cursor <= start + line.len()).then(|| {
555                            caret::character_advance(line, &line.text, cursor.saturating_sub(start))
556                        })
557                    })
558                    .flatten()
559            })
560            .flatten();
561        let advance = slot.map(|slot| follows.unwrap_or(slot));
562        let hollow = !window.is_window_active();
563        let caret_width = caret_shape
564            .quad(
565                Bounds::new(
566                    gpui::point(px(0.), px(0.)),
567                    gpui::size(CARET_WIDTH, font_size),
568                ),
569                line_height,
570                caret_height,
571                advance.unwrap_or_default(),
572                theme.caret,
573                hollow,
574            )
575            .bounds
576            .size
577            .width;
578        // A field that wraps never scrolls sideways.
579        let max = gpui::point(
580            match shape.is_multiline() {
581                true => px(0.),
582                false => (content_width + slot.unwrap_or_default() - bounds.size.width).max(px(0.)),
583            },
584            (content_height - bounds.size.height).max(px(0.)),
585        );
586        let mut scroll = gpui::point(
587            scrolled.x.clamp(px(0.), max.x),
588            scrolled.y.clamp(px(0.), max.y),
589        );
590        if follow_caret && let Some(at) = position_for_offset(&lines, cursor, line_height) {
591            if at.y < scroll.y {
592                scroll.y = at.y;
593            } else if at.y + line_height > scroll.y + bounds.size.height {
594                scroll.y = at.y + line_height - bounds.size.height;
595            }
596            // The caret is the thing being kept in view, so it is its own width
597            // that has to clear the right edge — not the character before it.
598            if at.x < scroll.x {
599                scroll.x = at.x;
600            } else if at.x + caret_width > scroll.x + bounds.size.width {
601                scroll.x = at.x + caret_width - bounds.size.width;
602            }
603            scroll.x = scroll.x.clamp(px(0.), max.x);
604            scroll.y = scroll.y.clamp(px(0.), max.y);
605        }
606        if at_start {
607            scroll.x = px(0.);
608        }
609        self.field.update(cx, |field, _| {
610            field.scroll = scroll;
611            field.follow_caret = false;
612            field.last_caret_shape = caret_shape;
613        });
614        let origin = bounds.origin - scroll;
615
616        let matches = if is_placeholder {
617            Vec::new()
618        } else {
619            matches
620                .iter()
621                .flat_map(|range| selection_rows(&lines, range, line_height))
622                .map(|rect| fill(Bounds::new(origin + rect.origin, rect.size), match_wash))
623                .collect()
624        };
625
626        let caret_shown = caret_shape.shown(!hollow, crate::input::caret::inactive_caret(cx));
627        let covered =
628            (selected_range.is_empty() && follows.is_some() && caret_shape.cuts_out(hollow))
629                .then(|| {
630                    let (start, line) =
631                        lines_from(&lines).find(|(start, line)| cursor <= start + line.len())?;
632                    let at = position_for_offset(&lines, cursor, line_height)?;
633                    Some(Covered {
634                        layout: line.unwrapped_layout.clone(),
635                        range: cursor - start..next_boundary(&text, cursor) - start,
636                        at: origin + at,
637                        color: theme::legible(theme.bg, theme.caret, theme::LEGIBLE),
638                    })
639                })
640                .flatten();
641        let (selection, cursor) = if selected_range.is_empty() {
642            let at = position_for_offset(&lines, cursor, line_height).unwrap_or_default();
643            (
644                Vec::new(),
645                caret_shown.then(|| {
646                    caret_shape.quad(
647                        // The font's size rather than the line's: leading is not
648                        // a bar's or an underline's to fill. A block fills it.
649                        Bounds::new(
650                            origin + at + gpui::point(px(0.), (line_height - font_size) / 2.),
651                            gpui::size(CARET_WIDTH, font_size),
652                        ),
653                        line_height,
654                        caret_height,
655                        advance.unwrap_or_default(),
656                        theme.caret,
657                        hollow,
658                    )
659                }),
660            )
661        } else {
662            (
663                selection_rows(&lines, &selected_range, line_height)
664                    .into_iter()
665                    .map(|rect| {
666                        fill(
667                            Bounds::new(origin + rect.origin, rect.size),
668                            theme.selection,
669                        )
670                    })
671                    .collect(),
672                None,
673            )
674        };
675
676        FieldPrepaint {
677            lines,
678            shown,
679            glyphs: chipped.slots,
680            glyph_color: theme.text_muted,
681            font_size,
682            origin,
683            cursor,
684            covered,
685            matches,
686            selection,
687            overdraws,
688        }
689    }
690
691    fn paint(
692        &mut self,
693        _id: Option<&GlobalElementId>,
694        _inspector_id: Option<&gpui::InspectorElementId>,
695        bounds: Bounds<Pixels>,
696        _request_layout: &mut Self::RequestLayoutState,
697        prepaint: &mut Self::PrepaintState,
698        window: &mut Window,
699        cx: &mut App,
700    ) {
701        let focus_handle = self.field.read(cx).focus_handle.clone();
702        let caret_on = self.field.read(cx).caret_on;
703        window.handle_input(
704            &focus_handle,
705            ElementInputHandler::new(bounds, self.field.clone()),
706            cx,
707        );
708        let line_height = self.field.read(cx).line_height();
709        // A drag that leaves the box is still a drag. `on_mouse_move` on the
710        // field's own div fires only while the box is the thing under the
711        // pointer, so a run dragged past the edge froze at the last character
712        // inside it — and the last line of a full box was unreachable, since
713        // reaching it means passing the edge. This is the window's own move,
714        // which arrives wherever the pointer went.
715        //
716        // Registered every frame because that is the contract: the listener is
717        // cleared with the frame that installed it.
718        let dragged = self.field.clone();
719        window.on_mouse_event(move |event: &MouseMoveEvent, phase, _window, cx| {
720            // The button being down is what makes this a drag. `selecting`
721            // is only cleared on the release, so a press whose release went
722            // somewhere we never heard about would otherwise leave a plain
723            // hover dragging the run around.
724            if phase != DispatchPhase::Bubble || !event.dragging() {
725                return;
726            }
727            dragged.update(cx, |field, cx| {
728                field.drag_to(event.position, line_height, cx);
729            });
730        });
731        let lines = std::mem::take(&mut prepaint.lines);
732        let matches = std::mem::take(&mut prepaint.matches);
733        // Shown while focus is in the field or its menu, kept for when it
734        // comes back.
735        let selection = match focus_handle.contains_focused(window, cx) {
736            true => std::mem::take(&mut prepaint.selection),
737            false => Vec::new(),
738        };
739        let cursor = prepaint.cursor.take();
740        let origin = prepaint.origin;
741
742        // Scrolled text runs past the box in both directions, so everything the
743        // field draws is masked to it — text, selection and caret alike.
744        window.with_content_mask(Some(gpui::ContentMask::new(bounds)), |window| {
745            for quad in selection.into_iter().chain(matches) {
746                window.paint_quad(quad);
747            }
748
749            let mut top = origin;
750            for line in &lines {
751                line.paint(top, line_height, gpui::TextAlign::Left, None, window, cx)
752                    .ok();
753                top.y += line.size(line_height).height;
754            }
755            overdraw::paint(
756                lines.iter().map(|line| &***line),
757                origin,
758                line_height,
759                px(0.),
760                &prepaint.overdraws,
761                window,
762            );
763
764            // The caret only exists while focused — an unfocused field showing
765            // one reads as two cursors on screen.
766            if focus_handle.is_focused(window)
767                && caret_on
768                && let Some(cursor) = cursor
769            {
770                window.paint_quad(cursor);
771                if let Some(covered) = &prepaint.covered {
772                    caret::paint_covered(
773                        &covered.layout,
774                        &covered.range,
775                        covered.at,
776                        line_height,
777                        covered.color,
778                        window,
779                    );
780                }
781            }
782
783            for (slot, glyph) in &prepaint.glyphs {
784                let Some(data) = glyph.data() else { continue };
785                let (Some(start), Some(end)) = (
786                    position_for_offset(&lines, slot.start, line_height),
787                    position_for_offset(&lines, slot.end, line_height),
788                ) else {
789                    continue;
790                };
791                let side = (end.x - start.x).min(prepaint.font_size);
792                let bounds = Bounds::new(
793                    origin + start + gpui::point(px(0.), (line_height - side) / 2.),
794                    gpui::size(side, side),
795                );
796                let path = SharedString::from(format!("field-glyph-{:p}", data.as_ptr()));
797                window
798                    .paint_svg(
799                        bounds,
800                        path,
801                        Some(data),
802                        gpui::TransformationMatrix::unit(),
803                        prepaint.glyph_color,
804                        cx,
805                    )
806                    .ok();
807            }
808        });
809
810        let shown = std::mem::take(&mut prepaint.shown);
811        self.field.update(cx, |field, _| {
812            field.last_layout = lines;
813            field.last_shown = shown;
814            field.last_bounds = Some(bounds);
815        });
816    }
817}