Initial fix of the z-index

Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Kirill Bulatov 2023-12-18 19:15:54 +02:00
parent a9fe108dbb
commit 825a8f0927
5 changed files with 181 additions and 65 deletions

View file

@ -1447,9 +1447,15 @@ impl Interactivity {
cx.on_action(action_type, listener) cx.on_action(action_type, listener)
} }
f(style, scroll_offset.unwrap_or_default(), cx) cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
}, if style.background.as_ref().is_some_and(|fill| {
); fill.color().is_some_and(|color| !color.is_transparent())
}) {
cx.add_opaque_layer(bounds)
}f(style, scroll_offset.unwrap_or_default(), cx)
})
},
);
if let Some(group) = self.group.as_ref() { if let Some(group) = self.group.as_ref() {
GroupBounds::pop(group, cx); GroupBounds::pop(group, cx);

View file

@ -857,55 +857,116 @@ impl Bounds<ScaledPixels> {
} }
} }
// #[cfg(test)] #[cfg(test)]
// mod tests { mod tests {
// use crate::{point, size}; use super::*;
use crate::{point, px, size, Size};
use smallvec::smallvec;
// use super::*; // todo!()
// use smallvec::smallvec; // #[test]
// fn test_scene() {
// let mut scene = SceneBuilder::default();
// assert_eq!(scene.layers_by_order.len(), 0);
// #[test] // // div with z_index(1)
// fn test_scene() { // // glyph with z_index(1)
// let mut scene = SceneBuilder::new(); // // div with z_index(1)
// assert_eq!(scene.layers_by_order.len(), 0); // // glyph with z_index(1)
// scene.insert(&smallvec![1].into(), quad()); // scene.insert(
// scene.insert(&smallvec![2].into(), shadow()); // &smallvec![1].into(),
// scene.insert(&smallvec![3].into(), quad()); // quad(
// point(px(0.), px(0.)),
// size(px(100.), px(100.)),
// crate::black(),
// ),
// );
// scene.insert(
// &smallvec![1, 1].into(),
// sprite(
// point(px(0.), px(0.)),
// size(px(10.), px(10.)),
// crate::white(),
// ),
// );
// scene.insert(
// &smallvec![1].into(),
// quad(
// point(px(10.), px(10.)),
// size(px(20.), px(20.)),
// crate::green(),
// ),
// );
// scene.insert(
// &smallvec![1, 1].into(),
// sprite(point(px(15.), px(15.)), size(px(5.), px(5.)), crate::blue()),
// );
// let mut batches_count = 0; // assert!(!scene.layers_by_order.is_empty());
// for _ in scene.build().batches() {
// batches_count += 1;
// }
// assert_eq!(batches_count, 3);
// }
// fn quad() -> Quad { // for batch in scene.build().batches() {
// Quad { // println!("new batch");
// order: 0, // match batch {
// bounds: Bounds { // PrimitiveBatch::Quads(quads) => {
// origin: point(ScaledPixels(0.), ScaledPixels(0.)), // for quad in quads {
// size: size(ScaledPixels(100.), ScaledPixels(100.)), // if quad.background == crate::black() {
// }, // println!(" black quad");
// content_mask: Default::default(), // } else if quad.background == crate::green() {
// background: Default::default(), // println!(" green quad");
// border_color: Default::default(), // } else {
// corner_radii: Default::default(), // todo!(" ((( bad quad");
// border_widths: Default::default(), // }
// } // }
// } // }
// PrimitiveBatch::MonochromeSprites { sprites, .. } => {
// for sprite in sprites {
// if sprite.color == crate::white() {
// println!(" white sprite");
// } else if sprite.color == crate::blue() {
// println!(" blue sprite");
// } else {
// todo!(" ((( bad sprite")
// }
// }
// }
// _ => todo!(),
// }
// }
// }
// fn shadow() -> Shadow { fn quad(origin: Point<Pixels>, size: Size<Pixels>, background: Hsla) -> Quad {
// Shadow { Quad {
// order: Default::default(), order: 0,
// bounds: Bounds { bounds: Bounds { origin, size }.scale(1.),
// origin: point(ScaledPixels(0.), ScaledPixels(0.)), background,
// size: size(ScaledPixels(100.), ScaledPixels(100.)), content_mask: ContentMask {
// }, bounds: Bounds { origin, size },
// corner_radii: Default::default(), }
// content_mask: Default::default(), .scale(1.),
// color: Default::default(), border_color: Default::default(),
// blur_radius: Default::default(), corner_radii: Default::default(),
// } border_widths: Default::default(),
// } }
// } }
fn sprite(origin: Point<Pixels>, size: Size<Pixels>, color: Hsla) -> MonochromeSprite {
MonochromeSprite {
order: 0,
bounds: Bounds { origin, size }.scale(1.),
content_mask: ContentMask {
bounds: Bounds { origin, size },
}
.scale(1.),
color,
tile: AtlasTile {
texture_id: AtlasTextureId {
index: 0,
kind: crate::AtlasTextureKind::Monochrome,
},
tile_id: crate::TileId(0),
bounds: Default::default(),
},
}
}
}

View file

@ -39,24 +39,36 @@ use std::{
Arc, Arc,
}, },
}; };
use util::ResultExt; use util::{post_inc, ResultExt};
const ACTIVE_DRAG_Z_INDEX: u8 = 1; const ACTIVE_DRAG_Z_INDEX: u8 = 1;
/// A global stacking order, which is created by stacking successive z-index values. /// 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. /// Each z-index will always be interpreted in the context of its parent z-index.
#[derive(Deref, DerefMut, Clone, Debug, Ord, PartialOrd, PartialEq, Eq)] #[derive(Deref, DerefMut, Clone, Ord, PartialOrd, PartialEq, Eq, Default)]
pub struct StackingOrder { pub struct StackingOrder {
#[deref] #[deref]
#[deref_mut] #[deref_mut]
z_indices: SmallVec<[u8; 64]>, context_stack: SmallVec<[StackingContext; 64]>,
} }
impl Default for StackingOrder { #[derive(Clone, Ord, PartialOrd, PartialEq, Eq)]
fn default() -> Self { pub struct StackingContext {
StackingOrder { // TODO kb use u16 and/or try to push the `id` above into the stacking order
z_indices: SmallVec::new(), z_index: u8,
id: u16,
}
impl std::fmt::Debug for StackingOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut stacks = self.context_stack.iter().peekable();
while let Some(z_index) = stacks.next() {
write!(f, "{}.{}", z_index.z_index, z_index.id)?;
if stacks.peek().is_some() {
write!(f, "->")?;
}
} }
Ok(())
} }
} }
@ -284,6 +296,7 @@ pub(crate) struct Frame {
pub(crate) scene_builder: SceneBuilder, pub(crate) scene_builder: SceneBuilder,
pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>, pub(crate) depth_map: Vec<(StackingOrder, Bounds<Pixels>)>,
pub(crate) z_index_stack: StackingOrder, pub(crate) z_index_stack: StackingOrder,
pub(crate) stacking_context_id_stack: Vec<u16>,
content_mask_stack: Vec<ContentMask<Pixels>>, content_mask_stack: Vec<ContentMask<Pixels>>,
element_offset_stack: Vec<Point<Pixels>>, element_offset_stack: Vec<Point<Pixels>>,
} }
@ -297,6 +310,7 @@ impl Frame {
dispatch_tree, dispatch_tree,
scene_builder: SceneBuilder::default(), scene_builder: SceneBuilder::default(),
z_index_stack: StackingOrder::default(), z_index_stack: StackingOrder::default(),
stacking_context_id_stack: vec![0],
depth_map: Default::default(), depth_map: Default::default(),
content_mask_stack: Vec::new(), content_mask_stack: Vec::new(),
element_offset_stack: Vec::new(), element_offset_stack: Vec::new(),
@ -308,6 +322,8 @@ impl Frame {
self.mouse_listeners.values_mut().for_each(Vec::clear); self.mouse_listeners.values_mut().for_each(Vec::clear);
self.dispatch_tree.clear(); self.dispatch_tree.clear();
self.depth_map.clear(); self.depth_map.clear();
self.stacking_context_id_stack.clear();
self.stacking_context_id_stack.push(0);
} }
fn focus_path(&self) -> SmallVec<[FocusId; 8]> { fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
@ -931,8 +947,20 @@ impl<'a> WindowContext<'a> {
/// Called during painting to invoke the given closure in a new stacking context. The given /// 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`. /// z-index is interpreted relative to the previous call to `stack`.
pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R { pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.next_frame.z_index_stack.push(z_index); let id = post_inc(
self.window
.next_frame
.stacking_context_id_stack
.last_mut()
.unwrap(),
);
self.window.next_frame.stacking_context_id_stack.push(0);
self.window
.next_frame
.z_index_stack
.push(StackingContext { z_index, id });
let result = f(self); let result = f(self);
self.window.next_frame.stacking_context_id_stack.pop();
self.window.next_frame.z_index_stack.pop(); self.window.next_frame.z_index_stack.pop();
result result
} }
@ -2046,6 +2074,30 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
result 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`.
fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
let id = post_inc(
self.window_mut()
.next_frame
.stacking_context_id_stack
.last_mut()
.unwrap(),
);
self.window_mut()
.next_frame
.stacking_context_id_stack
.push(0);
self.window_mut()
.next_frame
.z_index_stack
.push(StackingContext { z_index, id });
let result = f(self);
self.window_mut().next_frame.stacking_context_id_stack.pop();
self.window_mut().next_frame.z_index_stack.pop();
result
}
/// Update the global element offset relative to the current offset. This is used to implement /// Update the global element offset relative to the current offset. This is used to implement
/// scrolling. /// scrolling.
fn with_element_offset<R>( fn with_element_offset<R>(
@ -2269,13 +2321,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
&mut self.window_cx &mut self.window_cx
} }
pub fn with_z_index<R>(&mut self, z_index: u8, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.next_frame.z_index_stack.push(z_index);
let result = f(self);
self.window.next_frame.z_index_stack.pop();
result
}
pub fn on_next_frame(&mut self, f: impl FnOnce(&mut V, &mut ViewContext<V>) + 'static) pub fn on_next_frame(&mut self, f: impl FnOnce(&mut V, &mut ViewContext<V>) + 'static)
where where
V: 'static, V: 'static,

View file

@ -129,6 +129,7 @@ impl RenderOnce for ListItem {
fn render(self, cx: &mut WindowContext) -> Self::Rendered { fn render(self, cx: &mut WindowContext) -> Self::Rendered {
h_stack() h_stack()
.id(self.id) .id(self.id)
.bg(gpui::green())
.w_full() .w_full()
.relative() .relative()
// When an item is inset draw the indent spacing outside of the item // When an item is inset draw the indent spacing outside of the item
@ -171,7 +172,8 @@ impl RenderOnce for ListItem {
}) })
}) })
.when_some(self.on_click, |this, on_click| { .when_some(self.on_click, |this, on_click| {
this.cursor_pointer().on_click(on_click) this.cursor_copy()
.on_click(move |event, cx| on_click(dbg!(event), cx))
}) })
.when_some(self.on_secondary_mouse_down, |this, on_mouse_down| { .when_some(self.on_secondary_mouse_down, |this, on_mouse_down| {
this.on_mouse_down(MouseButton::Right, move |event, cx| { this.on_mouse_down(MouseButton::Right, move |event, cx| {

View file

@ -105,6 +105,8 @@ impl Render for Toolbar {
v_stack() v_stack()
.p_1() .p_1()
.gap_2() .gap_2()
// todo!() use a proper constant here (ask Marshall & Nate)
.z_index(80)
.border_b() .border_b()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().toolbar_background) .bg(cx.theme().colors().toolbar_background)