Compare commits

...

8 Commits

Author SHA1 Message Date
Piotr Osiewicz
9a2237134d Fix blocks being offset in project search 2024-04-02 20:10:40 +02:00
Piotr Osiewicz
6f1ead8a27 Fix scrolling at the top of the file 2024-04-02 15:10:49 +02:00
Piotr Osiewicz
5a19290b24 Merge branch 'main' into v2-editor-layout-floats 2024-04-02 14:43:19 +02:00
Piotr Osiewicz
05b6c2b60d WIP 2024-03-28 15:35:03 +01:00
Piotr Osiewicz
eec4796ffb Merge branch 'main' into v2-editor-layout-floats 2024-03-28 13:15:40 +01:00
Piotr Osiewicz
d6d04d311b Merge branch 'main' into v2-editor-layout-floats 2024-03-27 12:48:21 +01:00
Piotr Osiewicz
4d8d7a050c editor: fix glitchy layout with large \# of lines.
This is another attempt to fix #5371 - this time around I've exposed internal scroll_anchor used to calculate scroll_position()' y coordinate. Now, row offset calculations are performed on integrals.

This makes layout precision dependant on the \# of rendered lines and not on the \# of lines as a whole.
2024-03-25 16:29:11 +01:00
Piotr Osiewicz
e788f9700b Revert "editor: Rearrange float operations in layout (#9176)"
This reverts commit f9f9f0670f.
2024-03-25 16:29:11 +01:00
3 changed files with 109 additions and 69 deletions

View File

