mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 05:15:00 +00:00
Add scrollbars to editors
This commit is contained in:
parent
e96abf1429
commit
eedcc585af
3 changed files with 130 additions and 10 deletions
|
@ -43,7 +43,7 @@ use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
iter,
|
iter,
|
||||||
ops::Range,
|
ops::{DerefMut, Range},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use theme::DiffStyle;
|
use theme::DiffStyle;
|
||||||
|
@ -909,6 +909,96 @@ impl EditorElement {
|
||||||
cx.scene.pop_layer();
|
cx.scene.pop_layer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn paint_scrollbar(&mut self, bounds: RectF, layout: &mut LayoutState, cx: &mut PaintContext) {
|
||||||
|
enum ScrollbarMouseHandlers {}
|
||||||
|
let row_range = if let Some(row_range) = &layout.scrollbar_row_range {
|
||||||
|
row_range
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let style = &self.style.theme.scrollbar;
|
||||||
|
let top = bounds.min_y();
|
||||||
|
let bottom = bounds.max_y();
|
||||||
|
let height = bounds.height();
|
||||||
|
|
||||||
|
let max_row = layout.max_row + row_range.len() as u32;
|
||||||
|
let scrollbar_start = row_range.start as f32 / max_row as f32;
|
||||||
|
let scrollbar_end = row_range.end as f32 / max_row as f32;
|
||||||
|
|
||||||
|
let thumb_top = top + scrollbar_start * height;
|
||||||
|
let thumb_bottom = top + scrollbar_end * height;
|
||||||
|
let right = bounds.max_x();
|
||||||
|
let left = right - style.width;
|
||||||
|
let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom));
|
||||||
|
let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
|
||||||
|
|
||||||
|
cx.scene.push_quad(Quad {
|
||||||
|
bounds: track_bounds,
|
||||||
|
border: style.track.border,
|
||||||
|
background: style.track.background_color,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
cx.scene.push_quad(Quad {
|
||||||
|
bounds: thumb_bounds,
|
||||||
|
border: style.thumb.border,
|
||||||
|
background: style.thumb.background_color,
|
||||||
|
corner_radius: style.thumb.corner_radius,
|
||||||
|
});
|
||||||
|
cx.scene.push_cursor_region(CursorRegion {
|
||||||
|
bounds: track_bounds,
|
||||||
|
style: CursorStyle::Arrow,
|
||||||
|
});
|
||||||
|
|
||||||
|
let view = self.view.clone();
|
||||||
|
cx.scene.push_mouse_region(
|
||||||
|
MouseRegion::new::<ScrollbarMouseHandlers>(
|
||||||
|
self.view.id(),
|
||||||
|
self.view.id(),
|
||||||
|
track_bounds,
|
||||||
|
)
|
||||||
|
.on_down(MouseButton::Left, {
|
||||||
|
let view = view.clone();
|
||||||
|
let row_range_len = row_range.len();
|
||||||
|
move |e, cx| {
|
||||||
|
let y = e.position.y();
|
||||||
|
if y < thumb_top || thumb_bottom < y {
|
||||||
|
if let Some(view) = view.upgrade(cx.deref_mut()) {
|
||||||
|
view.update(cx.deref_mut(), |view, cx| {
|
||||||
|
let center_row =
|
||||||
|
((y - top) * max_row as f32 / height).round() as u32;
|
||||||
|
let top_row = center_row.saturating_sub(row_range_len as u32 / 2);
|
||||||
|
let mut position = view.scroll_position(cx);
|
||||||
|
position.set_y(top_row as f32);
|
||||||
|
view.set_scroll_position(position, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on_drag(MouseButton::Left, {
|
||||||
|
let view = view.clone();
|
||||||
|
move |e, cx| {
|
||||||
|
let y = e.prev_mouse_position.y();
|
||||||
|
let new_y = e.position.y();
|
||||||
|
if thumb_top < y && y < thumb_bottom {
|
||||||
|
if let Some(view) = view.upgrade(cx.deref_mut()) {
|
||||||
|
view.update(cx.deref_mut(), |view, cx| {
|
||||||
|
let mut position = view.scroll_position(cx);
|
||||||
|
position
|
||||||
|
.set_y(position.y() + (new_y - y) * (max_row as f32) / height);
|
||||||
|
if position.y() < 0.0 {
|
||||||
|
position.set_y(0.);
|
||||||
|
}
|
||||||
|
view.set_scroll_position(position, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn paint_highlighted_range(
|
fn paint_highlighted_range(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1467,13 +1557,11 @@ impl Element for EditorElement {
|
||||||
// The scroll position is a fractional point, the whole number of which represents
|
// The scroll position is a fractional point, the whole number of which represents
|
||||||
// the top of the window in terms of display rows.
|
// the top of the window in terms of display rows.
|
||||||
let start_row = scroll_position.y() as u32;
|
let start_row = scroll_position.y() as u32;
|
||||||
let scroll_top = scroll_position.y() * line_height;
|
let visible_row_count = (size.y() / line_height).ceil() as u32;
|
||||||
|
let max_row = snapshot.max_point().row();
|
||||||
|
|
||||||
// Add 1 to ensure selections bleed off screen
|
// Add 1 to ensure selections bleed off screen
|
||||||
let end_row = 1 + cmp::min(
|
let end_row = 1 + cmp::min(start_row + visible_row_count, max_row);
|
||||||
((scroll_top + size.y()) / line_height).ceil() as u32,
|
|
||||||
snapshot.max_point().row(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let start_anchor = if start_row == 0 {
|
let start_anchor = if start_row == 0 {
|
||||||
Anchor::min()
|
Anchor::min()
|
||||||
|
@ -1482,7 +1570,7 @@ impl Element for EditorElement {
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
|
.anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left))
|
||||||
};
|
};
|
||||||
let end_anchor = if end_row > snapshot.max_point().row() {
|
let end_anchor = if end_row > max_row {
|
||||||
Anchor::max()
|
Anchor::max()
|
||||||
} else {
|
} else {
|
||||||
snapshot
|
snapshot
|
||||||
|
@ -1564,6 +1652,12 @@ impl Element for EditorElement {
|
||||||
.git_diff_hunks_in_range(start_row..end_row)
|
.git_diff_hunks_in_range(start_row..end_row)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let scrollbar_row_range = if snapshot.mode == EditorMode::Full {
|
||||||
|
Some(start_row..(start_row + visible_row_count))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut max_visible_line_width = 0.0;
|
let mut max_visible_line_width = 0.0;
|
||||||
let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
|
let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx);
|
||||||
for line in &line_layouts {
|
for line in &line_layouts {
|
||||||
|
@ -1597,7 +1691,6 @@ impl Element for EditorElement {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
let max_row = snapshot.max_point().row();
|
|
||||||
let scroll_max = vec2f(
|
let scroll_max = vec2f(
|
||||||
((scroll_width - text_size.x()) / em_width).max(0.0),
|
((scroll_width - text_size.x()) / em_width).max(0.0),
|
||||||
max_row.saturating_sub(1) as f32,
|
max_row.saturating_sub(1) as f32,
|
||||||
|
@ -1707,6 +1800,8 @@ impl Element for EditorElement {
|
||||||
gutter_size,
|
gutter_size,
|
||||||
gutter_padding,
|
gutter_padding,
|
||||||
text_size,
|
text_size,
|
||||||
|
scrollbar_row_range,
|
||||||
|
max_row,
|
||||||
gutter_margin,
|
gutter_margin,
|
||||||
active_rows,
|
active_rows,
|
||||||
highlighted_rows,
|
highlighted_rows,
|
||||||
|
@ -1753,11 +1848,12 @@ impl Element for EditorElement {
|
||||||
}
|
}
|
||||||
self.paint_text(text_bounds, visible_bounds, layout, cx);
|
self.paint_text(text_bounds, visible_bounds, layout, cx);
|
||||||
|
|
||||||
if !layout.blocks.is_empty() {
|
|
||||||
cx.scene.push_layer(Some(bounds));
|
cx.scene.push_layer(Some(bounds));
|
||||||
|
if !layout.blocks.is_empty() {
|
||||||
self.paint_blocks(bounds, visible_bounds, layout, cx);
|
self.paint_blocks(bounds, visible_bounds, layout, cx);
|
||||||
cx.scene.pop_layer();
|
|
||||||
}
|
}
|
||||||
|
self.paint_scrollbar(bounds, layout, cx);
|
||||||
|
cx.scene.pop_layer();
|
||||||
|
|
||||||
cx.scene.pop_layer();
|
cx.scene.pop_layer();
|
||||||
}
|
}
|
||||||
|
@ -1849,6 +1945,8 @@ pub struct LayoutState {
|
||||||
blocks: Vec<BlockLayout>,
|
blocks: Vec<BlockLayout>,
|
||||||
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
||||||
selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
|
selections: Vec<(ReplicaId, Vec<SelectionLayout>)>,
|
||||||
|
scrollbar_row_range: Option<Range<u32>>,
|
||||||
|
max_row: u32,
|
||||||
context_menu: Option<(DisplayPoint, ElementBox)>,
|
context_menu: Option<(DisplayPoint, ElementBox)>,
|
||||||
diff_hunks: Vec<DiffHunk<u32>>,
|
diff_hunks: Vec<DiffHunk<u32>>,
|
||||||
code_actions_indicator: Option<(u32, ElementBox)>,
|
code_actions_indicator: Option<(u32, ElementBox)>,
|
||||||
|
|
|
@ -510,6 +510,14 @@ pub struct Editor {
|
||||||
pub link_definition: HighlightStyle,
|
pub link_definition: HighlightStyle,
|
||||||
pub composition_mark: HighlightStyle,
|
pub composition_mark: HighlightStyle,
|
||||||
pub jump_icon: Interactive<IconButton>,
|
pub jump_icon: Interactive<IconButton>,
|
||||||
|
pub scrollbar: Scrollbar,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
pub struct Scrollbar {
|
||||||
|
pub track: ContainerStyle,
|
||||||
|
pub thumb: ContainerStyle,
|
||||||
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import Theme from "../themes/common/theme";
|
import Theme from "../themes/common/theme";
|
||||||
|
import { withOpacity } from "../utils/color";
|
||||||
import {
|
import {
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
border,
|
border,
|
||||||
|
@ -170,6 +171,19 @@ export default function editor(theme: Theme) {
|
||||||
background: backgroundColor(theme, "on500"),
|
background: backgroundColor(theme, "on500"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
scrollbar: {
|
||||||
|
width: 12,
|
||||||
|
track: {
|
||||||
|
border: {
|
||||||
|
left: true,
|
||||||
|
width: 1,
|
||||||
|
color: borderColor(theme, "secondary"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
thumb: {
|
||||||
|
background: borderColor(theme, "secondary"),
|
||||||
|
}
|
||||||
|
},
|
||||||
compositionMark: {
|
compositionMark: {
|
||||||
underline: {
|
underline: {
|
||||||
thickness: 1.0,
|
thickness: 1.0,
|
||||||
|
|
Loading…
Reference in a new issue