Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/gpui/src/elements/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ impl StateInner {
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
item_origin.y -= layout_response.scroll_top.offset_in_item;
for item in &mut layout_response.item_layouts {
window.with_content_mask(Some(ContentMask { bounds }), |window| {
window.with_content_mask(Some(ContentMask::new(bounds)), |window| {
item.element.prepaint_at(item_origin, window, cx);
});

Expand Down Expand Up @@ -1595,7 +1595,7 @@ impl Element for List {
cx: &mut App,
) {
let current_view = window.current_view();
window.with_content_mask(Some(ContentMask { bounds }), |window| {
window.with_content_mask(Some(ContentMask::new(bounds)), |window| {
for item in &mut prepaint.layout.item_layouts {
item.element.paint(window, cx);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/gpui/src/elements/uniform_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ impl Element for UniformList {
(self.render_items)(visible_range.clone(), window, cx)
};

let content_mask = ContentMask { bounds };
let content_mask = ContentMask::new(bounds);
window.with_content_mask(Some(content_mask), |window| {
for (mut item, ix) in items.into_iter().zip(visible_range.clone()) {
let item_origin = padded_bounds.origin
Expand Down
49 changes: 30 additions & 19 deletions crates/gpui/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ mod tests {
order,
pad: 0,
bounds,
content_mask: ContentMask { bounds },
content_mask: ContentMask::new(bounds),
corner_radii: Corners::all(ScaledPixels(2.0)),
blur_radius: ScaledPixels(12.0),
source_origin_x: 0.0,
Expand All @@ -1153,18 +1153,29 @@ mod tests {
}
}

#[test]
fn content_mask_gpu_layout_matches_shader_storage_contract() {
assert_eq!(align_of::<ContentMask<ScaledPixels>>(), 4);
assert_eq!(size_of::<ContentMask<ScaledPixels>>(), 48);
assert_eq!(offset_of!(ContentMask<ScaledPixels>, bounds), 0);
assert_eq!(offset_of!(ContentMask<ScaledPixels>, rounded_bounds), 16);
assert_eq!(offset_of!(ContentMask<ScaledPixels>, corner_radii), 32);
}

#[test]
fn backdrop_blur_gpu_layout_matches_shader_storage_contract() {
assert_eq!(align_of::<BackdropBlur>(), 4);
assert_eq!(size_of::<BackdropBlur>(), 80);
assert_eq!(size_of::<BackdropBlur>(), 112);
assert_eq!(offset_of!(BackdropBlur, order), 0);
assert_eq!(offset_of!(BackdropBlur, bounds), 8);
assert_eq!(offset_of!(BackdropBlur, content_mask), 24);
assert_eq!(offset_of!(BackdropBlur, corner_radii), 40);
assert_eq!(offset_of!(BackdropBlur, blur_radius), 56);
assert_eq!(offset_of!(BackdropBlur, source_origin_x), 60);
assert_eq!(offset_of!(BackdropBlur, source_height), 72);
assert_eq!(offset_of!(BackdropBlur, pad2), 76);
assert_eq!(offset_of!(BackdropBlur, corner_radii), 72);
assert_eq!(offset_of!(BackdropBlur, blur_radius), 88);
assert_eq!(offset_of!(BackdropBlur, source_origin_x), 92);
assert_eq!(offset_of!(BackdropBlur, source_origin_y), 96);
assert_eq!(offset_of!(BackdropBlur, source_width), 100);
assert_eq!(offset_of!(BackdropBlur, source_height), 104);
assert_eq!(offset_of!(BackdropBlur, pad2), 108);
}

#[test]
Expand All @@ -1178,26 +1189,26 @@ mod tests {
#[test]
fn underline_gpu_layout_matches_shader_storage_contract() {
assert_eq!(align_of::<Underline>(), 4);
assert_eq!(size_of::<Underline>(), 64);
assert_eq!(size_of::<Underline>(), 96);
assert_eq!(offset_of!(Underline, order), 0);
assert_eq!(offset_of!(Underline, bounds), 8);
assert_eq!(offset_of!(Underline, content_mask), 24);
assert_eq!(offset_of!(Underline, color), 40);
assert_eq!(offset_of!(Underline, thickness), 56);
assert_eq!(offset_of!(Underline, wavy), 60);
assert_eq!(offset_of!(Underline, color), 72);
assert_eq!(offset_of!(Underline, thickness), 88);
assert_eq!(offset_of!(Underline, wavy), 92);
}

#[test]
fn polychrome_sprite_gpu_layout_matches_shader_storage_contract() {
assert_eq!(align_of::<PolychromeSprite>(), 4);
assert_eq!(size_of::<PolychromeSprite>(), 96);
assert_eq!(size_of::<PolychromeSprite>(), 128);
assert_eq!(offset_of!(PolychromeSprite, order), 0);
assert_eq!(offset_of!(PolychromeSprite, grayscale), 8);
assert_eq!(offset_of!(PolychromeSprite, opacity), 12);
assert_eq!(offset_of!(PolychromeSprite, bounds), 16);
assert_eq!(offset_of!(PolychromeSprite, content_mask), 32);
assert_eq!(offset_of!(PolychromeSprite, corner_radii), 48);
assert_eq!(offset_of!(PolychromeSprite, tile), 64);
assert_eq!(offset_of!(PolychromeSprite, corner_radii), 80);
assert_eq!(offset_of!(PolychromeSprite, tile), 96);
}

#[test]
Expand All @@ -1209,13 +1220,13 @@ mod tests {
Quad {
order: 4,
bounds,
content_mask: ContentMask { bounds },
content_mask: ContentMask::new(bounds),
..Default::default()
},
Quad {
order: 1,
bounds,
content_mask: ContentMask { bounds },
content_mask: ContentMask::new(bounds),
..Default::default()
},
];
Expand Down Expand Up @@ -1258,7 +1269,7 @@ mod tests {
#[test]
fn replay_retained_layer_ranges_and_marks_content_clean() {
let bounds = test_bounds();
let content_mask = ContentMask { bounds };
let content_mask = ContentMask::new(bounds);
let mut prev_scene = Scene::default();

prev_scene.insert_primitive(Quad {
Expand Down Expand Up @@ -1290,7 +1301,7 @@ mod tests {
#[test]
fn replay_offsets_retained_layer_ranges_after_existing_ops() {
let bounds = test_bounds();
let content_mask = ContentMask { bounds };
let content_mask = ContentMask::new(bounds);
let mut prev_scene = Scene::default();

prev_scene.insert_primitive(Quad {
Expand Down Expand Up @@ -1333,7 +1344,7 @@ mod tests {
blur_radius: ScaledPixels(0.0),
bounds,
corner_radii: Corners::all(ScaledPixels(0.0)),
content_mask: ContentMask { bounds },
content_mask: ContentMask::new(bounds),
color: Hsla::default(),
element_bounds,
element_corner_radii: Corners::all(ScaledPixels(0.0)),
Expand Down
132 changes: 114 additions & 18 deletions crates/gpui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,40 +649,72 @@ impl Style {
y: Overflow::Visible,
} => None,
_ => {
let element_size = bounds.size;
let mut min = bounds.origin;
let mut max = bounds.bottom_right();

if self
let border_widths = self.border_widths.to_pixels(rem_size);
let has_visible_border = self
.border_color
.is_some_and(|color| !color.is_transparent())
{
min.x += self.border_widths.left.to_pixels(rem_size);
max.x -= self.border_widths.right.to_pixels(rem_size);
min.y += self.border_widths.top.to_pixels(rem_size);
max.y -= self.border_widths.bottom.to_pixels(rem_size);
.is_some_and(|color| !color.is_transparent());

if has_visible_border {
min.x += border_widths.left;
max.x -= border_widths.right;
min.y += border_widths.top;
max.y -= border_widths.bottom;
}

let bounds = match (
let (bounds, clips_both_axes) = match (
self.overflow.x == Overflow::Visible,
self.overflow.y == Overflow::Visible,
) {
// x and y both visible
(true, true) => return None,
// x visible, y hidden
(true, false) => Bounds::from_corners(
point(min.x, bounds.origin.y),
point(max.x, bounds.bottom_right().y),
(true, false) => (
Bounds::from_corners(
point(bounds.origin.x, min.y),
point(bounds.bottom_right().x, max.y),
),
false,
),
// x hidden, y visible
(false, true) => Bounds::from_corners(
point(bounds.origin.x, min.y),
point(bounds.bottom_right().x, max.y),
(false, true) => (
Bounds::from_corners(
point(min.x, bounds.origin.y),
point(max.x, bounds.bottom_right().y),
),
false,
),
// both hidden
(false, false) => Bounds::from_corners(min, max),
(false, false) => (Bounds::from_corners(min, max), true),
};

Some(ContentMask { bounds })
let mut corner_radii = self
.corner_radii
.to_pixels(rem_size)
.clamp_radii_for_quad_size(element_size);
if has_visible_border {
corner_radii.top_left = (corner_radii.top_left
- border_widths.top.max(border_widths.left))
.max(Pixels::ZERO);
corner_radii.top_right = (corner_radii.top_right
- border_widths.top.max(border_widths.right))
.max(Pixels::ZERO);
corner_radii.bottom_right = (corner_radii.bottom_right
- border_widths.bottom.max(border_widths.right))
.max(Pixels::ZERO);
corner_radii.bottom_left = (corner_radii.bottom_left
- border_widths.bottom.max(border_widths.left))
.max(Pixels::ZERO);
corner_radii = corner_radii.clamp_radii_for_quad_size(bounds.size);
}

if clips_both_axes && corner_radii != Corners::default() {
Some(ContentMask::rounded(bounds, corner_radii))
} else {
Some(ContentMask::new(bounds))
}
}
}
}
Expand Down Expand Up @@ -1338,10 +1370,74 @@ impl From<Position> for taffy::style::Position {

#[cfg(test)]
mod tests {
use crate::{blue, green, px, red, yellow};
use crate::{Bounds, blue, green, px, red, size, yellow};

use super::*;

#[test]
fn overflow_mask_preserves_rounded_clip_geometry() {
let bounds = Bounds::new(point(px(10.), px(20.)), size(px(100.), px(80.)));
let style = Style {
overflow: point(Overflow::Hidden, Overflow::Hidden),
corner_radii: Corners::all(px(12.).into()),
..Default::default()
};

assert_eq!(
style.overflow_mask(bounds, px(16.)),
Some(ContentMask::rounded(bounds, Corners::all(px(12.))))
);
}

#[test]
fn overflow_mask_stays_rectangular_when_only_one_axis_is_clipped() {
let bounds = Bounds::new(point(px(10.), px(20.)), size(px(100.), px(80.)));
let style = Style {
overflow: point(Overflow::Hidden, Overflow::Visible),
corner_radii: Corners::all(px(12.).into()),
..Default::default()
};

assert_eq!(
style.overflow_mask(bounds, px(16.)),
Some(ContentMask::new(bounds))
);
}

#[test]
fn overflow_mask_insets_only_the_clipped_axis_for_visible_borders() {
let bounds = Bounds::new(point(px(10.), px(20.)), size(px(100.), px(80.)));
let base_style = Style {
border_widths: Edges::all(px(2.).into()),
border_color: Some(red()),
..Default::default()
};

let x_clipped = Style {
overflow: point(Overflow::Hidden, Overflow::Visible),
..base_style.clone()
};
assert_eq!(
x_clipped.overflow_mask(bounds, px(16.)),
Some(ContentMask::new(Bounds::from_corners(
point(px(12.), px(20.)),
point(px(108.), px(100.)),
)))
);

let y_clipped = Style {
overflow: point(Overflow::Visible, Overflow::Hidden),
..base_style
};
assert_eq!(
y_clipped.overflow_mask(bounds, px(16.)),
Some(ContentMask::new(Bounds::from_corners(
point(px(10.), px(22.)),
point(px(110.), px(98.)),
)))
);
}

#[test]
fn box_shadow_builder_sets_css_ordered_fields_and_defaults() {
let color = red();
Expand Down
Loading
Loading