@@ -10286,6 +10286,10 @@ impl EditorSnapshot {
self.scroll_anchor.scroll_position(&self.display_snapshot)
}
pub(crate) fn scroll_anchor(&self) -> ScrollAnchor {
self.scroll_anchor
}
pub fn gutter_dimensions(
&self,
font_id: FontId,

View File

@@ -782,7 +782,7 @@ impl EditorElement {
content_origin: gpui::Point<Pixels>,
visible_anchor_range: Range<Anchor>,
visible_display_row_range: Range<u32>,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
line_height: Pixels,
line_layouts: &[LineWithInvisibles],
cx: &mut ElementContext,
@@ -803,7 +803,8 @@ impl EditorElement {
let start_x = content_origin.x
+ line_layout.x_for_index(display_range.start.column() as usize)
- scroll_pixel_position.x;
let start_y = content_origin.y + row as f32 * line_height - scroll_pixel_position.y;
let start_y =
content_origin.y + scroll_pixel_position.offset_for_row(row, line_height);
let end_x = content_origin.x
+ line_layout.x_for_index(display_range.end.column() as usize)
- scroll_pixel_position.x;
@@ -848,7 +849,7 @@ impl EditorElement {
line_layouts: &[LineWithInvisibles],
text_hitbox: &Hitbox,
content_origin: gpui::Point<Pixels>,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
line_height: Pixels,
em_width: Pixels,
cx: &mut ElementContext,
@@ -906,8 +907,8 @@ impl EditorElement {
};
let x = cursor_character_x - scroll_pixel_position.x;
let y = (cursor_position.row() as f32 - scroll_pixel_position.y / line_height)
* line_height;
let y =
scroll_pixel_position.offset_for_row(cursor_position.row(), line_height);
if selection.is_newest {
editor.pixel_position_of_newest_cursor = Some(point(
text_hitbox.origin.x + x + block_width / 2.,
@@ -941,7 +942,7 @@ impl EditorElement {
&self,
snapshot: &EditorSnapshot,
bounds: Bounds<Pixels>,
scroll_position: gpui::Point<f32>,
scroll_position: &ScrollPosition,
line_height: Pixels,
height_in_lines: f32,
cx: &mut ElementContext,
@@ -974,8 +975,8 @@ impl EditorElement {
return None;
}
let visible_row_range = scroll_position.y..scroll_position.y + height_in_lines;
let y_scroll_position = scroll_position.row as f32;
let visible_row_range = y_scroll_position..y_scroll_position + height_in_lines;
// If a drag took place after we started dragging the scrollbar,
// cancel the scrollbar drag.
if cx.has_active_drag() {
@@ -1020,7 +1021,7 @@ impl EditorElement {
line_height: Pixels,
gutter_dimensions: &GutterDimensions,
gutter_settings: crate::editor_settings::Gutter,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
gutter_hitbox: &Hitbox,
cx: &mut ElementContext,
) -> Vec<Option<AnyElement>> {
@@ -1043,10 +1044,9 @@ impl EditorElement {
AvailableSpace::Definite(line_height * 0.55),
);
let fold_indicator_size = fold_indicator.measure(available_space, cx);
let position = point(
gutter_dimensions.width - gutter_dimensions.right_padding,
ix as f32 * line_height - (scroll_pixel_position.y % line_height),
ix as f32 * line_height - scroll_pixel_position.y_offset,
);
let centering_offset = point(
(gutter_dimensions.right_padding + gutter_dimensions.margin
@@ -1149,7 +1149,7 @@ impl EditorElement {
&self,
line_height: Pixels,
newest_selection_head: DisplayPoint,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
gutter_dimensions: &GutterDimensions,
gutter_hitbox: &Hitbox,
cx: &mut ElementContext,
@@ -1181,7 +1181,8 @@ impl EditorElement {
- blame_width;
x += available_width / 2.;
let mut y = newest_selection_head.row() as f32 * line_height - scroll_pixel_position.y;
let mut y = scroll_pixel_position.offset_for_row(newest_selection_head.row(), line_height);
y += (line_height - indicator_size.height) / 2.;
button.layout(gutter_hitbox.origin + point(x, y), available_space, cx);
@@ -1737,14 +1738,14 @@ impl EditorElement {
blocks: &mut Vec<BlockLayout>,
hitbox: &Hitbox,
line_height: Pixels,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
cx: &mut ElementContext,
) {
for block in blocks {
let mut origin = hitbox.origin
+ point(
Pixels::ZERO,
block.row as f32 * line_height - scroll_pixel_position.y,
scroll_pixel_position.offset_for_row(block.row, line_height),
);
if !matches!(block.style, BlockStyle::Sticky) {
origin += point(-scroll_pixel_position.x, Pixels::ZERO);
@@ -1761,7 +1762,7 @@ impl EditorElement {
text_hitbox: &Hitbox,
content_origin: gpui::Point<Pixels>,
start_row: u32,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
line_layouts: &[LineWithInvisibles],
newest_selection_head: DisplayPoint,
cx: &mut ElementContext,
@@ -1785,7 +1786,7 @@ impl EditorElement {
let cursor_row_layout = &line_layouts[(position.row() - start_row) as usize].line;
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
let y = (position.row() + 1) as f32 * line_height - scroll_pixel_position.y;
let y = scroll_pixel_position.offset_for_row(position.row() + 1, line_height);
let mut list_origin = content_origin + point(x, y);
let list_width = context_menu_size.width;
let list_height = context_menu_size.height;
@@ -1828,7 +1829,7 @@ impl EditorElement {
text_hitbox: &Hitbox,
visible_display_row_range: Range<u32>,
content_origin: gpui::Point<Pixels>,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_pixel_position: &ScrollPosition,
line_layouts: &[LineWithInvisibles],
line_height: Pixels,
em_width: Pixels,
@@ -1872,7 +1873,7 @@ impl EditorElement {
// Compute Hovered Point
let x =
hovered_row_layout.x_for_index(position.column() as usize) - scroll_pixel_position.x;
let y = position.row() as f32 * line_height - scroll_pixel_position.y;
let y = scroll_pixel_position.offset_for_row(position.row(), line_height);
let hovered_point = content_origin + point(x, y);
let mut overall_height = Pixels::ZERO;
@@ -1940,7 +1941,7 @@ impl EditorElement {
fn paint_background(&self, layout: &EditorLayout, cx: &mut ElementContext) {
cx.paint_layer(layout.hitbox.bounds, |cx| {
let scroll_top = layout.position_map.snapshot.scroll_position().y;
let scroll_top = &layout.position_map.scroll_position;
let gutter_bg = cx.theme().colors().editor_gutter_background;
cx.paint_quad(fill(layout.gutter_hitbox.bounds, gutter_bg));
cx.paint_quad(fill(layout.text_hitbox.bounds, self.style.background));
@@ -1960,8 +1961,8 @@ impl EditorElement {
let origin = point(
layout.hitbox.origin.x,
layout.hitbox.origin.y
+ (*start_row as f32 - scroll_top)
* layout.position_map.line_height,
+ scroll_top
.offset_for_row(*start_row, layout.position_map.line_height),
);
let size = size(
layout.hitbox.size.width,
@@ -1977,8 +1978,10 @@ impl EditorElement {
let origin = point(
layout.hitbox.origin.x,
layout.hitbox.origin.y
+ (highlight_row_start as f32 - scroll_top)
* layout.position_map.line_height,
+ scroll_top.offset_for_row(
highlight_row_start,
layout.position_map.line_height,
),
);
let size = size(
layout.hitbox.size.width,
@@ -2062,8 +2065,7 @@ impl EditorElement {
fn paint_gutter(&mut self, layout: &mut EditorLayout, cx: &mut ElementContext) {
let line_height = layout.position_map.line_height;
let scroll_position = layout.position_map.snapshot.scroll_position();
let scroll_top = scroll_position.y * line_height;
let scroll_position = layout.position_map.snapshot.scroll_anchor();
cx.set_cursor_style(CursorStyle::Arrow, &layout.gutter_hitbox);
@@ -2087,7 +2089,7 @@ impl EditorElement {
layout.gutter_hitbox.size.width
- line.width
- layout.gutter_dimensions.right_padding,
ix as f32 * line_height - (scroll_top % line_height),
ix as f32 * line_height - scroll_position.offset.y * line_height,
);
line.paint(line_origin, line_height, cx).log_err();
@@ -2644,8 +2646,10 @@ impl EditorElement {
line_height: layout.position_map.line_height,
corner_radius,
start_y: layout.content_origin.y
+ row_range.start as f32 * layout.position_map.line_height
- layout.position_map.scroll_pixel_position.y,
+ layout
.position_map
.scroll_position
.offset_for_row(row_range.start, layout.position_map.line_height),
lines: row_range
.into_iter()
.map(|row| {
@@ -2655,18 +2659,17 @@ impl EditorElement {
start_x: if row == range.start.row() {
layout.content_origin.x
+ line_layout.x_for_index(range.start.column() as usize)
- layout.position_map.scroll_pixel_position.x
- layout.position_map.scroll_position.x
} else {
layout.content_origin.x
- layout.position_map.scroll_pixel_position.x
layout.content_origin.x - layout.position_map.scroll_position.x
},
end_x: if row == range.end.row() {
layout.content_origin.x
+ line_layout.x_for_index(range.end.column() as usize)
- layout.position_map.scroll_pixel_position.x
- layout.position_map.scroll_position.x
} else {
layout.content_origin.x + line_layout.width + line_end_overshoot
- layout.position_map.scroll_pixel_position.x
- layout.position_map.scroll_position.x
},
}
})
@@ -3179,12 +3182,14 @@ impl LineWithInvisibles {
cx: &mut ElementContext,
) {
let line_height = layout.position_map.line_height;
let line_y =
line_height * (row as f32 - layout.position_map.scroll_pixel_position.y / line_height);
let line_y = layout
.position_map
.scroll_position
.offset_for_row(row, line_height);
self.line
.paint(
content_origin + gpui::point(-layout.position_map.scroll_pixel_position.x, line_y),
content_origin + gpui::point(-layout.position_map.scroll_position.x, line_y),
line_height,
cx,
)
@@ -3231,7 +3236,7 @@ impl LineWithInvisibles {
(layout.position_map.em_width - invisible_symbol.width).max(Pixels::ZERO) / 2.0;
let origin = content_origin
+ gpui::point(
x_offset + invisible_offset - layout.position_map.scroll_pixel_position.x,
x_offset + invisible_offset - layout.position_map.scroll_position.x,
line_y,
);
@@ -3401,16 +3406,16 @@ impl Element for EditorElement {
autoscroll_horizontally
});
let mut scroll_position = snapshot.scroll_position();
let mut scroll_position = snapshot.scroll_anchor();
// The scroll position is a fractional point, the whole number of which represents
// the top of the window in terms of display rows.
let start_row = scroll_position.y as u32;
let start_row = scroll_position.top_row_display(&snapshot.display_snapshot);
let height_in_lines = bounds.size.height / line_height;
let max_row = snapshot.max_point().row();
// Add 1 to ensure selections bleed off screen
let end_row =
1 + cmp::min((scroll_position.y + height_in_lines).ceil() as u32, max_row);
1 + cmp::min((start_row as f32 + height_in_lines).ceil() as u32, max_row);
let buffer_rows = snapshot
.buffer_rows(start_row)
@@ -3469,7 +3474,7 @@ impl Element for EditorElement {
let blamed_display_rows = self.layout_blame_entries(
buffer_rows,
em_width,
scroll_position,
scroll_position.scroll_position(&snapshot.display_snapshot),
line_height,
&gutter_hitbox,
gutter_dimensions.git_blame_entries_width,
@@ -3527,23 +3532,22 @@ impl Element for EditorElement {
if clamped || autoscrolled {
snapshot = editor.snapshot(cx);
scroll_position = snapshot.scroll_position();
scroll_position = snapshot.scroll_anchor();
}
});
let scroll_pixel_position = point(
scroll_position.x * em_width,
scroll_position.y * line_height,
);
let y_offset = if scroll_position.anchor != Anchor::min() {
scroll_position.offset.y * line_height
} else {
px(0.)
};
let scroll_position = ScrollPosition {
x: scroll_position.offset.x * em_width,
row: scroll_position.top_row_display_raw(&snapshot.display_snapshot),
y_offset,
};
cx.with_element_id(Some("blocks"), |cx| {
self.layout_blocks(
&mut blocks,
&hitbox,
line_height,
scroll_pixel_position,
cx,
);
self.layout_blocks(&mut blocks, &hitbox, line_height, &scroll_position, cx);
});
let cursors = self.layout_cursors(
@@ -3553,7 +3557,7 @@ impl Element for EditorElement {
&line_layouts,
&text_hitbox,
content_origin,
scroll_pixel_position,
&scroll_position,
line_height,
em_width,
cx,
@@ -3562,7 +3566,7 @@ impl Element for EditorElement {
let scrollbar_layout = self.layout_scrollbar(
&snapshot,
bounds,
scroll_position,
&scroll_position,
line_height,
height_in_lines,
cx,
@@ -3574,7 +3578,7 @@ impl Element for EditorElement {
content_origin,
start_anchor..end_anchor,
start_row..end_row,
scroll_pixel_position,
&scroll_position,
line_height,
&line_layouts,
cx,
@@ -3593,7 +3597,7 @@ impl Element for EditorElement {
&text_hitbox,
content_origin,
start_row,
scroll_pixel_position,
&scroll_position,
&line_layouts,
newest_selection_head,
cx,
@@ -3602,7 +3606,7 @@ impl Element for EditorElement {
code_actions_indicator = self.layout_code_actions_indicator(
line_height,
newest_selection_head,
scroll_pixel_position,
&scroll_position,
&gutter_dimensions,
&gutter_hitbox,
cx,
@@ -3618,7 +3622,7 @@ impl Element for EditorElement {
&text_hitbox,
start_row..end_row,
content_origin,
scroll_pixel_position,
&scroll_position,
&line_layouts,
line_height,
em_width,
@@ -3635,7 +3639,7 @@ impl Element for EditorElement {
line_height,
&gutter_dimensions,
gutter_settings,
scroll_pixel_position,
&scroll_position,
&gutter_hitbox,
cx,
)
@@ -3680,7 +3684,7 @@ impl Element for EditorElement {
mode: snapshot.mode,
position_map: Arc::new(PositionMap {
size: bounds.size,
scroll_pixel_position,
scroll_position,
scroll_max,
line_layouts,
line_height,
@@ -3853,10 +3857,25 @@ struct FoldLayout {
hover_element: AnyElement,
}
struct ScrollPosition {
/// 0-based index of the first fully-displayed row.
row: u32,
/// Pixel height of the non-full line preceding the top row.
y_offset: Pixels,
x: Pixels,
}
impl ScrollPosition {
fn offset_for_row(&self, row: u32, line_height: Pixels) -> Pixels {
let extended: i64 = row as _;
(extended - self.row as i64) as f32 * line_height - self.y_offset
}
}
struct PositionMap {
size: Size<Pixels>,
line_height: Pixels,
scroll_pixel_position: gpui::Point<Pixels>,
scroll_position: ScrollPosition,
scroll_max: gpui::Point<f32>,
em_width: Pixels,
em_advance: Pixels,
@@ -3888,15 +3907,15 @@ impl PositionMap {
text_bounds: Bounds<Pixels>,
position: gpui::Point<Pixels>,
) -> PointForPosition {
let scroll_position = self.snapshot.scroll_position();
let scroll_position = self.snapshot.scroll_anchor();
let position = position - text_bounds.origin;
let y = position.y.max(px(0.)).min(self.size.height);
let x = position.x + (scroll_position.x * self.em_width);
let row = ((y / self.line_height) + scroll_position.y) as u32;
let x = position.x + (scroll_position.offset.x * self.em_width);
let top_row = scroll_position.top_row_display(&self.snapshot.display_snapshot);
let row = (y / self.line_height) as u32 + top_row;
let (column, x_overshoot_after_line_end) = if let Some(line) = self
.line_layouts
.get(row as usize - scroll_position.y as usize)
.get((row - top_row) as usize)
.map(|LineWithInvisibles { line, .. }| line)
{
if let Some(ix) = line.index_for_x(x) {

View File

@@ -43,6 +43,23 @@ impl ScrollAnchor {
}
}
pub(crate) fn top_row_display_raw(&self, snapshot: &DisplaySnapshot) -> u32 {
if self.anchor != Anchor::min() {
self.anchor.to_display_point(snapshot).row()
} else {
0
}
}
pub(crate) fn top_row_display(&self, snapshot: &DisplaySnapshot) -> u32 {
if self.anchor != Anchor::min() {
let row: i64 = self.anchor.to_display_point(snapshot).row() as _;
let offset_integral = self.offset.y.floor() as i64;
(row + offset_integral) as u32
} else {
0
}
}
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<f32> {
let mut scroll_position = self.offset;
if self.anchor != Anchor::min() {