From 6ebb9418a09ba09ed0f20878224a77946fb75499 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 24 Oct 2025 20:18:30 +0800 Subject: [PATCH] editor: Use `measure_indent_width` for indent guides width. (#1428) Continue #1426, this changes I was forgotten to push before #1426 merged. --- crates/story/examples/editor.rs | 8 ++++--- crates/ui/src/input/element.rs | 5 ++-- crates/ui/src/input/indent.rs | 41 +++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/crates/story/examples/editor.rs b/crates/story/examples/editor.rs index 37af4d59..bb7cc1d9 100644 --- a/crates/story/examples/editor.rs +++ b/crates/story/examples/editor.rs @@ -9,7 +9,7 @@ use std::{ use anyhow::Ok; use gpui::{prelude::FluentBuilder, *}; use gpui_component::{ - ActiveTheme, ContextModal, IconName, IndexPath, Selectable, Sizable, + ActiveTheme, ContextModal, IconName, IndexPath, Sizable, button::{Button, ButtonVariants as _}, dropdown::{Dropdown, DropdownEvent, DropdownState}, h_flex, @@ -998,16 +998,18 @@ impl Render for Example { Button::new("soft-wrap") .ghost() .xsmall() + .when(self.soft_wrap, |this| this.icon(IconName::Check)) .label("Soft Wrap") - .selected(self.soft_wrap) .on_click(cx.listener(Self::toggle_soft_wrap)) }) .child({ Button::new("indent-guides") .ghost() .xsmall() + .when(self.indent_guides, |this| { + this.icon(IconName::Check) + }) .label("Indent Guides") - .selected(self.indent_guides) .on_click(cx.listener(Self::toggle_indent_guides)) }), ) diff --git a/crates/ui/src/input/element.rs b/crates/ui/src/input/element.rs index b7c5e880..703d9889 100644 --- a/crates/ui/src/input/element.rs +++ b/crates/ui/src/input/element.rs @@ -1084,7 +1084,8 @@ impl Element for TextElement { }; let hover_definition_hitbox = self.layout_hover_definition_hitbox(state, window, cx); - let indent_guides_path = self.layout_indent_guides(state, &last_layout); + let indent_guides_path = + self.layout_indent_guides(state, &last_layout, &text_style, window); PrepaintState { bounds, @@ -1194,7 +1195,7 @@ impl Element for TextElement { // Paint indent guides if let Some(path) = prepaint.indent_guides_path.take() { - window.paint_path(path, cx.theme().secondary); + window.paint_path(path, cx.theme().border.opacity(0.85)); } // Paint selections diff --git a/crates/ui/src/input/indent.rs b/crates/ui/src/input/indent.rs index 8183f176..ba0519e3 100644 --- a/crates/ui/src/input/indent.rs +++ b/crates/ui/src/input/indent.rs @@ -1,5 +1,6 @@ use gpui::{ - point, px, Context, EntityInputHandler as _, Path, PathBuilder, Pixels, SharedString, Window, + point, px, Context, EntityInputHandler as _, Hsla, Path, PathBuilder, Pixels, SharedString, + TextRun, TextStyle, Window, }; use ropey::RopeSlice; @@ -80,21 +81,46 @@ impl InputMode { } impl TextElement { + /// Measure the indent width in pixels for given column count. + fn measure_indent_width(&self, style: &TextStyle, column: usize, window: &Window) -> Pixels { + let font_size = style.font_size.to_pixels(window.rem_size()); + let layout = window.text_system().shape_line( + SharedString::from(" ".repeat(column)), + font_size, + &[TextRun { + len: column, + font: style.font(), + color: Hsla::default(), + background_color: None, + strikethrough: None, + underline: None, + }], + None, + ); + + layout.width + } + pub(super) fn layout_indent_guides( &self, state: &InputState, last_layout: &LastLayout, + text_style: &TextStyle, + window: &mut Window, ) -> Option> { if !state.mode.has_indent_guides() { return None; } + let indent_width = + self.measure_indent_width(text_style, state.mode.tab_size().tab_size, window); + let tab_size = state.mode.tab_size(); let line_height = last_layout.line_height; let visible_range = last_layout.visible_range.clone(); let mut builder = PathBuilder::stroke(px(1.)); let mut offset_y = - last_layout.visible_top + state.scroll_handle.offset().y + (line_height * 2 - px(3.)); + last_layout.visible_top + state.scroll_handle.offset().y + (line_height * 2 - px(3.5)); let mut last_indents = vec![]; for ix in visible_range { let line = state.text.slice_line(ix); @@ -103,12 +129,13 @@ impl TextElement { if line.len() > 0 { let indent_count = tab_size.indent_count(&line); for offset in (0..indent_count).step_by(tab_size.tab_size) { - let mut pos = line_layout - .position_for_index(offset, line_height) - .unwrap_or(point(px(0.), px(0.))); + let x = if indent_count > 0 { + indent_width * offset as f32 / tab_size.tab_size as f32 + } else { + px(0.) + }; - pos.x += last_layout.line_number_width; - pos.y += offset_y; + let pos = point(x + last_layout.line_number_width, offset_y); builder.move_to(pos); builder.line_to(point(pos.x, pos.y + line_height));