diff --git a/crates/collab_ui/src/face_pile.rs b/crates/collab_ui/src/face_pile.rs index cc47a057ac869bea6ef17cad92f46a0df03934ed..71d15eb155101b2ec91fa61dab44744e55f51375 100644 --- a/crates/collab_ui/src/face_pile.rs +++ b/crates/collab_ui/src/face_pile.rs @@ -28,7 +28,7 @@ impl RenderOnce for FacePile { let isnt_last = ix < player_count - 1; div() - .z_index((player_count - ix) as u8) + .z_index((player_count - ix) as u16) .when(isnt_last, |div| div.neg_mr_1()) .child(player) }); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 87dfce135b62b2160d77d07f7d814c6634846233..eeb8263f30fa1449350bf7872d45866cadadb54f 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -3169,7 +3169,7 @@ pub struct CursorName { string: SharedString, color: Hsla, is_top_row: bool, - z_index: u8, + z_index: u16, } impl Cursor { diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index c50cf34cfa6b8c7cb6bb40f32cfcd4ed1926f074..8cba0f8fc37b577a1adec9da8a8a2f4fd64e1017 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -110,7 +110,7 @@ pub struct Style { /// The mouse cursor style shown when the mouse pointer is over an element. pub mouse_cursor: Option, - pub z_index: Option, + pub z_index: Option, #[cfg(debug_assertions)] pub debug: bool, @@ -337,7 +337,7 @@ 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(0, |cx| { + cx.with_z_index(1, |cx| { let mut border_color = background_color.unwrap_or_default(); border_color.a = 0.; cx.paint_quad(quad( @@ -350,12 +350,12 @@ impl Style { }); } - cx.with_z_index(0, |cx| { + cx.with_z_index(2, |cx| { continuation(cx); }); if self.is_border_visible() { - cx.with_z_index(0, |cx| { + 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(); diff --git a/crates/gpui/src/styled.rs b/crates/gpui/src/styled.rs index ee27741642b44b6b3c3e8dc819313dc91b079a3c..3866dc65c7b400caaba55da22dca704ff893cbc0 100644 --- a/crates/gpui/src/styled.rs +++ b/crates/gpui/src/styled.rs @@ -12,7 +12,7 @@ pub trait Styled: Sized { gpui_macros::style_helpers!(); - fn z_index(mut self, z_index: u8) -> Self { + fn z_index(mut self, z_index: u16) -> Self { self.style().z_index = Some(z_index); self } diff --git a/crates/gpui/src/view.rs b/crates/gpui/src/view.rs index c6a4d4b52c588716a5c1599ed7691bd07711aa24..47f2809038d650e1f74de96019f134e05f9d6a20 100644 --- a/crates/gpui/src/view.rs +++ b/crates/gpui/src/view.rs @@ -23,6 +23,7 @@ impl Sealed for View {} #[doc(hidden)] pub struct AnyViewState { root_style: Style, + next_stacking_order_id: u16, cache_key: Option, element: Option, } @@ -292,6 +293,7 @@ impl Element for AnyView { let root_style = cx.layout_style(layout_id).unwrap().clone(); let state = AnyViewState { root_style, + next_stacking_order_id: 0, cache_key: None, element: Some(element), }; @@ -314,7 +316,7 @@ impl Element for AnyView { && !cx.window.dirty_views.contains(&self.entity_id()) && !cx.window.refreshing { - cx.reuse_view(); + cx.reuse_view(state.next_stacking_order_id); return; } } @@ -326,6 +328,7 @@ impl Element for AnyView { element.draw(bounds.origin, bounds.size.into(), cx); } + state.next_stacking_order_id = cx.window.next_frame.next_stacking_order_id; state.cache_key = Some(ViewCacheKey { bounds, stacking_order: cx.stacking_order().clone(), diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 03d64f6b1c06c192abf672b165833ee06b0b183f..06817158c472ed905761ffe3637acb0a8c698a46 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -39,30 +39,23 @@ use util::{measure, ResultExt}; mod element_cx; pub use element_cx::*; -const ACTIVE_DRAG_Z_INDEX: u8 = 1; +const ACTIVE_DRAG_Z_INDEX: u16 = 1; /// A global stacking order, which is created by stacking successive z-index values. /// Each z-index will always be interpreted in the context of its parent z-index. -#[derive(Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)] -pub struct StackingOrder { - #[deref] - #[deref_mut] - context_stack: SmallVec<[u8; 64]>, - pub(crate) id: u32, +#[derive(Debug, Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)] +pub struct StackingOrder(SmallVec<[StackingContext; 64]>); + +/// A single entry in a primitive's z-index stacking order +#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Default)] +pub struct StackingContext { + pub(crate) z_index: u16, + pub(crate) id: u16, } -impl std::fmt::Debug for StackingOrder { +impl std::fmt::Debug for StackingContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut stacks = self.context_stack.iter().peekable(); - write!(f, "[({}): ", self.id)?; - while let Some(z_index) = stacks.next() { - write!(f, "{z_index}")?; - if stacks.peek().is_some() { - write!(f, "->")?; - } - } - write!(f, "]")?; - Ok(()) + write!(f, "{{{}.{}}} ", self.z_index, self.id) } } @@ -807,18 +800,39 @@ impl<'a> WindowContext<'a> { } /// Returns true if there is no opaque layer containing the given point - /// on top of the given level. Layers whose level is an extension of the - /// level are not considered to be on top of the level. - pub fn was_top_layer(&self, point: &Point, level: &StackingOrder) -> bool { - for (opaque_level, _, bounds) in self.window.rendered_frame.depth_map.iter() { - if level >= opaque_level { - break; + /// on top of the given level. Layers who are extensions of the queried layer + /// are not considered to be on top of queried layer. + pub fn was_top_layer(&self, point: &Point, layer: &StackingOrder) -> bool { + // Precondition: the depth map is ordered from topmost to bottomost. + + for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() { + if layer >= opaque_layer { + // The queried layer is either above or is the same as the this opaque layer. + // Anything after this point is guaranteed to be below the queried layer. + return true; + } + + if !bounds.contains(point) { + // This opaque layer is above the queried layer but it doesn't contain + // the given position, so we can ignore it even if it's above. + continue; } - if bounds.contains(point) && !opaque_level.starts_with(level) { + // At this point, we've established that this opaque layer is on top of the queried layer + // and contains the position: + // - If the opaque layer is an extension of the queried layer, we don't want + // to consider the opaque layer to be on top and so we ignore it. + // - Else, we will bail early and say that the queried layer wasn't the top one. + let opaque_layer_is_extension_of_queried_layer = opaque_layer.len() >= layer.len() + && opaque_layer + .iter() + .zip(layer.iter()) + .all(|(a, b)| a.z_index == b.z_index); + if !opaque_layer_is_extension_of_queried_layer { return false; } } + true } @@ -831,11 +845,16 @@ impl<'a> WindowContext<'a> { if level >= opaque_level { break; } - if opaque_level.starts_with(&[ACTIVE_DRAG_Z_INDEX]) { + + if opaque_level + .first() + .map(|c| c.z_index == ACTIVE_DRAG_Z_INDEX) + .unwrap_or(false) + { continue; } - if bounds.contains(point) && !opaque_level.starts_with(level) { + if bounds.contains(point) { return false; } } diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index 9274082c5dcae29f971f1a2f386c1eb4866c3195..132cc72a5c7d344cf3dedbbc693ff2306b5bb8b9 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -19,9 +19,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, StackingOrder, Style, - Surface, TextStyleRefinement, Underline, UnderlineStyle, Window, WindowContext, - SUBPIXEL_VARIANTS, + RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext, + StackingOrder, Style, Surface, TextStyleRefinement, Underline, UnderlineStyle, Window, + WindowContext, SUBPIXEL_VARIANTS, }; type AnyMouseListener = Box; @@ -45,8 +45,8 @@ pub(crate) struct Frame { pub(crate) scene: Scene, pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds)>, pub(crate) z_index_stack: StackingOrder, - pub(crate) next_stacking_order_id: u32, - pub(crate) next_root_z_index: u8, + pub(crate) next_stacking_order_id: u16, + pub(crate) next_root_z_index: u16, pub(crate) content_mask_stack: Vec>, pub(crate) element_offset_stack: Vec>, pub(crate) requested_input_handler: Option, @@ -292,7 +292,7 @@ impl<'a> VisualContext for ElementContext<'a> { } impl<'a> ElementContext<'a> { - pub(crate) fn reuse_view(&mut self) { + pub(crate) fn reuse_view(&mut self, next_stacking_order_id: u16) { let view_id = self.parent_view_id(); let grafted_view_ids = self .cx @@ -333,6 +333,9 @@ impl<'a> ElementContext<'a> { self.window.next_frame.requested_cursor_style = Some(style); } } + + debug_assert!(next_stacking_order_id >= self.window.next_frame.next_stacking_order_id); + self.window.next_frame.next_stacking_order_id = next_stacking_order_id; } pub fn with_text_style(&mut self, style: Option, f: F) -> R @@ -410,36 +413,40 @@ impl<'a> ElementContext<'a> { size: self.window().viewport_size, }, }; + + let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index); let new_stacking_order_id = post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); - let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index); + let new_context = StackingContext { + z_index: new_root_z_index, + id: new_stacking_order_id, + }; + let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack); - self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; - self.window_mut() - .next_frame - .z_index_stack - .push(new_root_z_index); + + self.window_mut().next_frame.z_index_stack.push(new_context); self.window_mut().next_frame.content_mask_stack.push(mask); let result = f(self); self.window_mut().next_frame.content_mask_stack.pop(); self.window_mut().next_frame.z_index_stack = old_stacking_order; + result } /// Called during painting to invoke the given closure in a new stacking context. The given /// z-index is interpreted relative to the previous call to `stack`. - pub fn with_z_index(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R { + pub fn with_z_index(&mut self, z_index: u16, f: impl FnOnce(&mut Self) -> R) -> R { let new_stacking_order_id = post_inc(&mut self.window_mut().next_frame.next_stacking_order_id); - let old_stacking_order_id = mem::replace( - &mut self.window_mut().next_frame.z_index_stack.id, - new_stacking_order_id, - ); - self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id; - self.window_mut().next_frame.z_index_stack.push(z_index); + let new_context = StackingContext { + z_index, + id: new_stacking_order_id, + }; + + self.window_mut().next_frame.z_index_stack.push(new_context); let result = f(self); - self.window_mut().next_frame.z_index_stack.id = old_stacking_order_id; self.window_mut().next_frame.z_index_stack.pop(); + result } diff --git a/crates/storybook/src/stories/z_index.rs b/crates/storybook/src/stories/z_index.rs index b6e49bfae32b046242e96a5de34d30c3be806b94..63ee1af7591ee8a62f07f052b320db8a70077b9d 100644 --- a/crates/storybook/src/stories/z_index.rs +++ b/crates/storybook/src/stories/z_index.rs @@ -76,7 +76,7 @@ impl Styles for Div {} #[derive(IntoElement)] struct ZIndexExample { - z_index: u8, + z_index: u16, } impl RenderOnce for ZIndexExample { @@ -166,7 +166,7 @@ impl RenderOnce for ZIndexExample { } impl ZIndexExample { - pub fn new(z_index: u8) -> Self { + pub fn new(z_index: u16) -> Self { Self { z_index } } } diff --git a/crates/ui/src/styles/elevation.rs b/crates/ui/src/styles/elevation.rs index 0aa3786a279242c8a3a301b497a60ab0c2c537ec..c2605fd152df49d04db57d6784bc3a54aaefd80d 100644 --- a/crates/ui/src/styles/elevation.rs +++ b/crates/ui/src/styles/elevation.rs @@ -20,7 +20,7 @@ pub enum ElevationIndex { } impl ElevationIndex { - pub fn z_index(self) -> u8 { + pub fn z_index(self) -> u16 { match self { ElevationIndex::Background => 0, ElevationIndex::Surface => 42, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d10cef0c0f41ec18a513bbae5d478c3ff0deb3ac..b3658b6f5a191e57bbfd64f630b008f58cf98cb9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4326,7 +4326,7 @@ impl Element for DisconnectedOverlay { overlay: &mut Self::State, cx: &mut ElementContext, ) { - cx.with_z_index(u8::MAX, |cx| { + cx.with_z_index(u16::MAX, |cx| { cx.add_opaque_layer(bounds); overlay.paint(cx); })