Compare commits
5 commits
main
...
bounds-tre
Author | SHA1 | Date | |
---|---|---|---|
![]() |
74ed2f7f70 | ||
![]() |
eba5db3a6e | ||
![]() |
cd6bdd8b1c | ||
![]() |
a3ce933b04 | ||
![]() |
816c48b7d6 |
9 changed files with 944 additions and 547 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -8548,9 +8548,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
|
|
|
@ -727,6 +727,51 @@ impl EditorElement {
|
||||||
|
|
||||||
let gutter_settings = EditorSettings::get_global(cx).gutter;
|
let gutter_settings = EditorSettings::get_global(cx).gutter;
|
||||||
|
|
||||||
|
if let Some(indicator) = layout.code_actions_indicator.take() {
|
||||||
|
debug_assert!(gutter_settings.code_actions);
|
||||||
|
let mut button = indicator.button.into_any_element();
|
||||||
|
let available_space = size(
|
||||||
|
AvailableSpace::MinContent,
|
||||||
|
AvailableSpace::Definite(line_height),
|
||||||
|
);
|
||||||
|
let indicator_size = button.measure(available_space, cx);
|
||||||
|
|
||||||
|
let mut x = Pixels::ZERO;
|
||||||
|
let mut y = indicator.row as f32 * line_height - scroll_top;
|
||||||
|
// Center indicator.
|
||||||
|
x += (layout.gutter_dimensions.margin + layout.gutter_dimensions.left_padding
|
||||||
|
- indicator_size.width)
|
||||||
|
/ 2.;
|
||||||
|
y += (line_height - indicator_size.height) / 2.;
|
||||||
|
|
||||||
|
button.draw(bounds.origin + point(x, y), available_space, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
|
||||||
|
if let Some(fold_indicator) = fold_indicator {
|
||||||
|
debug_assert!(gutter_settings.folds);
|
||||||
|
let mut fold_indicator = fold_indicator.into_any_element();
|
||||||
|
let available_space = size(
|
||||||
|
AvailableSpace::MinContent,
|
||||||
|
AvailableSpace::Definite(line_height * 0.55),
|
||||||
|
);
|
||||||
|
let fold_indicator_size = fold_indicator.measure(available_space, cx);
|
||||||
|
|
||||||
|
let position = point(
|
||||||
|
bounds.size.width - layout.gutter_dimensions.right_padding,
|
||||||
|
ix as f32 * line_height - (scroll_top % line_height),
|
||||||
|
);
|
||||||
|
let centering_offset = point(
|
||||||
|
(layout.gutter_dimensions.right_padding + layout.gutter_dimensions.margin
|
||||||
|
- fold_indicator_size.width)
|
||||||
|
/ 2.,
|
||||||
|
(line_height - fold_indicator_size.height) / 2.,
|
||||||
|
);
|
||||||
|
let origin = bounds.origin + position + centering_offset;
|
||||||
|
fold_indicator.draw(origin, available_space, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (ix, line) in layout.line_numbers.iter().enumerate() {
|
for (ix, line) in layout.line_numbers.iter().enumerate() {
|
||||||
if let Some(line) = line {
|
if let Some(line) = line {
|
||||||
let line_origin = bounds.origin
|
let line_origin = bounds.origin
|
||||||
|
@ -738,53 +783,6 @@ impl EditorElement {
|
||||||
line.paint(line_origin, line_height, cx).log_err();
|
line.paint(line_origin, line_height, cx).log_err();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.with_z_index(1, |cx| {
|
|
||||||
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
|
|
||||||
if let Some(fold_indicator) = fold_indicator {
|
|
||||||
debug_assert!(gutter_settings.folds);
|
|
||||||
let mut fold_indicator = fold_indicator.into_any_element();
|
|
||||||
let available_space = size(
|
|
||||||
AvailableSpace::MinContent,
|
|
||||||
AvailableSpace::Definite(line_height * 0.55),
|
|
||||||
);
|
|
||||||
let fold_indicator_size = fold_indicator.measure(available_space, cx);
|
|
||||||
|
|
||||||
let position = point(
|
|
||||||
bounds.size.width - layout.gutter_dimensions.right_padding,
|
|
||||||
ix as f32 * line_height - (scroll_top % line_height),
|
|
||||||
);
|
|
||||||
let centering_offset = point(
|
|
||||||
(layout.gutter_dimensions.right_padding + layout.gutter_dimensions.margin
|
|
||||||
- fold_indicator_size.width)
|
|
||||||
/ 2.,
|
|
||||||
(line_height - fold_indicator_size.height) / 2.,
|
|
||||||
);
|
|
||||||
let origin = bounds.origin + position + centering_offset;
|
|
||||||
fold_indicator.draw(origin, available_space, cx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(indicator) = layout.code_actions_indicator.take() {
|
|
||||||
debug_assert!(gutter_settings.code_actions);
|
|
||||||
let mut button = indicator.button.into_any_element();
|
|
||||||
let available_space = size(
|
|
||||||
AvailableSpace::MinContent,
|
|
||||||
AvailableSpace::Definite(line_height),
|
|
||||||
);
|
|
||||||
let indicator_size = button.measure(available_space, cx);
|
|
||||||
|
|
||||||
let mut x = Pixels::ZERO;
|
|
||||||
let mut y = indicator.row as f32 * line_height - scroll_top;
|
|
||||||
// Center indicator.
|
|
||||||
x += (layout.gutter_dimensions.margin + layout.gutter_dimensions.left_padding
|
|
||||||
- indicator_size.width)
|
|
||||||
/ 2.;
|
|
||||||
y += (line_height - indicator_size.height) / 2.;
|
|
||||||
|
|
||||||
button.draw(bounds.origin + point(x, y), available_space, cx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
|
fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut ElementContext) {
|
||||||
|
@ -928,6 +926,153 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
|
||||||
|
for (participant_ix, (player_color, selections)) in
|
||||||
|
layout.selections.iter().enumerate()
|
||||||
|
{
|
||||||
|
for selection in selections {
|
||||||
|
if selection.is_local && !selection.range.is_empty() {
|
||||||
|
invisible_display_ranges.push(selection.range.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
|
||||||
|
let cursor_position = selection.head;
|
||||||
|
if layout
|
||||||
|
.visible_display_row_range
|
||||||
|
.contains(&cursor_position.row())
|
||||||
|
{
|
||||||
|
let cursor_row_layout = &layout.position_map.line_layouts
|
||||||
|
[(cursor_position.row() - start_row) as usize]
|
||||||
|
.line;
|
||||||
|
let cursor_column = cursor_position.column() as usize;
|
||||||
|
|
||||||
|
let cursor_character_x =
|
||||||
|
cursor_row_layout.x_for_index(cursor_column);
|
||||||
|
let mut block_width = cursor_row_layout
|
||||||
|
.x_for_index(cursor_column + 1)
|
||||||
|
- cursor_character_x;
|
||||||
|
if block_width == Pixels::ZERO {
|
||||||
|
block_width = layout.position_map.em_width;
|
||||||
|
}
|
||||||
|
let block_text = if let CursorShape::Block = selection.cursor_shape
|
||||||
|
{
|
||||||
|
layout
|
||||||
|
.position_map
|
||||||
|
.snapshot
|
||||||
|
.chars_at(cursor_position)
|
||||||
|
.next()
|
||||||
|
.and_then(|(character, _)| {
|
||||||
|
let text = if character == '\n' {
|
||||||
|
SharedString::from(" ")
|
||||||
|
} else {
|
||||||
|
SharedString::from(character.to_string())
|
||||||
|
};
|
||||||
|
let len = text.len();
|
||||||
|
cx.text_system()
|
||||||
|
.shape_line(
|
||||||
|
text,
|
||||||
|
cursor_row_layout.font_size,
|
||||||
|
&[TextRun {
|
||||||
|
len,
|
||||||
|
font: self.style.text.font(),
|
||||||
|
color: self.style.background,
|
||||||
|
background_color: None,
|
||||||
|
strikethrough: None,
|
||||||
|
underline: None,
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.log_err()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let x = cursor_character_x - layout.position_map.scroll_position.x;
|
||||||
|
let y = cursor_position.row() as f32
|
||||||
|
* layout.position_map.line_height
|
||||||
|
- layout.position_map.scroll_position.y;
|
||||||
|
if selection.is_newest {
|
||||||
|
self.editor.update(cx, |editor, _| {
|
||||||
|
editor.pixel_position_of_newest_cursor = Some(point(
|
||||||
|
text_bounds.origin.x + x + block_width / 2.,
|
||||||
|
text_bounds.origin.y
|
||||||
|
+ y
|
||||||
|
+ layout.position_map.line_height / 2.,
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursor = Cursor {
|
||||||
|
color: player_color.cursor,
|
||||||
|
block_width,
|
||||||
|
origin: point(x, y),
|
||||||
|
line_height: layout.position_map.line_height,
|
||||||
|
shape: selection.cursor_shape,
|
||||||
|
block_text,
|
||||||
|
cursor_name: selection.user_name.clone().map(|name| {
|
||||||
|
CursorName {
|
||||||
|
string: name,
|
||||||
|
color: self.style.background,
|
||||||
|
is_top_row: cursor_position.row() == 0,
|
||||||
|
z_index: (participant_ix % 256).try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
cursor.paint(content_origin, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.paint_redactions(text_bounds, &layout, cx);
|
||||||
|
|
||||||
|
for (ix, line_with_invisibles) in
|
||||||
|
layout.position_map.line_layouts.iter().enumerate()
|
||||||
|
{
|
||||||
|
let row = start_row + ix as u32;
|
||||||
|
line_with_invisibles.draw(
|
||||||
|
layout,
|
||||||
|
row,
|
||||||
|
content_origin,
|
||||||
|
whitespace_setting,
|
||||||
|
&invisible_display_ranges,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let corner_radius = 0.15 * layout.position_map.line_height;
|
||||||
|
for (player_color, selections) in &layout.selections {
|
||||||
|
for selection in selections.into_iter() {
|
||||||
|
self.paint_highlighted_range(
|
||||||
|
selection.range.clone(),
|
||||||
|
player_color.selection,
|
||||||
|
corner_radius,
|
||||||
|
corner_radius * 2.,
|
||||||
|
layout,
|
||||||
|
content_origin,
|
||||||
|
text_bounds,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if selection.is_local && !selection.range.is_empty() {
|
||||||
|
invisible_display_ranges.push(selection.range.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (range, color) in &layout.highlighted_ranges {
|
||||||
|
self.paint_highlighted_range(
|
||||||
|
range.clone(),
|
||||||
|
*color,
|
||||||
|
Pixels::ZERO,
|
||||||
|
line_end_overshoot,
|
||||||
|
layout,
|
||||||
|
content_origin,
|
||||||
|
text_bounds,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let fold_corner_radius = 0.15 * layout.position_map.line_height;
|
let fold_corner_radius = 0.15 * layout.position_map.line_height;
|
||||||
cx.with_element_id(Some("folds"), |cx| {
|
cx.with_element_id(Some("folds"), |cx| {
|
||||||
let snapshot = &layout.position_map.snapshot;
|
let snapshot = &layout.position_map.snapshot;
|
||||||
|
@ -1008,152 +1153,6 @@ impl EditorElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (range, color) in &layout.highlighted_ranges {
|
|
||||||
self.paint_highlighted_range(
|
|
||||||
range.clone(),
|
|
||||||
*color,
|
|
||||||
Pixels::ZERO,
|
|
||||||
line_end_overshoot,
|
|
||||||
layout,
|
|
||||||
content_origin,
|
|
||||||
text_bounds,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cursors = SmallVec::<[Cursor; 32]>::new();
|
|
||||||
let corner_radius = 0.15 * layout.position_map.line_height;
|
|
||||||
let mut invisible_display_ranges = SmallVec::<[Range<DisplayPoint>; 32]>::new();
|
|
||||||
|
|
||||||
for (participant_ix, (player_color, selections)) in
|
|
||||||
layout.selections.iter().enumerate()
|
|
||||||
{
|
|
||||||
for selection in selections.into_iter() {
|
|
||||||
self.paint_highlighted_range(
|
|
||||||
selection.range.clone(),
|
|
||||||
player_color.selection,
|
|
||||||
corner_radius,
|
|
||||||
corner_radius * 2.,
|
|
||||||
layout,
|
|
||||||
content_origin,
|
|
||||||
text_bounds,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
|
|
||||||
if selection.is_local && !selection.range.is_empty() {
|
|
||||||
invisible_display_ranges.push(selection.range.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !selection.is_local || self.editor.read(cx).show_local_cursors(cx) {
|
|
||||||
let cursor_position = selection.head;
|
|
||||||
if layout
|
|
||||||
.visible_display_row_range
|
|
||||||
.contains(&cursor_position.row())
|
|
||||||
{
|
|
||||||
let cursor_row_layout = &layout.position_map.line_layouts
|
|
||||||
[(cursor_position.row() - start_row) as usize]
|
|
||||||
.line;
|
|
||||||
let cursor_column = cursor_position.column() as usize;
|
|
||||||
|
|
||||||
let cursor_character_x =
|
|
||||||
cursor_row_layout.x_for_index(cursor_column);
|
|
||||||
let mut block_width = cursor_row_layout
|
|
||||||
.x_for_index(cursor_column + 1)
|
|
||||||
- cursor_character_x;
|
|
||||||
if block_width == Pixels::ZERO {
|
|
||||||
block_width = layout.position_map.em_width;
|
|
||||||
}
|
|
||||||
let block_text = if let CursorShape::Block = selection.cursor_shape
|
|
||||||
{
|
|
||||||
layout
|
|
||||||
.position_map
|
|
||||||
.snapshot
|
|
||||||
.chars_at(cursor_position)
|
|
||||||
.next()
|
|
||||||
.and_then(|(character, _)| {
|
|
||||||
let text = if character == '\n' {
|
|
||||||
SharedString::from(" ")
|
|
||||||
} else {
|
|
||||||
SharedString::from(character.to_string())
|
|
||||||
};
|
|
||||||
let len = text.len();
|
|
||||||
cx.text_system()
|
|
||||||
.shape_line(
|
|
||||||
text,
|
|
||||||
cursor_row_layout.font_size,
|
|
||||||
&[TextRun {
|
|
||||||
len,
|
|
||||||
font: self.style.text.font(),
|
|
||||||
color: self.style.background,
|
|
||||||
background_color: None,
|
|
||||||
strikethrough: None,
|
|
||||||
underline: None,
|
|
||||||
}],
|
|
||||||
)
|
|
||||||
.log_err()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let x = cursor_character_x - layout.position_map.scroll_position.x;
|
|
||||||
let y = cursor_position.row() as f32
|
|
||||||
* layout.position_map.line_height
|
|
||||||
- layout.position_map.scroll_position.y;
|
|
||||||
if selection.is_newest {
|
|
||||||
self.editor.update(cx, |editor, _| {
|
|
||||||
editor.pixel_position_of_newest_cursor = Some(point(
|
|
||||||
text_bounds.origin.x + x + block_width / 2.,
|
|
||||||
text_bounds.origin.y
|
|
||||||
+ y
|
|
||||||
+ layout.position_map.line_height / 2.,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cursors.push(Cursor {
|
|
||||||
color: player_color.cursor,
|
|
||||||
block_width,
|
|
||||||
origin: point(x, y),
|
|
||||||
line_height: layout.position_map.line_height,
|
|
||||||
shape: selection.cursor_shape,
|
|
||||||
block_text,
|
|
||||||
cursor_name: selection.user_name.clone().map(|name| {
|
|
||||||
CursorName {
|
|
||||||
string: name,
|
|
||||||
color: self.style.background,
|
|
||||||
is_top_row: cursor_position.row() == 0,
|
|
||||||
z_index: (participant_ix % 256).try_into().unwrap(),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ix, line_with_invisibles) in
|
|
||||||
layout.position_map.line_layouts.iter().enumerate()
|
|
||||||
{
|
|
||||||
let row = start_row + ix as u32;
|
|
||||||
line_with_invisibles.draw(
|
|
||||||
layout,
|
|
||||||
row,
|
|
||||||
content_origin,
|
|
||||||
whitespace_setting,
|
|
||||||
&invisible_display_ranges,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.with_z_index(0, |cx| self.paint_redactions(text_bounds, &layout, cx));
|
|
||||||
|
|
||||||
cx.with_z_index(1, |cx| {
|
|
||||||
for cursor in cursors {
|
|
||||||
cursor.paint(content_origin, cx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1346,132 +1345,20 @@ impl EditorElement {
|
||||||
let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
|
let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
|
||||||
|
|
||||||
if layout.show_scrollbars {
|
if layout.show_scrollbars {
|
||||||
|
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
|
||||||
|
|
||||||
cx.paint_quad(quad(
|
cx.paint_quad(quad(
|
||||||
track_bounds,
|
thumb_bounds,
|
||||||
Corners::default(),
|
Corners::default(),
|
||||||
cx.theme().colors().scrollbar_track_background,
|
cx.theme().colors().scrollbar_thumb_background,
|
||||||
Edges {
|
Edges {
|
||||||
top: Pixels::ZERO,
|
top: Pixels::ZERO,
|
||||||
right: Pixels::ZERO,
|
right: px(1.),
|
||||||
bottom: Pixels::ZERO,
|
bottom: Pixels::ZERO,
|
||||||
left: px(1.),
|
left: px(1.),
|
||||||
},
|
},
|
||||||
cx.theme().colors().scrollbar_track_border,
|
cx.theme().colors().scrollbar_thumb_border,
|
||||||
));
|
));
|
||||||
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
|
|
||||||
if layout.is_singleton && scrollbar_settings.selections {
|
|
||||||
let start_anchor = Anchor::min();
|
|
||||||
let end_anchor = Anchor::max();
|
|
||||||
let background_ranges = self
|
|
||||||
.editor
|
|
||||||
.read(cx)
|
|
||||||
.background_highlight_row_ranges::<BufferSearchHighlights>(
|
|
||||||
start_anchor..end_anchor,
|
|
||||||
&layout.position_map.snapshot,
|
|
||||||
50000,
|
|
||||||
);
|
|
||||||
for range in background_ranges {
|
|
||||||
let start_y = y_for_row(range.start().row() as f32);
|
|
||||||
let mut end_y = y_for_row(range.end().row() as f32);
|
|
||||||
if end_y - start_y < px(1.) {
|
|
||||||
end_y = start_y + px(1.);
|
|
||||||
}
|
|
||||||
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
|
||||||
cx.paint_quad(quad(
|
|
||||||
bounds,
|
|
||||||
Corners::default(),
|
|
||||||
cx.theme().status().info,
|
|
||||||
Edges {
|
|
||||||
top: Pixels::ZERO,
|
|
||||||
right: px(1.),
|
|
||||||
bottom: Pixels::ZERO,
|
|
||||||
left: px(1.),
|
|
||||||
},
|
|
||||||
cx.theme().colors().scrollbar_thumb_border,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if layout.is_singleton && scrollbar_settings.symbols_selections {
|
|
||||||
let selection_ranges = self.editor.read(cx).background_highlights_in_range(
|
|
||||||
Anchor::min()..Anchor::max(),
|
|
||||||
&layout.position_map.snapshot,
|
|
||||||
cx.theme().colors(),
|
|
||||||
);
|
|
||||||
for hunk in selection_ranges {
|
|
||||||
let start_display = Point::new(hunk.0.start.row(), 0)
|
|
||||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
|
||||||
let end_display = Point::new(hunk.0.end.row(), 0)
|
|
||||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
|
||||||
let start_y = y_for_row(start_display.row() as f32);
|
|
||||||
let mut end_y = if hunk.0.start == hunk.0.end {
|
|
||||||
y_for_row((end_display.row() + 1) as f32)
|
|
||||||
} else {
|
|
||||||
y_for_row((end_display.row()) as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
if end_y - start_y < px(1.) {
|
|
||||||
end_y = start_y + px(1.);
|
|
||||||
}
|
|
||||||
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
|
||||||
|
|
||||||
cx.paint_quad(quad(
|
|
||||||
bounds,
|
|
||||||
Corners::default(),
|
|
||||||
cx.theme().status().info,
|
|
||||||
Edges {
|
|
||||||
top: Pixels::ZERO,
|
|
||||||
right: px(1.),
|
|
||||||
bottom: Pixels::ZERO,
|
|
||||||
left: px(1.),
|
|
||||||
},
|
|
||||||
cx.theme().colors().scrollbar_thumb_border,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if layout.is_singleton && scrollbar_settings.git_diff {
|
|
||||||
for hunk in layout
|
|
||||||
.position_map
|
|
||||||
.snapshot
|
|
||||||
.buffer_snapshot
|
|
||||||
.git_diff_hunks_in_range(0..(max_row.floor() as u32))
|
|
||||||
{
|
|
||||||
let start_display = Point::new(hunk.buffer_range.start, 0)
|
|
||||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
|
||||||
let end_display = Point::new(hunk.buffer_range.end, 0)
|
|
||||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
|
||||||
let start_y = y_for_row(start_display.row() as f32);
|
|
||||||
let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
|
|
||||||
y_for_row((end_display.row() + 1) as f32)
|
|
||||||
} else {
|
|
||||||
y_for_row((end_display.row()) as f32)
|
|
||||||
};
|
|
||||||
|
|
||||||
if end_y - start_y < px(1.) {
|
|
||||||
end_y = start_y + px(1.);
|
|
||||||
}
|
|
||||||
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
|
||||||
|
|
||||||
let color = match hunk.status() {
|
|
||||||
DiffHunkStatus::Added => cx.theme().status().created,
|
|
||||||
DiffHunkStatus::Modified => cx.theme().status().modified,
|
|
||||||
DiffHunkStatus::Removed => cx.theme().status().deleted,
|
|
||||||
};
|
|
||||||
cx.paint_quad(quad(
|
|
||||||
bounds,
|
|
||||||
Corners::default(),
|
|
||||||
color,
|
|
||||||
Edges {
|
|
||||||
top: Pixels::ZERO,
|
|
||||||
right: px(1.),
|
|
||||||
bottom: Pixels::ZERO,
|
|
||||||
left: px(1.),
|
|
||||||
},
|
|
||||||
cx.theme().colors().scrollbar_thumb_border,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if layout.is_singleton && scrollbar_settings.diagnostics {
|
if layout.is_singleton && scrollbar_settings.diagnostics {
|
||||||
let max_point = layout
|
let max_point = layout
|
||||||
|
@ -1531,17 +1418,131 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if layout.is_singleton && scrollbar_settings.git_diff {
|
||||||
|
for hunk in layout
|
||||||
|
.position_map
|
||||||
|
.snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.git_diff_hunks_in_range(0..(max_row.floor() as u32))
|
||||||
|
{
|
||||||
|
let start_display = Point::new(hunk.buffer_range.start, 0)
|
||||||
|
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||||
|
let end_display = Point::new(hunk.buffer_range.end, 0)
|
||||||
|
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||||
|
let start_y = y_for_row(start_display.row() as f32);
|
||||||
|
let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
|
||||||
|
y_for_row((end_display.row() + 1) as f32)
|
||||||
|
} else {
|
||||||
|
y_for_row((end_display.row()) as f32)
|
||||||
|
};
|
||||||
|
|
||||||
|
if end_y - start_y < px(1.) {
|
||||||
|
end_y = start_y + px(1.);
|
||||||
|
}
|
||||||
|
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
||||||
|
|
||||||
|
let color = match hunk.status() {
|
||||||
|
DiffHunkStatus::Added => cx.theme().status().created,
|
||||||
|
DiffHunkStatus::Modified => cx.theme().status().modified,
|
||||||
|
DiffHunkStatus::Removed => cx.theme().status().deleted,
|
||||||
|
};
|
||||||
|
cx.paint_quad(quad(
|
||||||
|
bounds,
|
||||||
|
Corners::default(),
|
||||||
|
color,
|
||||||
|
Edges {
|
||||||
|
top: Pixels::ZERO,
|
||||||
|
right: px(1.),
|
||||||
|
bottom: Pixels::ZERO,
|
||||||
|
left: px(1.),
|
||||||
|
},
|
||||||
|
cx.theme().colors().scrollbar_thumb_border,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if layout.is_singleton && scrollbar_settings.symbols_selections {
|
||||||
|
let selection_ranges = self.editor.read(cx).background_highlights_in_range(
|
||||||
|
Anchor::min()..Anchor::max(),
|
||||||
|
&layout.position_map.snapshot,
|
||||||
|
cx.theme().colors(),
|
||||||
|
);
|
||||||
|
for hunk in selection_ranges {
|
||||||
|
let start_display = Point::new(hunk.0.start.row(), 0)
|
||||||
|
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||||
|
let end_display = Point::new(hunk.0.end.row(), 0)
|
||||||
|
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||||
|
let start_y = y_for_row(start_display.row() as f32);
|
||||||
|
let mut end_y = if hunk.0.start == hunk.0.end {
|
||||||
|
y_for_row((end_display.row() + 1) as f32)
|
||||||
|
} else {
|
||||||
|
y_for_row((end_display.row()) as f32)
|
||||||
|
};
|
||||||
|
|
||||||
|
if end_y - start_y < px(1.) {
|
||||||
|
end_y = start_y + px(1.);
|
||||||
|
}
|
||||||
|
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
||||||
|
|
||||||
|
cx.paint_quad(quad(
|
||||||
|
bounds,
|
||||||
|
Corners::default(),
|
||||||
|
cx.theme().status().info,
|
||||||
|
Edges {
|
||||||
|
top: Pixels::ZERO,
|
||||||
|
right: px(1.),
|
||||||
|
bottom: Pixels::ZERO,
|
||||||
|
left: px(1.),
|
||||||
|
},
|
||||||
|
cx.theme().colors().scrollbar_thumb_border,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if layout.is_singleton && scrollbar_settings.selections {
|
||||||
|
let start_anchor = Anchor::min();
|
||||||
|
let end_anchor = Anchor::max();
|
||||||
|
let background_ranges = self
|
||||||
|
.editor
|
||||||
|
.read(cx)
|
||||||
|
.background_highlight_row_ranges::<BufferSearchHighlights>(
|
||||||
|
start_anchor..end_anchor,
|
||||||
|
&layout.position_map.snapshot,
|
||||||
|
50000,
|
||||||
|
);
|
||||||
|
for range in background_ranges {
|
||||||
|
let start_y = y_for_row(range.start().row() as f32);
|
||||||
|
let mut end_y = y_for_row(range.end().row() as f32);
|
||||||
|
if end_y - start_y < px(1.) {
|
||||||
|
end_y = start_y + px(1.);
|
||||||
|
}
|
||||||
|
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
||||||
|
cx.paint_quad(quad(
|
||||||
|
bounds,
|
||||||
|
Corners::default(),
|
||||||
|
cx.theme().status().info,
|
||||||
|
Edges {
|
||||||
|
top: Pixels::ZERO,
|
||||||
|
right: px(1.),
|
||||||
|
bottom: Pixels::ZERO,
|
||||||
|
left: px(1.),
|
||||||
|
},
|
||||||
|
cx.theme().colors().scrollbar_thumb_border,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cx.paint_quad(quad(
|
cx.paint_quad(quad(
|
||||||
thumb_bounds,
|
track_bounds,
|
||||||
Corners::default(),
|
Corners::default(),
|
||||||
cx.theme().colors().scrollbar_thumb_background,
|
cx.theme().colors().scrollbar_track_background,
|
||||||
Edges {
|
Edges {
|
||||||
top: Pixels::ZERO,
|
top: Pixels::ZERO,
|
||||||
right: px(1.),
|
right: Pixels::ZERO,
|
||||||
bottom: Pixels::ZERO,
|
bottom: Pixels::ZERO,
|
||||||
left: px(1.),
|
left: px(1.),
|
||||||
},
|
},
|
||||||
cx.theme().colors().scrollbar_thumb_border,
|
cx.theme().colors().scrollbar_track_border,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3126,34 +3127,25 @@ impl Element for EditorElement {
|
||||||
ElementInputHandler::new(bounds, self.editor.clone()),
|
ElementInputHandler::new(bounds, self.editor.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
self.paint_scrollbar(bounds, &mut layout, cx);
|
||||||
|
self.paint_overlays(text_bounds, &mut layout, cx);
|
||||||
|
if !layout.blocks.is_empty() {
|
||||||
|
cx.with_element_id(Some("editor_blocks"), |cx| {
|
||||||
|
self.paint_blocks(bounds, &mut layout, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.paint_mouse_listeners(
|
||||||
|
bounds,
|
||||||
|
gutter_bounds,
|
||||||
|
text_bounds,
|
||||||
|
&layout,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
self.paint_text(text_bounds, &mut layout, cx);
|
||||||
if layout.gutter_size.width > Pixels::ZERO {
|
if layout.gutter_size.width > Pixels::ZERO {
|
||||||
self.paint_gutter(gutter_bounds, &mut layout, cx);
|
self.paint_gutter(gutter_bounds, &mut layout, cx);
|
||||||
}
|
}
|
||||||
self.paint_text(text_bounds, &mut layout, cx);
|
self.paint_background(gutter_bounds, text_bounds, &layout, cx);
|
||||||
|
|
||||||
cx.with_z_index(0, |cx| {
|
|
||||||
self.paint_mouse_listeners(
|
|
||||||
bounds,
|
|
||||||
gutter_bounds,
|
|
||||||
text_bounds,
|
|
||||||
&layout,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if !layout.blocks.is_empty() {
|
|
||||||
cx.with_z_index(0, |cx| {
|
|
||||||
cx.with_element_id(Some("editor_blocks"), |cx| {
|
|
||||||
self.paint_blocks(bounds, &mut layout, cx);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.with_z_index(1, |cx| {
|
|
||||||
self.paint_overlays(text_bounds, &mut layout, cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
435
crates/gpui/src/bounds_tree.rs
Normal file
435
crates/gpui/src/bounds_tree.rs
Normal file
|
@ -0,0 +1,435 @@
|
||||||
|
use crate::{Bounds, Half};
|
||||||
|
use std::{
|
||||||
|
cmp,
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Add, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct BoundsTree<U: Default + Clone + Debug> {
|
||||||
|
root: Option<usize>,
|
||||||
|
nodes: Vec<Node<U>>,
|
||||||
|
stack: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U> BoundsTree<U>
|
||||||
|
where
|
||||||
|
U: Clone + Debug + PartialOrd + Add<U, Output = U> + Sub<Output = U> + Half + Default,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
BoundsTree::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.root = None;
|
||||||
|
self.nodes.clear();
|
||||||
|
self.stack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, new_bounds: Bounds<U>) -> u32 {
|
||||||
|
// If the tree is empty, make the root the new leaf.
|
||||||
|
if self.root.is_none() {
|
||||||
|
let new_node = self.push_leaf(new_bounds, 1);
|
||||||
|
self.root = Some(new_node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the best place to add the new leaf based on heuristics.
|
||||||
|
let mut max_intersecting_ordering = 0;
|
||||||
|
let mut index = self.root.unwrap();
|
||||||
|
while let Node::Internal {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
bounds: node_bounds,
|
||||||
|
..
|
||||||
|
} = self.node_mut(index)
|
||||||
|
{
|
||||||
|
let left = *left;
|
||||||
|
let right = *right;
|
||||||
|
*node_bounds = node_bounds.union(&new_bounds);
|
||||||
|
self.stack.push(index);
|
||||||
|
|
||||||
|
// Descend to the best-fit child, based on which one would increase
|
||||||
|
// the surface area the least. This attempts to keep the tree balanced
|
||||||
|
// in terms of surface area. If there is an intersection with the other child,
|
||||||
|
// add its keys to the intersections vector.
|
||||||
|
let left_cost = new_bounds.union(self.node(left).bounds()).half_perimeter();
|
||||||
|
let right_cost = new_bounds.union(self.node(right).bounds()).half_perimeter();
|
||||||
|
if left_cost < right_cost {
|
||||||
|
max_intersecting_ordering =
|
||||||
|
self.find_max_ordering(right, &new_bounds, max_intersecting_ordering);
|
||||||
|
index = left;
|
||||||
|
} else {
|
||||||
|
max_intersecting_ordering =
|
||||||
|
self.find_max_ordering(left, &new_bounds, max_intersecting_ordering);
|
||||||
|
index = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've found a leaf ('index' now refers to a leaf node).
|
||||||
|
// We'll insert a new parent node above the leaf and attach our new leaf to it.
|
||||||
|
let sibling = index;
|
||||||
|
|
||||||
|
// Check for collision with the located leaf node
|
||||||
|
let Node::Leaf {
|
||||||
|
bounds: sibling_bounds,
|
||||||
|
order: sibling_ordering,
|
||||||
|
..
|
||||||
|
} = self.node(index)
|
||||||
|
else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
if sibling_bounds.intersects(&new_bounds) {
|
||||||
|
max_intersecting_ordering = cmp::max(max_intersecting_ordering, *sibling_ordering);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ordering = max_intersecting_ordering + 1;
|
||||||
|
let new_node = self.push_leaf(new_bounds, ordering);
|
||||||
|
let new_parent = self.push_internal(sibling, new_node);
|
||||||
|
|
||||||
|
// If there was an old parent, we need to update its children indices.
|
||||||
|
if let Some(old_parent) = self.stack.last().copied() {
|
||||||
|
let Node::Internal { left, right, .. } = self.node_mut(old_parent) else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
if *left == sibling {
|
||||||
|
*left = new_parent;
|
||||||
|
} else {
|
||||||
|
*right = new_parent;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the old parent was the root, the new parent is the new root.
|
||||||
|
self.root = Some(new_parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
for node_index in self.stack.drain(..) {
|
||||||
|
let Node::Internal { max_ordering, .. } = &mut self.nodes[node_index] else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
*max_ordering = cmp::max(*max_ordering, ordering);
|
||||||
|
}
|
||||||
|
|
||||||
|
ordering
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_max_ordering(&self, index: usize, bounds: &Bounds<U>, mut max_ordering: u32) -> u32 {
|
||||||
|
match self.node(index) {
|
||||||
|
Node::Leaf {
|
||||||
|
bounds: node_bounds,
|
||||||
|
order: ordering,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if bounds.intersects(node_bounds) {
|
||||||
|
max_ordering = cmp::max(*ordering, max_ordering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Internal {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
bounds: node_bounds,
|
||||||
|
max_ordering: node_max_ordering,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if bounds.intersects(node_bounds) && max_ordering < *node_max_ordering {
|
||||||
|
let left_max_ordering = self.node(*left).max_ordering();
|
||||||
|
let right_max_ordering = self.node(*right).max_ordering();
|
||||||
|
if left_max_ordering > right_max_ordering {
|
||||||
|
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
|
||||||
|
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
|
||||||
|
} else {
|
||||||
|
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
|
||||||
|
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
max_ordering
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_leaf(&mut self, bounds: Bounds<U>, order: u32) -> usize {
|
||||||
|
self.nodes.push(Node::Leaf { bounds, order });
|
||||||
|
self.nodes.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_internal(&mut self, left: usize, right: usize) -> usize {
|
||||||
|
let left_node = self.node(left);
|
||||||
|
let right_node = self.node(right);
|
||||||
|
let new_bounds = left_node.bounds().union(right_node.bounds());
|
||||||
|
let max_ordering = cmp::max(left_node.max_ordering(), right_node.max_ordering());
|
||||||
|
self.nodes.push(Node::Internal {
|
||||||
|
bounds: new_bounds,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
max_ordering,
|
||||||
|
});
|
||||||
|
self.nodes.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn node(&self, index: usize) -> &Node<U> {
|
||||||
|
&self.nodes[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn node_mut(&mut self, index: usize) -> &mut Node<U> {
|
||||||
|
&mut self.nodes[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq)]
|
||||||
|
pub struct Primitive<U: Clone + Default + Debug> {
|
||||||
|
bounds: Bounds<U>,
|
||||||
|
order: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Node<U: Clone + Default + Debug> {
|
||||||
|
Leaf {
|
||||||
|
bounds: Bounds<U>,
|
||||||
|
order: u32,
|
||||||
|
},
|
||||||
|
Internal {
|
||||||
|
left: usize,
|
||||||
|
right: usize,
|
||||||
|
bounds: Bounds<U>,
|
||||||
|
max_ordering: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U> Node<U>
|
||||||
|
where
|
||||||
|
U: Clone + Default + Debug,
|
||||||
|
{
|
||||||
|
fn bounds(&self) -> &Bounds<U> {
|
||||||
|
match self {
|
||||||
|
Node::Leaf { bounds, .. } => bounds,
|
||||||
|
Node::Internal { bounds, .. } => bounds,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_ordering(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Node::Leaf {
|
||||||
|
order: ordering, ..
|
||||||
|
} => *ordering,
|
||||||
|
Node::Internal { max_ordering, .. } => *max_ordering,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{Point, Size};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bounds_insertion_with_two_bounds() {
|
||||||
|
let mut tree = BoundsTree::new();
|
||||||
|
let bounds1 = Bounds {
|
||||||
|
origin: Point { x: 0.0, y: 0.0 },
|
||||||
|
size: Size {
|
||||||
|
width: 10.0,
|
||||||
|
height: 10.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let bounds2 = Bounds {
|
||||||
|
origin: Point { x: 5.0, y: 5.0 },
|
||||||
|
size: Size {
|
||||||
|
width: 10.0,
|
||||||
|
height: 10.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert the first Bounds.
|
||||||
|
assert_eq!(tree.insert(bounds1), 1);
|
||||||
|
|
||||||
|
// Insert the second Bounds, which overlaps with the first.
|
||||||
|
assert_eq!(tree.insert(bounds2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_adjacent_bounds() {
|
||||||
|
let mut tree = BoundsTree::new();
|
||||||
|
let bounds1 = Bounds {
|
||||||
|
origin: Point { x: 0.0, y: 0.0 },
|
||||||
|
size: Size {
|
||||||
|
width: 10.0,
|
||||||
|
height: 10.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let bounds2 = Bounds {
|
||||||
|
origin: Point { x: 10.0, y: 0.0 },
|
||||||
|
size: Size {
|
||||||
|
width: 10.0,
|
||||||
|
height: 10.0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert the first bounds.
|
||||||
|
assert_eq!(tree.insert(bounds1), 1);
|
||||||
|
|
||||||
|
// Insert the second bounds, which is adjacent to the first but not overlapping.
|
||||||
|
assert_eq!(tree.insert(bounds2), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_random_iterations() {
|
||||||
|
let max_bounds = 100;
|
||||||
|
|
||||||
|
let mut actual_intersections: Vec<usize> = Vec::new();
|
||||||
|
for seed in 1..=1000 {
|
||||||
|
// let seed = 44;
|
||||||
|
let debug = false;
|
||||||
|
if debug {
|
||||||
|
let svg_path = Path::new("./svg");
|
||||||
|
if svg_path.exists() {
|
||||||
|
fs::remove_dir_all("./svg").unwrap();
|
||||||
|
}
|
||||||
|
fs::create_dir_all("./svg").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(seed);
|
||||||
|
|
||||||
|
let mut tree = BoundsTree::new();
|
||||||
|
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
|
||||||
|
let mut expected_quads: Vec<Primitive<f32>> = Vec::new();
|
||||||
|
|
||||||
|
let mut insert_time = std::time::Duration::ZERO;
|
||||||
|
|
||||||
|
// Insert a random number of random Bounds into the tree.
|
||||||
|
let num_bounds = rng.gen_range(1..=max_bounds);
|
||||||
|
for quad_id in 0..num_bounds {
|
||||||
|
let min_x: f32 = rng.gen_range(-100.0..100.0);
|
||||||
|
let min_y: f32 = rng.gen_range(-100.0..100.0);
|
||||||
|
let max_x: f32 = rng.gen_range(min_x..min_x + 50.0);
|
||||||
|
let max_y: f32 = rng.gen_range(min_y..min_y + 50.0);
|
||||||
|
let bounds = Bounds {
|
||||||
|
origin: Point { x: min_x, y: min_y },
|
||||||
|
size: Size {
|
||||||
|
width: max_x - min_x,
|
||||||
|
height: max_y - min_y,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_ordering = expected_quads
|
||||||
|
.iter()
|
||||||
|
.filter_map(|quad| {
|
||||||
|
(quad.bounds.origin.x < bounds.origin.x + bounds.size.width
|
||||||
|
&& quad.bounds.origin.x + quad.bounds.size.width > bounds.origin.x
|
||||||
|
&& quad.bounds.origin.y < bounds.origin.y + bounds.size.height
|
||||||
|
&& quad.bounds.origin.y + quad.bounds.size.height > bounds.origin.y)
|
||||||
|
.then_some(quad.order)
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
+ 1;
|
||||||
|
expected_quads.push(Primitive {
|
||||||
|
bounds,
|
||||||
|
order: expected_ordering,
|
||||||
|
});
|
||||||
|
if debug {
|
||||||
|
println!("inserting {} with Bounds: {:?}", quad_id, bounds);
|
||||||
|
draw_bounds(
|
||||||
|
format!("./svg/expected_bounds_after_{}.svg", quad_id),
|
||||||
|
&expected_quads,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the Bounds into the tree and collect intersections.
|
||||||
|
actual_intersections.clear();
|
||||||
|
let t0 = std::time::Instant::now();
|
||||||
|
let actual_ordering = tree.insert(bounds);
|
||||||
|
insert_time += t0.elapsed();
|
||||||
|
assert_eq!(actual_ordering, expected_ordering);
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
tree.draw(format!("./svg/bounds_tree_after_{}.svg", quad_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_bounds(svg_path: impl AsRef<Path>, bounds: &[Primitive<f32>]) {
|
||||||
|
let mut svg_content = String::from(
|
||||||
|
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="-100 -100 200 200" style="border:1px solid black;">"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
for quad in bounds {
|
||||||
|
svg_content.push_str(&format!(
|
||||||
|
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:none;stroke:black;stroke-width:1" />"#,
|
||||||
|
quad.bounds.origin.x,
|
||||||
|
quad.bounds.origin.y,
|
||||||
|
quad.bounds.size.width,
|
||||||
|
quad.bounds.size.height
|
||||||
|
));
|
||||||
|
svg_content.push_str(&format!(
|
||||||
|
r#"<text x="{}" y="{}" font-size="3" text-anchor="middle" alignment-baseline="central"></text>"#,
|
||||||
|
quad.bounds.origin.x + quad.bounds.size.width / 2.0,
|
||||||
|
quad.bounds.origin.y + quad.bounds.size.height / 2.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
svg_content.push_str("</svg>");
|
||||||
|
fs::write(svg_path, &svg_content).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundsTree<f32> {
|
||||||
|
fn draw(&self, svg_path: impl AsRef<std::path::Path>) {
|
||||||
|
let root_bounds = self.node(self.root.unwrap()).bounds();
|
||||||
|
|
||||||
|
let mut svg_content = format!(
|
||||||
|
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="border:1px solid black;" viewBox="{} {} {} {}">"#,
|
||||||
|
root_bounds.origin.x,
|
||||||
|
root_bounds.origin.y,
|
||||||
|
root_bounds.size.width,
|
||||||
|
root_bounds.size.height
|
||||||
|
);
|
||||||
|
|
||||||
|
fn draw_node(svg_content: &mut String, nodes: &[Node<f32>], index: usize) {
|
||||||
|
match &nodes[index] {
|
||||||
|
Node::Internal {
|
||||||
|
bounds,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
svg_content.push_str(&format!(
|
||||||
|
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:rgba({},{},{},0.1);stroke:rgba({},{},{},1);stroke-width:1" />"#,
|
||||||
|
bounds.origin.x,
|
||||||
|
bounds.origin.y,
|
||||||
|
bounds.size.width,
|
||||||
|
bounds.size.height,
|
||||||
|
(index * 50) % 255, // Red component
|
||||||
|
(index * 120) % 255, // Green component
|
||||||
|
(index * 180) % 255, // Blue component
|
||||||
|
(index * 50) % 255, // Red stroke
|
||||||
|
(index * 120) % 255, // Green stroke
|
||||||
|
(index * 180) % 255 // Blue stroke
|
||||||
|
));
|
||||||
|
draw_node(svg_content, nodes, *left);
|
||||||
|
draw_node(svg_content, nodes, *right);
|
||||||
|
}
|
||||||
|
Node::Leaf { bounds, .. } => {
|
||||||
|
svg_content.push_str(&format!(
|
||||||
|
r#"<rect x="{}" y="{}" width="{}" height="{}" style="fill:none;stroke:black;stroke-width:1" />"#,
|
||||||
|
bounds.origin.x,
|
||||||
|
bounds.origin.y,
|
||||||
|
bounds.size.width,
|
||||||
|
bounds.size.height
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(root) = self.root {
|
||||||
|
draw_node(&mut svg_content, &self.nodes, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg_content.push_str("</svg>");
|
||||||
|
std::fs::write(svg_path, &svg_content).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1127,7 +1127,7 @@ impl Element for Div {
|
||||||
cx,
|
cx,
|
||||||
|_style, scroll_offset, cx| {
|
|_style, scroll_offset, cx| {
|
||||||
cx.with_element_offset(scroll_offset, |cx| {
|
cx.with_element_offset(scroll_offset, |cx| {
|
||||||
for child in &mut self.children {
|
for child in self.children.iter_mut().rev() {
|
||||||
child.paint(cx);
|
child.paint(cx);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -828,6 +828,28 @@ where
|
||||||
y: self.origin.y.clone() + self.size.height.clone().half(),
|
y: self.origin.y.clone() + self.size.height.clone().half(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates the half perimeter of a rectangle defined by the bounds.
|
||||||
|
///
|
||||||
|
/// The half perimeter is calculated as the sum of the width and the height of the rectangle.
|
||||||
|
/// This method is generic over the type `T` which must implement the `Sub` trait to allow
|
||||||
|
/// calculation of the width and height from the bounds' origin and size, as well as the `Add` trait
|
||||||
|
/// to sum the width and height for the half perimeter.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use zed::{Bounds, Point, Size};
|
||||||
|
/// let bounds = Bounds {
|
||||||
|
/// origin: Point { x: 0, y: 0 },
|
||||||
|
/// size: Size { width: 10, height: 20 },
|
||||||
|
/// };
|
||||||
|
/// let half_perimeter = bounds.half_perimeter();
|
||||||
|
/// assert_eq!(half_perimeter, 30);
|
||||||
|
/// ```
|
||||||
|
pub fn half_perimeter(&self) -> T {
|
||||||
|
self.size.width.clone() + self.size.height.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
|
impl<T: Clone + Default + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
|
||||||
|
|
|
@ -70,6 +70,7 @@ mod app;
|
||||||
|
|
||||||
mod arena;
|
mod arena;
|
||||||
mod assets;
|
mod assets;
|
||||||
|
mod bounds_tree;
|
||||||
mod color;
|
mod color;
|
||||||
mod element;
|
mod element;
|
||||||
mod elements;
|
mod elements;
|
||||||
|
@ -117,6 +118,7 @@ pub use anyhow::Result;
|
||||||
pub use app::*;
|
pub use app::*;
|
||||||
pub(crate) use arena::*;
|
pub(crate) use arena::*;
|
||||||
pub use assets::*;
|
pub use assets::*;
|
||||||
|
pub(crate) use bounds_tree::*;
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use ctor::ctor;
|
pub use ctor::ctor;
|
||||||
pub use element::*;
|
pub use element::*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels,
|
point, AtlasTextureId, AtlasTile, Bounds, BoundsTree, ContentMask, Corners, Edges, EntityId,
|
||||||
Point, ScaledPixels, StackingOrder,
|
Hsla, Pixels, Point, ScaledPixels, StackingOrder,
|
||||||
};
|
};
|
||||||
use collections::{BTreeMap, FxHashSet};
|
use collections::{BTreeMap, FxHashSet};
|
||||||
use std::{fmt::Debug, iter::Peekable, slice};
|
use std::{fmt::Debug, iter::Peekable, slice};
|
||||||
|
@ -37,9 +37,6 @@ impl From<ViewId> for EntityId {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct Scene {
|
pub(crate) struct Scene {
|
||||||
last_layer: Option<(StackingOrder, LayerId)>,
|
|
||||||
layers_by_order: BTreeMap<StackingOrder, LayerId>,
|
|
||||||
orders_by_layer: BTreeMap<LayerId, StackingOrder>,
|
|
||||||
pub(crate) shadows: Vec<Shadow>,
|
pub(crate) shadows: Vec<Shadow>,
|
||||||
pub(crate) quads: Vec<Quad>,
|
pub(crate) quads: Vec<Quad>,
|
||||||
pub(crate) paths: Vec<Path<ScaledPixels>>,
|
pub(crate) paths: Vec<Path<ScaledPixels>>,
|
||||||
|
@ -47,13 +44,11 @@ pub(crate) struct Scene {
|
||||||
pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
|
pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
|
||||||
pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
|
pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
|
||||||
pub(crate) surfaces: Vec<Surface>,
|
pub(crate) surfaces: Vec<Surface>,
|
||||||
|
bounds_tree: BoundsTree<ScaledPixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.last_layer = None;
|
|
||||||
self.layers_by_order.clear();
|
|
||||||
self.orders_by_layer.clear();
|
|
||||||
self.shadows.clear();
|
self.shadows.clear();
|
||||||
self.quads.clear();
|
self.quads.clear();
|
||||||
self.paths.clear();
|
self.paths.clear();
|
||||||
|
@ -61,6 +56,7 @@ impl Scene {
|
||||||
self.monochrome_sprites.clear();
|
self.monochrome_sprites.clear();
|
||||||
self.polychrome_sprites.clear();
|
self.polychrome_sprites.clear();
|
||||||
self.surfaces.clear();
|
self.surfaces.clear();
|
||||||
|
self.bounds_tree.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paths(&self) -> &[Path<ScaledPixels>] {
|
pub fn paths(&self) -> &[Path<ScaledPixels>] {
|
||||||
|
@ -93,7 +89,11 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
|
pub(crate) fn insert(
|
||||||
|
&mut self,
|
||||||
|
order: &StackingOrder,
|
||||||
|
primitive: impl Into<Primitive>,
|
||||||
|
) -> Option<u32> {
|
||||||
let primitive = primitive.into();
|
let primitive = primitive.into();
|
||||||
let clipped_bounds = primitive
|
let clipped_bounds = primitive
|
||||||
.bounds()
|
.bounds()
|
||||||
|
@ -101,153 +101,108 @@ impl Scene {
|
||||||
if clipped_bounds.size.width <= ScaledPixels(0.)
|
if clipped_bounds.size.width <= ScaledPixels(0.)
|
||||||
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
|| clipped_bounds.size.height <= ScaledPixels(0.)
|
||||||
{
|
{
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer_id = self.layer_id_for_order(order);
|
let order = u32::MAX - self.bounds_tree.insert(clipped_bounds);
|
||||||
match primitive {
|
match primitive {
|
||||||
Primitive::Shadow(mut shadow) => {
|
Primitive::Shadow(mut shadow) => {
|
||||||
shadow.layer_id = layer_id;
|
shadow.order = order;
|
||||||
self.shadows.push(shadow);
|
self.shadows.push(shadow);
|
||||||
}
|
}
|
||||||
Primitive::Quad(mut quad) => {
|
Primitive::Quad(mut quad) => {
|
||||||
quad.layer_id = layer_id;
|
quad.order = order;
|
||||||
self.quads.push(quad);
|
self.quads.push(quad);
|
||||||
}
|
}
|
||||||
Primitive::Path(mut path) => {
|
Primitive::Path(mut path) => {
|
||||||
path.layer_id = layer_id;
|
path.order = order;
|
||||||
path.id = PathId(self.paths.len());
|
path.id = PathId(self.paths.len());
|
||||||
self.paths.push(path);
|
self.paths.push(path);
|
||||||
}
|
}
|
||||||
Primitive::Underline(mut underline) => {
|
Primitive::Underline(mut underline) => {
|
||||||
underline.layer_id = layer_id;
|
underline.order = order;
|
||||||
self.underlines.push(underline);
|
self.underlines.push(underline);
|
||||||
}
|
}
|
||||||
Primitive::MonochromeSprite(mut sprite) => {
|
Primitive::MonochromeSprite(mut sprite) => {
|
||||||
sprite.layer_id = layer_id;
|
sprite.order = order;
|
||||||
self.monochrome_sprites.push(sprite);
|
self.monochrome_sprites.push(sprite);
|
||||||
}
|
}
|
||||||
Primitive::PolychromeSprite(mut sprite) => {
|
Primitive::PolychromeSprite(mut sprite) => {
|
||||||
sprite.layer_id = layer_id;
|
sprite.order = order;
|
||||||
self.polychrome_sprites.push(sprite);
|
self.polychrome_sprites.push(sprite);
|
||||||
}
|
}
|
||||||
Primitive::Surface(mut surface) => {
|
Primitive::Surface(mut surface) => {
|
||||||
surface.layer_id = layer_id;
|
surface.order = order;
|
||||||
self.surfaces.push(surface);
|
self.surfaces.push(surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
|
Some(order)
|
||||||
if let Some((last_order, last_layer_id)) = self.last_layer.as_ref() {
|
|
||||||
if order == last_order {
|
|
||||||
return *last_layer_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
|
|
||||||
*layer_id
|
|
||||||
} else {
|
|
||||||
let next_id = self.layers_by_order.len() as LayerId;
|
|
||||||
self.layers_by_order.insert(order.clone(), next_id);
|
|
||||||
self.orders_by_layer.insert(next_id, order.clone());
|
|
||||||
next_id
|
|
||||||
};
|
|
||||||
self.last_layer = Some((order.clone(), layer_id));
|
|
||||||
layer_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
|
pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
|
||||||
for shadow in prev_scene.shadows.drain(..) {
|
// todo!()
|
||||||
if views.contains(&shadow.view_id.into()) {
|
// for shadow in prev_scene.shadows.drain(..) {
|
||||||
let order = &prev_scene.orders_by_layer[&shadow.layer_id];
|
// if views.contains(&shadow.view_id.into()) {
|
||||||
self.insert(order, shadow);
|
// let order = &prev_scene.orders_by_layer[&shadow.layer_id];
|
||||||
}
|
// self.insert(order, shadow);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
for quad in prev_scene.quads.drain(..) {
|
// for quad in prev_scene.quads.drain(..) {
|
||||||
if views.contains(&quad.view_id.into()) {
|
// if views.contains(&quad.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&quad.layer_id];
|
// let order = &prev_scene.orders_by_layer[&quad.layer_id];
|
||||||
self.insert(order, quad);
|
// self.insert(order, quad);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for path in prev_scene.paths.drain(..) {
|
// for path in prev_scene.paths.drain(..) {
|
||||||
if views.contains(&path.view_id.into()) {
|
// if views.contains(&path.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&path.layer_id];
|
// let order = &prev_scene.orders_by_layer[&path.layer_id];
|
||||||
self.insert(order, path);
|
// self.insert(order, path);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for underline in prev_scene.underlines.drain(..) {
|
// for underline in prev_scene.underlines.drain(..) {
|
||||||
if views.contains(&underline.view_id.into()) {
|
// if views.contains(&underline.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&underline.layer_id];
|
// let order = &prev_scene.orders_by_layer[&underline.layer_id];
|
||||||
self.insert(order, underline);
|
// self.insert(order, underline);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for sprite in prev_scene.monochrome_sprites.drain(..) {
|
// for sprite in prev_scene.monochrome_sprites.drain(..) {
|
||||||
if views.contains(&sprite.view_id.into()) {
|
// if views.contains(&sprite.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
// let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
||||||
self.insert(order, sprite);
|
// self.insert(order, sprite);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for sprite in prev_scene.polychrome_sprites.drain(..) {
|
// for sprite in prev_scene.polychrome_sprites.drain(..) {
|
||||||
if views.contains(&sprite.view_id.into()) {
|
// if views.contains(&sprite.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
// let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
||||||
self.insert(order, sprite);
|
// self.insert(order, sprite);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
for surface in prev_scene.surfaces.drain(..) {
|
// for surface in prev_scene.surfaces.drain(..) {
|
||||||
if views.contains(&surface.view_id.into()) {
|
// if views.contains(&surface.view_id.into()) {
|
||||||
let order = &prev_scene.orders_by_layer[&surface.layer_id];
|
// let order = &prev_scene.orders_by_layer[&surface.layer_id];
|
||||||
self.insert(order, surface);
|
// self.insert(order, surface);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&mut self) {
|
pub fn finish(&mut self) {
|
||||||
let mut orders = vec![0; self.layers_by_order.len()];
|
self.shadows.sort_unstable_by_key(|shadow| shadow.order);
|
||||||
for (ix, layer_id) in self.layers_by_order.values().enumerate() {
|
self.quads.sort_unstable_by_key(|quad| quad.order);
|
||||||
orders[*layer_id as usize] = ix as u32;
|
self.paths.sort_unstable_by_key(|path| path.order);
|
||||||
}
|
self.underlines
|
||||||
|
.sort_unstable_by_key(|underline| underline.order);
|
||||||
for shadow in &mut self.shadows {
|
self.monochrome_sprites
|
||||||
shadow.order = orders[shadow.layer_id as usize];
|
.sort_unstable_by_key(|sprite| sprite.order);
|
||||||
}
|
self.polychrome_sprites
|
||||||
self.shadows.sort_by_key(|shadow| shadow.order);
|
.sort_unstable_by_key(|sprite| sprite.order);
|
||||||
|
self.surfaces.sort_unstable_by_key(|surface| surface.order);
|
||||||
for quad in &mut self.quads {
|
|
||||||
quad.order = orders[quad.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.quads.sort_by_key(|quad| quad.order);
|
|
||||||
|
|
||||||
for path in &mut self.paths {
|
|
||||||
path.order = orders[path.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.paths.sort_by_key(|path| path.order);
|
|
||||||
|
|
||||||
for underline in &mut self.underlines {
|
|
||||||
underline.order = orders[underline.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.underlines.sort_by_key(|underline| underline.order);
|
|
||||||
|
|
||||||
for monochrome_sprite in &mut self.monochrome_sprites {
|
|
||||||
monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
|
|
||||||
|
|
||||||
for polychrome_sprite in &mut self.polychrome_sprites {
|
|
||||||
polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
|
|
||||||
|
|
||||||
for surface in &mut self.surfaces {
|
|
||||||
surface.order = orders[surface.layer_id as usize];
|
|
||||||
}
|
|
||||||
self.surfaces.sort_by_key(|surface| surface.order);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -402,98 +402,88 @@ impl Style {
|
||||||
|
|
||||||
let rem_size = cx.rem_size();
|
let rem_size = cx.rem_size();
|
||||||
|
|
||||||
cx.with_z_index(0, |cx| {
|
if self.is_border_visible() {
|
||||||
cx.paint_shadows(
|
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
|
||||||
bounds,
|
let border_widths = self.border_widths.to_pixels(rem_size);
|
||||||
self.corner_radii.to_pixels(bounds.size, rem_size),
|
let max_border_width = border_widths.max();
|
||||||
&self.box_shadow,
|
let max_corner_radius = corner_radii.max();
|
||||||
|
|
||||||
|
let top_bounds = Bounds::from_corners(
|
||||||
|
bounds.origin,
|
||||||
|
bounds.upper_right() + point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
|
||||||
);
|
);
|
||||||
});
|
let bottom_bounds = Bounds::from_corners(
|
||||||
|
bounds.lower_left() - point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
|
||||||
|
bounds.lower_right(),
|
||||||
|
);
|
||||||
|
let left_bounds = Bounds::from_corners(
|
||||||
|
top_bounds.lower_left(),
|
||||||
|
bottom_bounds.origin + point(max_border_width, Pixels::ZERO),
|
||||||
|
);
|
||||||
|
let right_bounds = Bounds::from_corners(
|
||||||
|
top_bounds.lower_right() - point(max_border_width, Pixels::ZERO),
|
||||||
|
bottom_bounds.upper_right(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut background = self.border_color.unwrap_or_default();
|
||||||
|
background.a = 0.;
|
||||||
|
let quad = quad(
|
||||||
|
bounds,
|
||||||
|
corner_radii,
|
||||||
|
background,
|
||||||
|
border_widths,
|
||||||
|
self.border_color.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
|
||||||
|
cx.paint_quad(quad.clone());
|
||||||
|
});
|
||||||
|
cx.with_content_mask(
|
||||||
|
Some(ContentMask {
|
||||||
|
bounds: right_bounds,
|
||||||
|
}),
|
||||||
|
|cx| {
|
||||||
|
cx.paint_quad(quad.clone());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cx.with_content_mask(
|
||||||
|
Some(ContentMask {
|
||||||
|
bounds: bottom_bounds,
|
||||||
|
}),
|
||||||
|
|cx| {
|
||||||
|
cx.paint_quad(quad.clone());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cx.with_content_mask(
|
||||||
|
Some(ContentMask {
|
||||||
|
bounds: left_bounds,
|
||||||
|
}),
|
||||||
|
|cx| {
|
||||||
|
cx.paint_quad(quad);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation(cx);
|
||||||
|
|
||||||
let background_color = self.background.as_ref().and_then(Fill::color);
|
let background_color = self.background.as_ref().and_then(Fill::color);
|
||||||
if background_color.map_or(false, |color| !color.is_transparent()) {
|
if background_color.map_or(false, |color| !color.is_transparent()) {
|
||||||
cx.with_z_index(1, |cx| {
|
let mut border_color = background_color.unwrap_or_default();
|
||||||
let mut border_color = background_color.unwrap_or_default();
|
border_color.a = 0.;
|
||||||
border_color.a = 0.;
|
cx.paint_quad(quad(
|
||||||
cx.paint_quad(quad(
|
bounds,
|
||||||
bounds,
|
self.corner_radii.to_pixels(bounds.size, rem_size),
|
||||||
self.corner_radii.to_pixels(bounds.size, rem_size),
|
background_color.unwrap_or_default(),
|
||||||
background_color.unwrap_or_default(),
|
Edges::default(),
|
||||||
Edges::default(),
|
border_color,
|
||||||
border_color,
|
));
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.with_z_index(2, |cx| {
|
cx.paint_shadows(
|
||||||
continuation(cx);
|
bounds,
|
||||||
});
|
self.corner_radii.to_pixels(bounds.size, rem_size),
|
||||||
|
&self.box_shadow,
|
||||||
if self.is_border_visible() {
|
);
|
||||||
cx.with_z_index(3, |cx| {
|
|
||||||
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
|
|
||||||
let border_widths = self.border_widths.to_pixels(rem_size);
|
|
||||||
let max_border_width = border_widths.max();
|
|
||||||
let max_corner_radius = corner_radii.max();
|
|
||||||
|
|
||||||
let top_bounds = Bounds::from_corners(
|
|
||||||
bounds.origin,
|
|
||||||
bounds.upper_right()
|
|
||||||
+ point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
|
|
||||||
);
|
|
||||||
let bottom_bounds = Bounds::from_corners(
|
|
||||||
bounds.lower_left()
|
|
||||||
- point(Pixels::ZERO, max_border_width.max(max_corner_radius)),
|
|
||||||
bounds.lower_right(),
|
|
||||||
);
|
|
||||||
let left_bounds = Bounds::from_corners(
|
|
||||||
top_bounds.lower_left(),
|
|
||||||
bottom_bounds.origin + point(max_border_width, Pixels::ZERO),
|
|
||||||
);
|
|
||||||
let right_bounds = Bounds::from_corners(
|
|
||||||
top_bounds.lower_right() - point(max_border_width, Pixels::ZERO),
|
|
||||||
bottom_bounds.upper_right(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut background = self.border_color.unwrap_or_default();
|
|
||||||
background.a = 0.;
|
|
||||||
let quad = quad(
|
|
||||||
bounds,
|
|
||||||
corner_radii,
|
|
||||||
background,
|
|
||||||
border_widths,
|
|
||||||
self.border_color.unwrap_or_default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
|
|
||||||
cx.paint_quad(quad.clone());
|
|
||||||
});
|
|
||||||
cx.with_content_mask(
|
|
||||||
Some(ContentMask {
|
|
||||||
bounds: right_bounds,
|
|
||||||
}),
|
|
||||||
|cx| {
|
|
||||||
cx.paint_quad(quad.clone());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
cx.with_content_mask(
|
|
||||||
Some(ContentMask {
|
|
||||||
bounds: bottom_bounds,
|
|
||||||
}),
|
|
||||||
|cx| {
|
|
||||||
cx.paint_quad(quad.clone());
|
|
||||||
},
|
|
||||||
);
|
|
||||||
cx.with_content_mask(
|
|
||||||
Some(ContentMask {
|
|
||||||
bounds: left_bounds,
|
|
||||||
}),
|
|
||||||
|cx| {
|
|
||||||
cx.paint_quad(quad);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
if self.debug_below {
|
if self.debug_below {
|
||||||
|
|
|
@ -212,7 +212,8 @@ impl AnyView {
|
||||||
/// When using this method, the view's previous layout and paint will be recycled from the previous frame if [ViewContext::notify] has not been called since it was rendered.
|
/// When using this method, the view's previous layout and paint will be recycled from the previous frame if [ViewContext::notify] has not been called since it was rendered.
|
||||||
/// The one exception is when [WindowContext::refresh] is called, in which case caching is ignored.
|
/// The one exception is when [WindowContext::refresh] is called, in which case caching is ignored.
|
||||||
pub fn cached(mut self) -> Self {
|
pub fn cached(mut self) -> Self {
|
||||||
self.cache = true;
|
// TODO!: ENABLE ME!
|
||||||
|
// self.cache = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue