diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 406f2ea311..6bf24f750b 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -755,6 +755,14 @@ impl Interactivity { ) { let style = self.compute_style(Some(bounds), element_state, cx); + if style + .background + .as_ref() + .is_some_and(|fill| fill.color().is_some_and(|color| !color.is_transparent())) + { + cx.with_z_index(style.z_index.unwrap_or(0), |cx| cx.add_opaque_layer(bounds)) + } + if let Some(mouse_cursor) = style.mouse_cursor { let hovered = bounds.contains_point(&cx.mouse_position()); if hovered { @@ -1098,19 +1106,21 @@ impl Interactivity { } } } - // if self.hover_style.is_some() { - if bounds.contains_point(&mouse_position) { - // eprintln!("div hovered {bounds:?} {mouse_position:?}"); - style.refine(&self.hover_style); - } else { - // eprintln!("div NOT hovered {bounds:?} {mouse_position:?}"); + if self.hover_style.is_some() { + if bounds + .intersect(&cx.content_mask().bounds) + .contains_point(&mouse_position) + && cx.was_top_layer(&mouse_position, cx.stacking_order()) + { + style.refine(&self.hover_style); + } } - // } if let Some(drag) = cx.active_drag.take() { for (state_type, group_drag_style) in &self.group_drag_over_styles { if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { if *state_type == drag.view.entity_type() + // todo!() needs to handle cx.content_mask() and cx.is_top() && group_bounds.contains_point(&mouse_position) { style.refine(&group_drag_style.style); @@ -1120,7 +1130,10 @@ impl Interactivity { for (state_type, drag_over_style) in &self.drag_over_styles { if *state_type == drag.view.entity_type() - && bounds.contains_point(&mouse_position) + && bounds + .intersect(&cx.content_mask().bounds) + .contains_point(&mouse_position) + && cx.was_top_layer(&mouse_position, cx.stacking_order()) { style.refine(drag_over_style); } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 20561c5443..0d444d762e 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -12,7 +12,7 @@ use crate::{ VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Context as _, Result}; -use collections::HashMap; +use collections::{BTreeMap, HashMap}; use derive_more::{Deref, DerefMut}; use futures::{ channel::{mpsc, oneshot}, @@ -39,8 +39,8 @@ use util::ResultExt; /// 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, Ord, PartialOrd, Eq, PartialEq, Clone, Default)] -pub(crate) struct StackingOrder(pub(crate) SmallVec<[u32; 16]>); +#[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default, Debug)] +pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>); /// Represents the two different phases when dispatching events. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] @@ -243,7 +243,8 @@ pub(crate) struct Frame { pub(crate) dispatch_tree: DispatchTree, pub(crate) focus_listeners: Vec, pub(crate) scene_builder: SceneBuilder, - z_index_stack: StackingOrder, + pub(crate) depth_map: BTreeMap>, + pub(crate) z_index_stack: StackingOrder, content_mask_stack: Vec>, element_offset_stack: Vec>, } @@ -257,6 +258,7 @@ impl Frame { focus_listeners: Vec::new(), scene_builder: SceneBuilder::default(), z_index_stack: StackingOrder::default(), + depth_map: Default::default(), content_mask_stack: Vec::new(), element_offset_stack: Vec::new(), } @@ -806,6 +808,32 @@ impl<'a> WindowContext<'a> { result } + /// Called during painting to track which z-index is on top at each pixel position + pub fn add_opaque_layer(&mut self, bounds: Bounds) { + let stacking_order = self.window.current_frame.z_index_stack.clone(); + self.window + .current_frame + .depth_map + .insert(stacking_order, bounds); + } + + /// Returns true if the top-most opaque layer painted over this point was part of the + /// same layer as the given stacking order. + pub fn was_top_layer(&self, point: &Point, level: &StackingOrder) -> bool { + for (stack, bounds) in self.window.previous_frame.depth_map.iter() { + if bounds.contains_point(point) { + return level.starts_with(stack) || stack.starts_with(level); + } + } + + false + } + + /// Called during painting to get the current stacking order. + pub fn stacking_order(&self) -> &StackingOrder { + &self.window.current_frame.z_index_stack + } + /// Paint one or more drop shadows into the scene for the current frame at the current z-index. pub fn paint_shadows( &mut self, @@ -1153,6 +1181,7 @@ impl<'a> WindowContext<'a> { frame.mouse_listeners.values_mut().for_each(Vec::clear); frame.focus_listeners.clear(); frame.dispatch_tree.clear(); + frame.depth_map.clear(); } /// Dispatch a mouse or keyboard event on the window.