diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 4c2aa08c..f1c4e2c6 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -182,8 +182,8 @@ where window_bounds: Some(WindowBounds::Windowed(window_bounds)), titlebar: Some(TitleBar::title_bar_options()), window_min_size: Some(gpui::Size { - width: px(640.), - height: px(480.), + width: px(480.), + height: px(320.), }), kind: WindowKind::Normal, #[cfg(target_os = "linux")] diff --git a/crates/ui/src/input/element.rs b/crates/ui/src/input/element.rs index fee5e41c..e11d7b2f 100644 --- a/crates/ui/src/input/element.rs +++ b/crates/ui/src/input/element.rs @@ -338,18 +338,23 @@ impl TextElement { return 0..1; } - let scroll_top = -state.scroll_handle.offset().y; - let total_lines = state.text_wrapper.lines.len(); - let mut visible_range = 0..total_lines; - let mut line_top = px(0.); - for (ix, line) in state.text_wrapper.lines.iter().enumerate() { - line_top += line.height(line_height); + let Some(last_layout) = state.last_layout.as_ref() else { + return 0..1; + }; - if line_top < scroll_top { + let scroll_top = state.scroll_handle.offset().y; + let total_lines = last_layout.lines.len(); + + let mut visible_range = 0..total_lines; + let mut line_bottom = px(0.); + for (ix, line) in last_layout.lines.iter().enumerate() { + line_bottom += (line.wrap_boundaries.len() + 1) * line_height; + + if line_bottom < -scroll_top { visible_range.start = ix; } - if line_top > scroll_top + input_height { + if line_bottom + scroll_top >= input_height { visible_range.end = (ix + 1).min(total_lines); break; } @@ -435,7 +440,6 @@ pub(super) struct PrepaintState { last_layout: LastLayout, /// The lines only contains the visible lines in the viewport, based on `visible_range`. line_numbers: Option>>, - line_number_width: Pixels, /// Size of the scrollable area by entire lines. scroll_size: Size, cursor_bounds: Option>, @@ -657,7 +661,7 @@ impl Element for TextElement { }; let wrap_width = if multi_line { - Some(bounds.size.width - line_number_width - RIGHT_MARGIN) + Some(bounds.size.width - line_number_width) } else { None }; @@ -796,10 +800,11 @@ impl Element for TextElement { lines: Rc::new(lines), line_height, visible_range, + line_number_width, + wrap_width, }, scroll_size, line_numbers, - line_number_width, cursor_bounds, cursor_scroll_offset, current_line_index, @@ -915,7 +920,10 @@ impl Element for TextElement { .skip(visible_range.start) .take(visible_range.len()) { - let p = point(origin.x + prepaint.line_number_width, origin.y + offset_y); + let p = point( + origin.x + prepaint.last_layout.line_number_width, + origin.y + offset_y, + ); _ = line.paint(p, line_height, TextAlign::Left, None, window, cx); offset_y += line.size(line_height).height; } @@ -934,7 +942,6 @@ impl Element for TextElement { state.set_input_bounds(input_bounds, cx); state.last_selected_range = Some(selected_range); state.scroll_size = prepaint.scroll_size; - state.line_number_width = prepaint.line_number_width; state .scroll_handle .set_offset(prepaint.cursor_scroll_offset); diff --git a/crates/ui/src/input/hover_popover.rs b/crates/ui/src/input/hover_popover.rs index 7319a620..571e2c8f 100644 --- a/crates/ui/src/input/hover_popover.rs +++ b/crates/ui/src/input/hover_popover.rs @@ -34,7 +34,11 @@ impl DiagnosticPopover { let Some(range) = self.marker.range.as_ref() else { return None; }; - let line_number_width = self.state.read(cx).line_number_width; + let Some(last_layout) = self.state.read(cx).last_layout.as_ref() else { + return None; + }; + + let line_number_width = last_layout.line_number_width; let (_, _, start_pos) = self .state diff --git a/crates/ui/src/input/state.rs b/crates/ui/src/input/state.rs index 1851f589..22100f4b 100644 --- a/crates/ui/src/input/state.rs +++ b/crates/ui/src/input/state.rs @@ -222,6 +222,10 @@ pub(super) struct LastLayout { pub(super) line_height: Pixels, /// The visible range (no wrap) of lines in the viewport. pub(super) visible_range: Range, + /// The wrap width of text layout, this will change will InputElement painted. + pub(super) wrap_width: Option, + /// The line number width of text layout. + pub(super) line_number_width: Pixels, } impl Deref for LastLayout { @@ -268,7 +272,6 @@ pub struct InputState { pub(super) scroll_state: ScrollbarState, /// The size of the scrollable content. pub(crate) scroll_size: gpui::Size, - pub(crate) line_number_width: Pixels, /// The mask pattern for formatting the input text pub(crate) mask_pattern: MaskPattern, @@ -344,7 +347,6 @@ impl InputState { scroll_state: ScrollbarState::default(), scroll_size: gpui::size(px(0.), px(0.)), preferred_x_offset: None, - line_number_width: px(0.), placeholder: SharedString::default(), mask_pattern: MaskPattern::default(), diagnostic_popover: None, @@ -1749,6 +1751,7 @@ impl InputState { }; let line_height = last_layout.line_height; + let line_number_width = last_layout.line_number_width; // TIP: About the IBeam cursor // @@ -1760,7 +1763,7 @@ impl InputState { // // - included the input padding. // - included the scroll offset. - let inner_position = position - bounds.origin - point(self.line_number_width, px(0.)); + let inner_position = position - bounds.origin - point(line_number_width, px(0.)); let mut index = 0; let mut y_offset = px(0.); @@ -2095,10 +2098,12 @@ impl InputState { self.input_bounds = new_bounds; // Update text_wrapper wrap_width if changed. - if wrap_width_changed { - self.text_wrapper - .set_wrap_width(Some(new_bounds.size.width), cx); - self.mode.update_auto_grow(&self.text_wrapper); + if let Some(last_layout) = self.last_layout.as_ref() { + if wrap_width_changed { + self.text_wrapper.set_wrap_width(last_layout.wrap_width, cx); + self.mode.update_auto_grow(&self.text_wrapper); + cx.notify(); + } } } @@ -2256,11 +2261,12 @@ impl EntityInputHandler for InputState { ) -> Option> { let last_layout = self.last_layout.as_ref()?; let line_height = last_layout.line_height; + let line_number_width = last_layout.line_number_width; let range = self.range_from_utf16(&range_utf16); let mut start_origin = None; let mut end_origin = None; - let line_number_origin = point(self.line_number_width, px(0.)); + let line_number_origin = point(line_number_width, px(0.)); let mut y_offset = px(0.); let mut index_offset = 0; diff --git a/crates/ui/src/input/text_wrapper.rs b/crates/ui/src/input/text_wrapper.rs index e0d8f5c3..b99294dd 100644 --- a/crates/ui/src/input/text_wrapper.rs +++ b/crates/ui/src/input/text_wrapper.rs @@ -6,17 +6,15 @@ use gpui::{App, Font, LineFragment, Pixels, SharedString}; #[allow(unused)] pub(super) struct LineWrap { /// The number of soft wrapped lines of this line (Not include first line.) + /// + /// FIXME: Here in somecase, the `line_wrapper.wrap_line` has returned different + /// like the `window.text_system().shape_text`. So, this value may not equal + /// the actual rendered lines. pub(super) wrap_lines: usize, /// The range of the line text in the entire text. pub(super) range: Range, } -impl LineWrap { - pub(super) fn height(&self, line_height: Pixels) -> Pixels { - line_height * (self.wrap_lines + 1) - } -} - /// Used to prepare the text with soft_wrap to be get lines to displayed in the TextArea /// /// After use lines to calculate the scroll size of the TextArea @@ -70,7 +68,6 @@ impl TextWrapper { let mut line_wrapper = cx .text_system() .line_wrapper(self.font.clone(), self.font_size); - let mut prev_line_ix = 0; for line in text.split('\n') { let mut line_wraps = vec![];