Compare commits
11 commits
main
...
fix-hover-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
30239b3cc6 | ||
![]() |
180421fe5a | ||
![]() |
1a13995b8f | ||
![]() |
b6379a9177 | ||
![]() |
2cb041504b | ||
![]() |
4d8dc79d7e | ||
![]() |
474c806331 | ||
![]() |
2db6ccd803 | ||
![]() |
2d6a227258 | ||
![]() |
a3ce933b04 | ||
![]() |
816c48b7d6 |
20 changed files with 2356 additions and 1442 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -8548,9 +8548,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
|
|
|
@ -2672,24 +2672,32 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
|
|||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
),
|
||||
),
|
||||
color,
|
||||
),
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
None,
|
||||
None,
|
||||
);
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
})
|
||||
.w(width)
|
||||
.h(line_height)
|
||||
|
|
|
@ -632,8 +632,8 @@ impl EditorElement {
|
|||
let scroll_top =
|
||||
layout.position_map.snapshot.scroll_position().y * layout.position_map.line_height;
|
||||
let gutter_bg = cx.theme().colors().editor_gutter_background;
|
||||
cx.paint_quad(fill(gutter_bounds, gutter_bg));
|
||||
cx.paint_quad(fill(text_bounds, self.style.background));
|
||||
cx.paint_quad(fill(gutter_bounds, gutter_bg), None, None);
|
||||
cx.paint_quad(fill(text_bounds, self.style.background), None, None);
|
||||
|
||||
if let EditorMode::Full = layout.mode {
|
||||
let mut active_rows = layout.active_rows.iter().peekable();
|
||||
|
@ -657,7 +657,7 @@ impl EditorElement {
|
|||
layout.position_map.line_height * (end_row - start_row + 1) as f32,
|
||||
);
|
||||
let active_line_bg = cx.theme().colors().editor_active_line_background;
|
||||
cx.paint_quad(fill(Bounds { origin, size }, active_line_bg));
|
||||
cx.paint_quad(fill(Bounds { origin, size }, active_line_bg), None, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -673,7 +673,11 @@ impl EditorElement {
|
|||
layout.position_map.line_height * highlighted_rows.len() as f32,
|
||||
);
|
||||
let highlighted_line_bg = cx.theme().colors().editor_highlighted_line_background;
|
||||
cx.paint_quad(fill(Bounds { origin, size }, highlighted_line_bg));
|
||||
cx.paint_quad(
|
||||
fill(Bounds { origin, size }, highlighted_line_bg),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
let scroll_left =
|
||||
|
@ -694,13 +698,17 @@ impl EditorElement {
|
|||
} else {
|
||||
cx.theme().colors().editor_wrap_guide
|
||||
};
|
||||
cx.paint_quad(fill(
|
||||
Bounds {
|
||||
origin: point(x, text_bounds.origin.y),
|
||||
size: size(px(1.), text_bounds.size.height),
|
||||
},
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds {
|
||||
origin: point(x, text_bounds.origin.y),
|
||||
size: size(px(1.), text_bounds.size.height),
|
||||
},
|
||||
color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -804,13 +812,17 @@ impl EditorElement {
|
|||
let highlight_origin = bounds.origin + point(-width, start_y);
|
||||
let highlight_size = size(width * 2., end_y - start_y);
|
||||
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
|
||||
cx.paint_quad(quad(
|
||||
highlight_bounds,
|
||||
Corners::all(1. * line_height),
|
||||
cx.theme().status().modified,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
));
|
||||
cx.paint_quad(
|
||||
quad(
|
||||
highlight_bounds,
|
||||
Corners::all(1. * line_height),
|
||||
cx.theme().status().modified,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -837,13 +849,17 @@ impl EditorElement {
|
|||
let highlight_origin = bounds.origin + point(-width, start_y);
|
||||
let highlight_size = size(width * 2., end_y - start_y);
|
||||
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
|
||||
cx.paint_quad(quad(
|
||||
highlight_bounds,
|
||||
Corners::all(1. * line_height),
|
||||
cx.theme().status().deleted,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
));
|
||||
cx.paint_quad(
|
||||
quad(
|
||||
highlight_bounds,
|
||||
Corners::all(1. * line_height),
|
||||
cx.theme().status().deleted,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -877,13 +893,17 @@ impl EditorElement {
|
|||
let highlight_origin = bounds.origin + point(-width, start_y);
|
||||
let highlight_size = size(width * 2., end_y - start_y);
|
||||
let highlight_bounds = Bounds::new(highlight_origin, highlight_size);
|
||||
cx.paint_quad(quad(
|
||||
highlight_bounds,
|
||||
Corners::all(0.05 * line_height),
|
||||
color,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
));
|
||||
cx.paint_quad(
|
||||
quad(
|
||||
highlight_bounds,
|
||||
Corners::all(0.05 * line_height),
|
||||
color,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1346,18 +1366,22 @@ impl EditorElement {
|
|||
let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
|
||||
|
||||
if layout.show_scrollbars {
|
||||
cx.paint_quad(quad(
|
||||
track_bounds,
|
||||
Corners::default(),
|
||||
cx.theme().colors().scrollbar_track_background,
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: Pixels::ZERO,
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
cx.theme().colors().scrollbar_track_border,
|
||||
));
|
||||
cx.paint_quad(
|
||||
quad(
|
||||
track_bounds,
|
||||
Corners::default(),
|
||||
cx.theme().colors().scrollbar_track_background,
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: Pixels::ZERO,
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
cx.theme().colors().scrollbar_track_border,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
|
||||
if layout.is_singleton && scrollbar_settings.selections {
|
||||
let start_anchor = Anchor::min();
|
||||
|
@ -1377,18 +1401,22 @@ impl EditorElement {
|
|||
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(
|
||||
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,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1415,18 +1443,22 @@ impl EditorElement {
|
|||
}
|
||||
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(
|
||||
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,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1458,18 +1490,22 @@ impl EditorElement {
|
|||
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,
|
||||
));
|
||||
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,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1516,33 +1552,41 @@ impl EditorElement {
|
|||
DiagnosticSeverity::INFORMATION => cx.theme().status().info,
|
||||
_ => cx.theme().status().hint,
|
||||
};
|
||||
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,
|
||||
));
|
||||
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,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
cx.paint_quad(quad(
|
||||
thumb_bounds,
|
||||
Corners::default(),
|
||||
cx.theme().colors().scrollbar_thumb_background,
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: px(1.),
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
cx.theme().colors().scrollbar_thumb_border,
|
||||
));
|
||||
cx.paint_quad(
|
||||
quad(
|
||||
thumb_bounds,
|
||||
Corners::default(),
|
||||
cx.theme().colors().scrollbar_thumb_background,
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: px(1.),
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
cx.theme().colors().scrollbar_thumb_border,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
let interactive_track_bounds = InteractiveBounds {
|
||||
|
@ -3421,7 +3465,7 @@ impl Cursor {
|
|||
})
|
||||
}
|
||||
|
||||
cx.paint_quad(cursor);
|
||||
cx.paint_quad(cursor, None, None);
|
||||
|
||||
if let Some(block_text) = &self.block_text {
|
||||
block_text
|
||||
|
|
|
@ -94,7 +94,7 @@ fn generate_shader_bindings() -> PathBuf {
|
|||
let mut builder = cbindgen::Builder::new();
|
||||
|
||||
let src_paths = [
|
||||
crate_dir.join("src/scene.rs"),
|
||||
crate_dir.join("src/scene/primitives.rs"),
|
||||
crate_dir.join("src/geometry.rs"),
|
||||
crate_dir.join("src/color.rs"),
|
||||
crate_dir.join("src/window.rs"),
|
||||
|
|
363
crates/gpui/src/bounds_tree.rs
Normal file
363
crates/gpui/src/bounds_tree.rs
Normal file
|
@ -0,0 +1,363 @@
|
|||
use crate::{Bounds, Half, Point};
|
||||
use std::{
|
||||
cmp,
|
||||
fmt::Debug,
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoundsTree<U, T>
|
||||
where
|
||||
U: Default + Clone + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
root: Option<usize>,
|
||||
nodes: Vec<Node<U, T>>,
|
||||
stack: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<U, T> BoundsTree<U, T>
|
||||
where
|
||||
U: Clone + Debug + PartialOrd + Add<U, Output = U> + Sub<Output = U> + Half + Default,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
pub fn clear(&mut self) {
|
||||
self.root = None;
|
||||
self.nodes.clear();
|
||||
self.stack.clear();
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, new_bounds: Bounds<U>, payload: T) -> 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, payload, 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,
|
||||
..
|
||||
} = &mut self.nodes[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.nodes[left].bounds())
|
||||
.half_perimeter();
|
||||
let right_cost = new_bounds
|
||||
.union(&self.nodes[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.nodes[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, payload, 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, .. } = &mut self.nodes[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_order: max_ordering,
|
||||
..
|
||||
} = &mut self.nodes[node_index]
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
*max_ordering = cmp::max(*max_ordering, ordering);
|
||||
}
|
||||
|
||||
ordering
|
||||
}
|
||||
|
||||
/// Finds all nodes whose bounds contain the given point and pushes their (bounds, payload) pairs onto the result vector.
|
||||
pub(crate) fn find_containing(
|
||||
&mut self,
|
||||
point: &Point<U>,
|
||||
result: &mut Vec<BoundsSearchResult<U, T>>,
|
||||
) {
|
||||
if let Some(mut index) = self.root {
|
||||
self.stack.clear();
|
||||
self.stack.push(index);
|
||||
|
||||
while let Some(current_index) = self.stack.pop() {
|
||||
match &self.nodes[current_index] {
|
||||
Node::Leaf {
|
||||
bounds,
|
||||
order,
|
||||
data,
|
||||
} => {
|
||||
if bounds.contains(point) {
|
||||
result.push(BoundsSearchResult {
|
||||
bounds: bounds.clone(),
|
||||
order: *order,
|
||||
data: data.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
Node::Internal {
|
||||
left,
|
||||
right,
|
||||
bounds,
|
||||
..
|
||||
} => {
|
||||
if bounds.contains(point) {
|
||||
self.stack.push(*left);
|
||||
self.stack.push(*right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_max_ordering(&self, index: usize, bounds: &Bounds<U>, mut max_ordering: u32) -> u32 {
|
||||
match {
|
||||
let this = &self;
|
||||
&this.nodes[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_order: node_max_ordering,
|
||||
..
|
||||
} => {
|
||||
if bounds.intersects(node_bounds) && max_ordering < *node_max_ordering {
|
||||
let left_max_ordering = self.nodes[*left].max_ordering();
|
||||
let right_max_ordering = self.nodes[*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>, payload: T, order: u32) -> usize {
|
||||
self.nodes.push(Node::Leaf {
|
||||
bounds,
|
||||
data: payload,
|
||||
order,
|
||||
});
|
||||
self.nodes.len() - 1
|
||||
}
|
||||
|
||||
fn push_internal(&mut self, left: usize, right: usize) -> usize {
|
||||
let left_node = &self.nodes[left];
|
||||
let right_node = &self.nodes[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_order: max_ordering,
|
||||
});
|
||||
self.nodes.len() - 1
|
||||
}
|
||||
}
|
||||
|
||||
impl<U, T> Default for BoundsTree<U, T>
|
||||
where
|
||||
U: Default + Clone + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
fn default() -> Self {
|
||||
BoundsTree {
|
||||
root: None,
|
||||
nodes: Vec::new(),
|
||||
stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Node<U, T>
|
||||
where
|
||||
U: Clone + Default + Debug,
|
||||
T: Clone + Debug,
|
||||
{
|
||||
Leaf {
|
||||
bounds: Bounds<U>,
|
||||
order: u32,
|
||||
data: T,
|
||||
},
|
||||
Internal {
|
||||
left: usize,
|
||||
right: usize,
|
||||
bounds: Bounds<U>,
|
||||
max_order: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl<U, T> Node<U, T>
|
||||
where
|
||||
U: Clone + Default + Debug,
|
||||
T: Clone + 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_order: max_ordering,
|
||||
..
|
||||
} => *max_ordering,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BoundsSearchResult<U: Clone + Default + Debug, T> {
|
||||
pub bounds: Bounds<U>,
|
||||
pub order: u32,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Bounds, Point, Size};
|
||||
|
||||
#[test]
|
||||
fn test_insert_and_find_containing() {
|
||||
let mut tree = BoundsTree::<f32, String>::default();
|
||||
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,
|
||||
},
|
||||
};
|
||||
let bounds3 = Bounds {
|
||||
origin: Point { x: 10.0, y: 10.0 },
|
||||
size: Size {
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
},
|
||||
};
|
||||
|
||||
// Insert bounds into the tree
|
||||
tree.insert(bounds1.clone(), "Payload 1".to_string());
|
||||
tree.insert(bounds2.clone(), "Payload 2".to_string());
|
||||
tree.insert(bounds3.clone(), "Payload 3".to_string());
|
||||
|
||||
// Points for testing
|
||||
let point_inside_bounds1 = Point { x: 1.0, y: 1.0 };
|
||||
let point_inside_bounds1_and_2 = Point { x: 6.0, y: 6.0 };
|
||||
let point_inside_bounds2_and_3 = Point { x: 12.0, y: 12.0 };
|
||||
let point_outside_all_bounds = Point { x: 21.0, y: 21.0 };
|
||||
|
||||
assert!(!bounds1.contains(&point_inside_bounds2_and_3));
|
||||
assert!(!bounds1.contains(&point_outside_all_bounds));
|
||||
assert!(bounds2.contains(&point_inside_bounds1_and_2));
|
||||
assert!(bounds2.contains(&point_inside_bounds2_and_3));
|
||||
assert!(!bounds2.contains(&point_outside_all_bounds));
|
||||
assert!(!bounds3.contains(&point_inside_bounds1));
|
||||
assert!(bounds3.contains(&point_inside_bounds2_and_3));
|
||||
assert!(!bounds3.contains(&point_outside_all_bounds));
|
||||
|
||||
// Test find_containing for different points
|
||||
let mut result = Vec::new();
|
||||
tree.find_containing(&point_inside_bounds1, &mut result);
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result[0].data, "Payload 1");
|
||||
|
||||
result.clear();
|
||||
tree.find_containing(&point_inside_bounds1_and_2, &mut result);
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result.iter().any(|r| r.data == "Payload 1"));
|
||||
assert!(result.iter().any(|r| r.data == "Payload 2"));
|
||||
|
||||
result.clear();
|
||||
tree.find_containing(&point_inside_bounds2_and_3, &mut result);
|
||||
assert_eq!(result.len(), 2);
|
||||
assert!(result.iter().any(|r| r.data == "Payload 2"));
|
||||
assert!(result.iter().any(|r| r.data == "Payload 3"));
|
||||
|
||||
result.clear();
|
||||
tree.find_containing(&point_outside_all_bounds, &mut result);
|
||||
assert_eq!(result.len(), 0);
|
||||
}
|
||||
}
|
|
@ -338,6 +338,11 @@ impl Hsla {
|
|||
self.a == 0.0
|
||||
}
|
||||
|
||||
/// Returns true if the HSLA color is fully opaque, false otherwise.
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
self.a == 1.0
|
||||
}
|
||||
|
||||
/// Blends `other` on top of `self` based on `other`'s alpha value. The resulting color is a combination of `self`'s and `other`'s colors.
|
||||
///
|
||||
/// If `other`'s alpha value is 1.0 or greater, `other` color is fully opaque, thus `other` is returned as the output color.
|
||||
|
|
|
@ -45,7 +45,7 @@ impl Element for Canvas {
|
|||
}
|
||||
|
||||
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
|
||||
style.paint(bounds, cx, |cx| {
|
||||
style.paint(bounds, None, None, cx, |cx| {
|
||||
(self.paint_callback.take().unwrap())(&bounds, cx)
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -152,7 +152,7 @@ impl Element for Img {
|
|||
let size = size(surface.width().into(), surface.height().into());
|
||||
let new_bounds = preserve_aspect_ratio(bounds, size);
|
||||
// TODO: Add support for corner_radii and grayscale.
|
||||
cx.paint_surface(new_bounds, surface);
|
||||
cx.paint_surface(new_bounds, surface, true);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -828,6 +828,28 @@ where
|
|||
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> {
|
||||
|
@ -2617,6 +2639,12 @@ pub trait Half {
|
|||
fn half(&self) -> Self;
|
||||
}
|
||||
|
||||
impl Half for i32 {
|
||||
fn half(&self) -> Self {
|
||||
self / 2
|
||||
}
|
||||
}
|
||||
|
||||
impl Half for f32 {
|
||||
fn half(&self) -> Self {
|
||||
self / 2.
|
||||
|
|
|
@ -70,6 +70,7 @@ mod app;
|
|||
|
||||
mod arena;
|
||||
mod assets;
|
||||
mod bounds_tree;
|
||||
mod color;
|
||||
mod element;
|
||||
mod elements;
|
||||
|
@ -117,6 +118,7 @@ pub use anyhow::Result;
|
|||
pub use app::*;
|
||||
pub(crate) use arena::*;
|
||||
pub use assets::*;
|
||||
pub(crate) use bounds_tree::*;
|
||||
pub use color::*;
|
||||
pub use ctor::ctor;
|
||||
pub use element::*;
|
||||
|
|
File diff suppressed because it is too large
Load diff
345
crates/gpui/src/scene/primitives.rs
Normal file
345
crates/gpui/src/scene/primitives.rs
Normal file
|
@ -0,0 +1,345 @@
|
|||
use crate::{
|
||||
point, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels, Point,
|
||||
ScaledPixels,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Quad {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub background: Hsla,
|
||||
pub border_color: Hsla,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
pub border_widths: Edges<ScaledPixels>,
|
||||
}
|
||||
|
||||
impl Ord for Quad {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Quad {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Underline {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub thickness: ScaledPixels,
|
||||
pub wavy: bool,
|
||||
}
|
||||
|
||||
impl Ord for Underline {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Underline {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Shadow {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub blur_radius: ScaledPixels,
|
||||
pub pad: u32, // align to 8 bytes
|
||||
}
|
||||
|
||||
impl Ord for Shadow {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Shadow {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MonochromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub tile: AtlasTile,
|
||||
}
|
||||
|
||||
impl Ord for MonochromeSprite {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.order.cmp(&other.order) {
|
||||
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
|
||||
order => order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for MonochromeSprite {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PolychromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
pub tile: AtlasTile,
|
||||
pub grayscale: bool,
|
||||
pub pad: u32, // align to 8 bytes
|
||||
}
|
||||
|
||||
impl Ord for PolychromeSprite {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.order.cmp(&other.order) {
|
||||
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
|
||||
order => order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PolychromeSprite {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Surface {
|
||||
pub view_id: ViewId,
|
||||
pub order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub image_buffer: media::core_video::CVImageBuffer,
|
||||
}
|
||||
|
||||
impl Ord for Surface {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Surface {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PathId(pub(crate) usize);
|
||||
|
||||
/// A line made up of a series of vertices and control points.
|
||||
#[derive(Debug)]
|
||||
pub struct Path<P: Clone + Default + Debug> {
|
||||
pub(crate) id: PathId,
|
||||
pub(crate) view_id: ViewId,
|
||||
pub(crate) order: u32,
|
||||
pub(crate) bounds: Bounds<P>,
|
||||
pub(crate) content_mask: ContentMask<P>,
|
||||
pub(crate) vertices: Vec<PathVertex<P>>,
|
||||
pub(crate) color: Hsla,
|
||||
pub(crate) start: Point<P>,
|
||||
pub(crate) current: Point<P>,
|
||||
pub(crate) contour_count: usize,
|
||||
}
|
||||
|
||||
impl Path<Pixels> {
|
||||
/// Create a new path with the given starting point.
|
||||
pub fn new(start: Point<Pixels>) -> Self {
|
||||
Self {
|
||||
id: PathId(0),
|
||||
view_id: ViewId::default(),
|
||||
order: u32::default(),
|
||||
vertices: Vec::new(),
|
||||
start,
|
||||
current: start,
|
||||
bounds: Bounds {
|
||||
origin: start,
|
||||
size: Default::default(),
|
||||
},
|
||||
content_mask: Default::default(),
|
||||
color: Default::default(),
|
||||
contour_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Scale this path by the given factor.
|
||||
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
|
||||
Path {
|
||||
id: self.id,
|
||||
view_id: self.view_id,
|
||||
order: self.order,
|
||||
bounds: self.bounds.scale(factor),
|
||||
content_mask: self.content_mask.scale(factor),
|
||||
vertices: self
|
||||
.vertices
|
||||
.iter()
|
||||
.map(|vertex| vertex.scale(factor))
|
||||
.collect(),
|
||||
start: self.start.map(|start| start.scale(factor)),
|
||||
current: self.current.scale(factor),
|
||||
contour_count: self.contour_count,
|
||||
color: self.color,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a straight line from the current point to the given point.
|
||||
pub fn line_to(&mut self, to: Point<Pixels>) {
|
||||
self.contour_count += 1;
|
||||
if self.contour_count > 1 {
|
||||
self.push_triangle(
|
||||
(self.start, self.current, to),
|
||||
(point(0., 1.), point(0., 1.), point(0., 1.)),
|
||||
);
|
||||
}
|
||||
self.current = to;
|
||||
}
|
||||
|
||||
/// Draw a curve from the current point to the given point, using the given control point.
|
||||
pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
|
||||
self.contour_count += 1;
|
||||
if self.contour_count > 1 {
|
||||
self.push_triangle(
|
||||
(self.start, self.current, to),
|
||||
(point(0., 1.), point(0., 1.), point(0., 1.)),
|
||||
);
|
||||
}
|
||||
|
||||
self.push_triangle(
|
||||
(self.current, ctrl, to),
|
||||
(point(0., 0.), point(0.5, 0.), point(1., 1.)),
|
||||
);
|
||||
self.current = to;
|
||||
}
|
||||
|
||||
fn push_triangle(
|
||||
&mut self,
|
||||
xy: (Point<Pixels>, Point<Pixels>, Point<Pixels>),
|
||||
st: (Point<f32>, Point<f32>, Point<f32>),
|
||||
) {
|
||||
self.bounds = self
|
||||
.bounds
|
||||
.union(&Bounds {
|
||||
origin: xy.0,
|
||||
size: Default::default(),
|
||||
})
|
||||
.union(&Bounds {
|
||||
origin: xy.1,
|
||||
size: Default::default(),
|
||||
})
|
||||
.union(&Bounds {
|
||||
origin: xy.2,
|
||||
size: Default::default(),
|
||||
});
|
||||
|
||||
self.vertices.push(PathVertex {
|
||||
xy_position: xy.0,
|
||||
st_position: st.0,
|
||||
content_mask: Default::default(),
|
||||
});
|
||||
self.vertices.push(PathVertex {
|
||||
xy_position: xy.1,
|
||||
st_position: st.1,
|
||||
content_mask: Default::default(),
|
||||
});
|
||||
self.vertices.push(PathVertex {
|
||||
xy_position: xy.2,
|
||||
st_position: st.2,
|
||||
content_mask: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Path<ScaledPixels> {}
|
||||
|
||||
impl PartialEq for Path<ScaledPixels> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.order == other.order
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Path<ScaledPixels> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Path<ScaledPixels> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PathVertex<P: Clone + Default + Debug> {
|
||||
pub(crate) xy_position: Point<P>,
|
||||
pub(crate) st_position: Point<f32>,
|
||||
pub(crate) content_mask: ContentMask<P>,
|
||||
}
|
||||
|
||||
impl PathVertex<Pixels> {
|
||||
pub fn scale(&self, factor: f32) -> PathVertex<ScaledPixels> {
|
||||
PathVertex {
|
||||
xy_position: self.xy_position.scale(factor),
|
||||
st_position: self.st_position,
|
||||
content_mask: self.content_mask.scale(factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct ViewId {
|
||||
low_bits: u32,
|
||||
high_bits: u32,
|
||||
}
|
||||
|
||||
impl From<EntityId> for ViewId {
|
||||
fn from(value: EntityId) -> Self {
|
||||
let value = value.as_u64();
|
||||
Self {
|
||||
low_bits: value as u32,
|
||||
high_bits: (value >> 32) as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ViewId> for EntityId {
|
||||
fn from(value: ViewId) -> Self {
|
||||
let value = (value.low_bits as u64) | ((value.high_bits as u64) << 32);
|
||||
value.into()
|
||||
}
|
||||
}
|
|
@ -383,10 +383,12 @@ impl Style {
|
|||
}
|
||||
}
|
||||
|
||||
/// Paints the background of an element styled with this style.
|
||||
/// Paints the background of an element styled with this style, then calls the continuation function, then paints the border.
|
||||
pub fn paint(
|
||||
&self,
|
||||
bounds: Bounds<Pixels>,
|
||||
hover: Option<Self>,
|
||||
group_hover: Option<(SharedString, Option<Self>)>,
|
||||
cx: &mut ElementContext,
|
||||
continuation: impl FnOnce(&mut ElementContext),
|
||||
) {
|
||||
|
@ -397,7 +399,7 @@ impl Style {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
if self.debug || cx.has_global::<DebugBelow>() {
|
||||
cx.paint_quad(crate::outline(bounds, crate::red()));
|
||||
cx.paint_quad(crate::outline(bounds, crate::red()), None, None);
|
||||
}
|
||||
|
||||
let rem_size = cx.rem_size();
|
||||
|
@ -410,101 +412,229 @@ impl Style {
|
|||
);
|
||||
});
|
||||
|
||||
let background_color = self.background.as_ref().and_then(Fill::color);
|
||||
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();
|
||||
border_color.a = 0.;
|
||||
cx.paint_quad(quad(
|
||||
bounds,
|
||||
self.corner_radii.to_pixels(bounds.size, rem_size),
|
||||
background_color.unwrap_or_default(),
|
||||
Edges::default(),
|
||||
border_color,
|
||||
));
|
||||
});
|
||||
}
|
||||
let named_hover_group = group_hover
|
||||
.as_ref()
|
||||
.map(|(group_name, _)| group_name.clone());
|
||||
cx.with_hover_group(named_hover_group, |cx| {
|
||||
let background_color = self.background_color();
|
||||
let hover_background_color = hover
|
||||
.as_ref()
|
||||
.map(|hover_style| hover_style.background_color())
|
||||
.unwrap_or_default();
|
||||
let group_hover_background_color = group_hover
|
||||
.as_ref()
|
||||
.and_then(|(_, group_hover_style)| {
|
||||
Some(group_hover_style.as_ref()?.background_color())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
cx.with_z_index(2, |cx| {
|
||||
continuation(cx);
|
||||
});
|
||||
if !background_color.is_transparent()
|
||||
|| !hover_background_color.is_transparent()
|
||||
|| !group_hover_background_color.is_transparent()
|
||||
{
|
||||
cx.with_z_index(1, |cx| {
|
||||
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
|
||||
|
||||
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 mut border_color = background_color;
|
||||
border_color.a = 0.;
|
||||
let base_quad = quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
background_color,
|
||||
Edges::default(),
|
||||
border_color,
|
||||
);
|
||||
|
||||
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 hover_quad = if hover_background_color.is_transparent() {
|
||||
None
|
||||
} else {
|
||||
let mut border_color = hover_background_color;
|
||||
border_color.a = 0.;
|
||||
Some(quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
hover_background_color,
|
||||
Edges::default(),
|
||||
border_color,
|
||||
))
|
||||
};
|
||||
|
||||
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(),
|
||||
);
|
||||
let group_hover_quad = group_hover.as_ref().map(|(group_id, _)| {
|
||||
let quad = if group_hover_background_color.is_transparent() {
|
||||
None
|
||||
} else {
|
||||
let mut border_color = group_hover_background_color;
|
||||
border_color.a = 0.;
|
||||
Some(quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
group_hover_background_color,
|
||||
Edges::default(),
|
||||
border_color,
|
||||
))
|
||||
};
|
||||
(group_id.clone(), quad)
|
||||
});
|
||||
|
||||
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
|
||||
cx.paint_quad(quad.clone());
|
||||
cx.paint_quad(base_quad, hover_quad, group_hover_quad);
|
||||
});
|
||||
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)]
|
||||
if self.debug_below {
|
||||
cx.remove_global::<DebugBelow>();
|
||||
cx.with_z_index(2, |cx| {
|
||||
continuation(cx);
|
||||
});
|
||||
|
||||
let border_color = self.border_color();
|
||||
let hover_border_color = hover
|
||||
.as_ref()
|
||||
.map(|hover_style| hover_style.border_color())
|
||||
.unwrap_or_default();
|
||||
let group_hover_border_color = group_hover
|
||||
.as_ref()
|
||||
.and_then(|(_, group_hover_style)| Some(group_hover_style.as_ref()?.border_color()))
|
||||
.unwrap_or_default();
|
||||
if self.border_widths.any(|width| !width.is_zero())
|
||||
&& (!border_color.is_transparent()
|
||||
|| !hover_border_color.is_transparent()
|
||||
|| !group_hover_border_color.is_transparent())
|
||||
{
|
||||
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 = border_color;
|
||||
background.a = 0.;
|
||||
let border_quad = quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
background,
|
||||
border_widths,
|
||||
border_color,
|
||||
);
|
||||
let hover_border_quad = if hover_border_color.is_transparent() {
|
||||
None
|
||||
} else {
|
||||
let mut background = hover_border_color;
|
||||
background.a = 0.;
|
||||
Some(quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
background,
|
||||
border_widths,
|
||||
hover_border_color,
|
||||
))
|
||||
};
|
||||
let group_hover_border_quad = group_hover.as_ref().map(|(group_id, _)| {
|
||||
let quad = if group_hover_border_color.is_transparent() {
|
||||
None
|
||||
} else {
|
||||
let mut background = group_hover_border_color;
|
||||
background.a = 0.;
|
||||
Some(quad(
|
||||
bounds,
|
||||
corner_radii,
|
||||
background,
|
||||
border_widths,
|
||||
group_hover_border_color,
|
||||
))
|
||||
};
|
||||
(group_id.clone(), quad)
|
||||
});
|
||||
|
||||
cx.with_content_mask(Some(ContentMask { bounds: top_bounds }), |cx| {
|
||||
cx.paint_quad(
|
||||
border_quad.clone(),
|
||||
hover_border_quad.clone(),
|
||||
group_hover_border_quad.clone(),
|
||||
);
|
||||
});
|
||||
cx.with_content_mask(
|
||||
Some(ContentMask {
|
||||
bounds: right_bounds,
|
||||
}),
|
||||
|cx| {
|
||||
cx.paint_quad(
|
||||
border_quad.clone(),
|
||||
hover_border_quad.clone(),
|
||||
group_hover_border_quad.clone(),
|
||||
);
|
||||
},
|
||||
);
|
||||
cx.with_content_mask(
|
||||
Some(ContentMask {
|
||||
bounds: bottom_bounds,
|
||||
}),
|
||||
|cx| {
|
||||
cx.paint_quad(
|
||||
border_quad.clone(),
|
||||
hover_border_quad.clone(),
|
||||
group_hover_border_quad.clone(),
|
||||
);
|
||||
},
|
||||
);
|
||||
cx.with_content_mask(
|
||||
Some(ContentMask {
|
||||
bounds: left_bounds,
|
||||
}),
|
||||
|cx| {
|
||||
cx.paint_quad(border_quad, hover_border_quad, group_hover_border_quad);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if self.debug_below {
|
||||
cx.remove_global::<DebugBelow>();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the background color of the style based on the visibility.
|
||||
/// If the visibility is `Visible`, it returns the background color of the style if set,
|
||||
/// otherwise it returns the default color. If the visibility is `Hidden`, it returns
|
||||
/// a transparent black color.
|
||||
fn background_color(&self) -> Hsla {
|
||||
match self.visibility {
|
||||
Visibility::Visible => self
|
||||
.background
|
||||
.as_ref()
|
||||
.and_then(Fill::color)
|
||||
.unwrap_or_default(),
|
||||
Visibility::Hidden => Hsla::transparent_black(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_border_visible(&self) -> bool {
|
||||
self.border_color
|
||||
.map_or(false, |color| !color.is_transparent())
|
||||
&& self.border_widths.any(|length| !length.is_zero())
|
||||
/// Returns the border color of the style based on the visibility.
|
||||
/// If the visibility is `Visible`, it returns the border color of the style if set,
|
||||
/// otherwise it returns the default color. If the visibility is `Hidden`, it returns
|
||||
/// a transparent black color.
|
||||
fn border_color(&self) -> Hsla {
|
||||
match self.visibility {
|
||||
Visibility::Visible => self.border_color.unwrap_or_default(),
|
||||
Visibility::Hidden => Hsla::transparent_black(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,13 +130,17 @@ fn paint_line(
|
|||
if wraps.peek() == Some(&&WrapBoundary { run_ix, glyph_ix }) {
|
||||
wraps.next();
|
||||
if let Some((background_origin, background_color)) = current_background.as_mut() {
|
||||
cx.paint_quad(fill(
|
||||
Bounds {
|
||||
origin: *background_origin,
|
||||
size: size(glyph_origin.x - background_origin.x, line_height),
|
||||
},
|
||||
*background_color,
|
||||
));
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds {
|
||||
origin: *background_origin,
|
||||
size: size(glyph_origin.x - background_origin.x, line_height),
|
||||
},
|
||||
*background_color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
background_origin.x = origin.x;
|
||||
background_origin.y += line_height;
|
||||
}
|
||||
|
@ -229,13 +233,17 @@ fn paint_line(
|
|||
}
|
||||
|
||||
if let Some((background_origin, background_color)) = finished_background {
|
||||
cx.paint_quad(fill(
|
||||
Bounds {
|
||||
origin: background_origin,
|
||||
size: size(glyph_origin.x - background_origin.x, line_height),
|
||||
},
|
||||
background_color,
|
||||
));
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds {
|
||||
origin: background_origin,
|
||||
size: size(glyph_origin.x - background_origin.x, line_height),
|
||||
},
|
||||
background_color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||
|
@ -289,13 +297,17 @@ fn paint_line(
|
|||
}
|
||||
|
||||
if let Some((background_origin, background_color)) = current_background.take() {
|
||||
cx.paint_quad(fill(
|
||||
Bounds {
|
||||
origin: background_origin,
|
||||
size: size(last_line_end_x - background_origin.x, line_height),
|
||||
},
|
||||
background_color,
|
||||
));
|
||||
cx.paint_quad(
|
||||
fill(
|
||||
Bounds {
|
||||
origin: background_origin,
|
||||
size: size(last_line_end_x - background_origin.x, line_height),
|
||||
},
|
||||
background_color,
|
||||
),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((underline_start, underline_style)) = current_underline.take() {
|
||||
|
|
|
@ -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.
|
||||
/// The one exception is when [WindowContext::refresh] is called, in which case caching is ignored.
|
||||
pub fn cached(mut self) -> Self {
|
||||
self.cache = true;
|
||||
// TODO!: ENABLE ME!
|
||||
// self.cache = true;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ use crate::{
|
|||
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
|
||||
Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
|
||||
PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
|
||||
TaffyLayoutEngine, Task, View, VisualContext, WeakView, WindowAppearance, WindowBounds,
|
||||
PromptLevel, Quad, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
|
||||
TaffyLayoutEngine, Task, View, ViewId, VisualContext, WeakView, WindowAppearance, WindowBounds,
|
||||
WindowOptions, WindowTextSystem,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
|
@ -1043,9 +1043,10 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.layout_engine.as_mut().unwrap().clear();
|
||||
self.text_system()
|
||||
.finish_frame(&self.window.next_frame.reused_views);
|
||||
let mouse_position = self.window.mouse_position.scale(self.window.scale_factor);
|
||||
self.window
|
||||
.next_frame
|
||||
.finish(&mut self.window.rendered_frame);
|
||||
.finish(&mut self.window.rendered_frame, mouse_position);
|
||||
ELEMENT_ARENA.with_borrow_mut(|element_arena| {
|
||||
let percentage = (element_arena.len() as f32 / element_arena.capacity() as f32) * 100.;
|
||||
if percentage >= 80. {
|
||||
|
@ -2797,6 +2798,24 @@ impl PaintQuad {
|
|||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_primitive(
|
||||
self,
|
||||
view_id: impl Into<ViewId>,
|
||||
scale_factor: f32,
|
||||
content_mask: ContentMask<Pixels>,
|
||||
) -> Quad {
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
order: 0,
|
||||
bounds: self.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: self.background,
|
||||
border_color: self.border_color,
|
||||
corner_radii: self.corner_radii.scale(scale_factor),
|
||||
border_widths: self.border_widths.scale(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a quad with the given parameters.
|
||||
|
|
|
@ -34,9 +34,9 @@ use crate::{
|
|||
EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
|
||||
InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
|
||||
Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
|
||||
RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext,
|
||||
StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline, UnderlineStyle,
|
||||
Window, WindowContext, SUBPIXEL_VARIANTS,
|
||||
RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size,
|
||||
StackingContext, StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline,
|
||||
UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
|
||||
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
|
||||
|
@ -124,7 +124,7 @@ impl Frame {
|
|||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
|
||||
pub(crate) fn finish(&mut self, prev_frame: &mut Self, mouse_position: Point<ScaledPixels>) {
|
||||
// Reuse mouse listeners that didn't change since the last frame.
|
||||
for (type_id, listeners) in &mut prev_frame.mouse_listeners {
|
||||
let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
|
||||
|
@ -157,7 +157,7 @@ impl Frame {
|
|||
// Reuse geometry that didn't change since the last frame.
|
||||
self.scene
|
||||
.reuse_views(&self.reused_views, &mut prev_frame.scene);
|
||||
self.scene.finish();
|
||||
self.scene.finish(mouse_position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,6 +651,7 @@ impl<'a> ElementContext<'a> {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Paint one or more drop shadows into the scene for the next frame at the current z-index.
|
||||
pub fn paint_shadows(
|
||||
&mut self,
|
||||
|
@ -666,11 +667,9 @@ impl<'a> ElementContext<'a> {
|
|||
let mut shadow_bounds = bounds;
|
||||
shadow_bounds.origin += shadow.offset;
|
||||
shadow_bounds.dilate(shadow.spread_radius);
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_shadow(
|
||||
Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
|
@ -679,6 +678,8 @@ impl<'a> ElementContext<'a> {
|
|||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -686,17 +687,20 @@ impl<'a> ElementContext<'a> {
|
|||
/// Paint one or more quads into the scene for the next frame at the current stacking context.
|
||||
/// Quads are colored rectangular regions with an optional background, border, and corner radius.
|
||||
/// see [`fill`](crate::fill), [`outline`](crate::outline), and [`quad`](crate::quad) to construct this type.
|
||||
pub fn paint_quad(&mut self, quad: PaintQuad) {
|
||||
pub fn paint_quad(
|
||||
&mut self,
|
||||
quad: PaintQuad,
|
||||
hover: Option<PaintQuad>,
|
||||
group_hover: Option<(SharedString, Option<PaintQuad>)>,
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let content_mask = self.content_mask();
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_quad(
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
|
@ -705,6 +709,13 @@ impl<'a> ElementContext<'a> {
|
|||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
},
|
||||
hover.map(|quad| quad.into_primitive(view_id, scale_factor, content_mask.clone())),
|
||||
group_hover.map(|(group_id, quad)| {
|
||||
(
|
||||
group_id,
|
||||
quad.map(|quad| quad.into_primitive(view_id, scale_factor, content_mask)),
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -721,7 +732,7 @@ impl<'a> ElementContext<'a> {
|
|||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
|
||||
.insert_path(path.scale(scale_factor), None, None);
|
||||
}
|
||||
|
||||
/// Paint an underline into the scene for the next frame at the current z-index.
|
||||
|
@ -745,11 +756,9 @@ impl<'a> ElementContext<'a> {
|
|||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_underline(
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
|
@ -757,6 +766,8 @@ impl<'a> ElementContext<'a> {
|
|||
thickness: style.thickness.scale(scale_factor),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -777,11 +788,9 @@ impl<'a> ElementContext<'a> {
|
|||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_underline(
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
|
@ -789,6 +798,8 @@ impl<'a> ElementContext<'a> {
|
|||
color: style.color.unwrap_or_default(),
|
||||
wavy: false,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -837,17 +848,17 @@ impl<'a> ElementContext<'a> {
|
|||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_monochrome_sprite(
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -895,11 +906,9 @@ impl<'a> ElementContext<'a> {
|
|||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_polychrome_sprite(
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
|
@ -908,6 +917,8 @@ impl<'a> ElementContext<'a> {
|
|||
grayscale: false,
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
@ -941,17 +952,17 @@ impl<'a> ElementContext<'a> {
|
|||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_monochrome_sprite(
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -980,11 +991,9 @@ impl<'a> ElementContext<'a> {
|
|||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_polychrome_sprite(
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
|
@ -993,28 +1002,34 @@ impl<'a> ElementContext<'a> {
|
|||
grayscale,
|
||||
pad: 0,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Paint a surface into the scene for the next frame at the current z-index.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVImageBuffer) {
|
||||
pub fn paint_surface(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
image_buffer: CVImageBuffer,
|
||||
occludes_hover: bool,
|
||||
) {
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
window.next_frame.scene.insert_surface(
|
||||
crate::Surface {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
image_buffer,
|
||||
},
|
||||
occludes_hover,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1161,6 +1176,22 @@ impl<'a> ElementContext<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Invoke the given function with the given hover group id present on the hover stack.
|
||||
/// This is a fairly low-level method used to paint hover effects for views that share
|
||||
/// the same hover group.
|
||||
pub fn with_hover_group<R>(
|
||||
&mut self,
|
||||
name: Option<SharedString>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
let window = &mut self.window;
|
||||
let group = window.next_frame.scene.hover_group(name);
|
||||
window.hover_group_stack.push(group);
|
||||
let result = f(self);
|
||||
window.hover_group_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
/// Sets an input handler, such as [`ElementInputHandler`][element_input_handler], which interfaces with the
|
||||
/// platform to receive textual input with proper integration with concerns such
|
||||
/// as IME interactions. This handler will be active for the upcoming frame until the following frame is
|
||||
|
|
|
@ -133,7 +133,7 @@ impl LayoutRect {
|
|||
)
|
||||
.into();
|
||||
|
||||
cx.paint_quad(fill(Bounds::new(position, size), self.color));
|
||||
cx.paint_quad(fill(Bounds::new(position, size), self.color), None, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,7 +782,7 @@ impl Element for TerminalElement {
|
|||
) {
|
||||
let mut layout = self.compute_layout(bounds, cx);
|
||||
|
||||
cx.paint_quad(fill(bounds, layout.background_color));
|
||||
cx.paint_quad(fill(bounds, layout.background_color), None, None);
|
||||
let origin = bounds.origin + Point::new(layout.gutter, px(0.));
|
||||
|
||||
let terminal_input_handler = TerminalInputHandler {
|
||||
|
|
|
@ -766,7 +766,11 @@ mod element {
|
|||
}
|
||||
|
||||
cx.add_opaque_layer(handle_bounds);
|
||||
cx.paint_quad(gpui::fill(divider_bounds, cx.theme().colors().border));
|
||||
cx.paint_quad(
|
||||
gpui::fill(divider_bounds, cx.theme().colors().border),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
cx.on_mouse_event({
|
||||
let dragged_handle = dragged_handle.clone();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue