reorganizing line paint logic

This commit is contained in:
temportalflux
2026-07-11 09:31:07 -04:00
parent 93b00c5d32
commit bee1f68e9c
@@ -7,7 +7,7 @@ use gpui::{
ElementInputHandler, Entity, FocusHandle, Focusable, Hitbox, HitboxBehavior, Hsla,
InteractiveElement, Interactivity, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent,
MouseUpEvent, PaintQuad, Pixels, Point, ScrollWheelEvent, ShapedLine, SharedString, Style,
StyleRefinement, Styled, TextRun, TextStyle, Window, fill, point, size,
StyleRefinement, Styled, TextAlign, TextRun, TextStyle, Window, fill, point, size,
};
use smallvec::SmallVec;
@@ -74,7 +74,11 @@ impl super::StateBackedElement for TextInputElement {
}
enum PrepaintElement {
Line(ShapedLine),
Line {
line: ShapedLine,
point: Point<Pixels>,
align: TextAlign,
},
Quad(PaintQuad),
}
@@ -94,7 +98,7 @@ pub mod element {
pub hitbox: Option<Hitbox>,
pub focus_handle: FocusHandle,
pub(super) elements: SmallVec<[PrepaintElement; 3]>,
pub scroll_x: Pixels,
pub scroll_offset: Point<Pixels>,
pub display_text: SharedString,
pub caret_visible: bool,
}
@@ -160,8 +164,6 @@ impl Element for TextInputElement {
let focus_handle = input.focus_handle(cx);
let caret_pos = input.caret_pos();
let selection = input.selected_range();
// TODO: horizontal scroll
let scroll_x_input = Pixels::ZERO; //input.scroll_x();
// TODO: Cursor blinking
let cursor_visible = true; // input.cursor_visible();
@@ -184,6 +186,20 @@ impl Element for TextInputElement {
}
};
let (hitbox, scroll_offset) = self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
bounds.size,
window,
cx,
|_style, scroll_offset, hitbox, window, _cx| {
let hitbox =
hitbox.or_else(|| Some(window.insert_hitbox(bounds, HitboxBehavior::Normal)));
(hitbox, scroll_offset)
},
);
let style = window.text_style();
let font_size = style.font_size.to_pixels(window.rem_size());
let run = TextRun {
@@ -198,27 +214,18 @@ impl Element for TextInputElement {
.text_system()
.shape_line(display_text.clone(), font_size, &[run], None);
let caret_x_line = line.x_for_index(caret_pos);
let cursor_thickness = gpui::px(2.0);
let max_cursor_x = (bounds.size.width - cursor_thickness).max(Pixels::ZERO);
let max_scroll_x = (line.width - max_cursor_x).max(Pixels::ZERO);
let mut scroll_x = scroll_x_input.clamp(Pixels::ZERO, max_scroll_x);
if caret_x_line < scroll_x {
scroll_x = caret_x_line;
} else if caret_x_line > scroll_x + max_cursor_x {
scroll_x = caret_x_line - max_cursor_x;
}
scroll_x = scroll_x.clamp(Pixels::ZERO, max_scroll_x);
let has_selection = !selection.is_empty() && !is_empty;
if has_selection {
let start_x = line.x_for_index(selection.start);
let end_x = line.x_for_index(selection.end);
let quad = fill(
Bounds::from_corners(
point(bounds.left() + start_x.min(end_x) - scroll_x, bounds.top()),
point(
bounds.left() + start_x.max(end_x) - scroll_x,
bounds.left() + start_x.min(end_x) - scroll_offset.x,
bounds.top(),
),
point(
bounds.left() + start_x.max(end_x) - scroll_offset.x,
bounds.bottom(),
),
),
@@ -227,11 +234,17 @@ impl Element for TextInputElement {
elements.push(PrepaintElement::Quad(quad));
}
elements.push(PrepaintElement::Line(line));
let caret_x_line = line.x_for_index(caret_pos);
elements.push(PrepaintElement::Line {
line,
point: bounds.origin - point(scroll_offset.x, gpui::px(0.)),
align: TextAlign::Left,
});
let is_focused = focus_handle.is_focused(window);
if !has_selection && is_focused && cursor_visible {
let cursor_paint_x = bounds.left() + caret_x_line - scroll_x;
let cursor_thickness = gpui::px(2.0);
let cursor_paint_x = bounds.left() + caret_x_line - scroll_offset.x;
let quad = fill(
Bounds::new(
point(cursor_paint_x, bounds.top()),
@@ -242,23 +255,11 @@ impl Element for TextInputElement {
elements.push(PrepaintElement::Quad(quad));
}
let hitbox = self.interactivity.prepaint(
global_id,
inspector_id,
bounds,
bounds.size,
window,
cx,
|style, scroll_offset, hitbox, window, cx| {
hitbox.or_else(|| Some(window.insert_hitbox(bounds, HitboxBehavior::Normal)))
},
);
Self::PrepaintState {
hitbox,
focus_handle,
elements,
scroll_x,
scroll_offset,
display_text,
caret_visible: cursor_visible,
}
@@ -392,19 +393,12 @@ impl Element for TextInputElement {
});
layout_data = window.with_content_mask(Some(ContentMask { bounds }), |window| {
let line_h = window.line_height();
let mut lines = Vec::with_capacity(prepaint.elements.len());
for element in prepaint.elements.drain(..) {
match element {
PrepaintElement::Line(line) => {
let origin_x = bounds.left() - prepaint.scroll_x;
let _ = line.paint(
point(origin_x, bounds.top()),
window.line_height(),
gpui::TextAlign::Left,
None,
window,
cx,
);
PrepaintElement::Line { line, point, align } => {
let _ = line.paint(point, line_h, align, None, window, cx);
lines.push(line);
}
PrepaintElement::Quad(quad) => window.paint_quad(quad),