From 6a4c2a0d40f032b3046ab979577e7b96cb23454a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 9 Oct 2023 16:02:48 +0200 Subject: [PATCH 01/13] WIP --- crates/gpui3/src/geometry.rs | 6 +++ crates/gpui3/src/scene.rs | 99 +++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 93d7dd2a0804604e7bbe5886c22df1d91a2619f5..0580160f07fbaa72f7d2d03f7096cd36d9542009 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -289,6 +289,12 @@ impl + Sub> Bound let lower_right = self.lower_right().min(&other.lower_right()); Self::from_corners(upper_left, lower_right) } + + pub fn union(&self, other: &Self) -> Self { + let top_left = self.origin.min(&other.origin); + let bottom_right = self.lower_right().max(&other.lower_right()); + Bounds::from_corners(top_left, bottom_right) + } } impl Mul for Bounds diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 8cabda9f9f0a8c2743085a27edacfd1d878a012a..9ddb69ccae659b630136fcd4dba402f216a715d6 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,11 +1,16 @@ use crate::{ - AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Point, ScaledContentMask, ScaledPixels, + point, px, AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Pixels, Point, + ScaledContentMask, ScaledPixels, }; use collections::BTreeMap; use etagere::euclid::{Point3D, Vector3D}; use plane_split::{BspSplitter, Polygon as BspPolygon}; use smallvec::SmallVec; -use std::{iter::Peekable, mem, slice}; +use std::{ + iter::Peekable, + mem, slice, + sync::atomic::{AtomicU32, Ordering::SeqCst}, +}; // Exported to metal pub type PointF = Point; @@ -492,6 +497,96 @@ impl From for Primitive { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct PathId(u32); + +pub struct PathBuilder { + bounds: Bounds, + vertices: Vec, + start: Option>, + current: Point, +} + +impl PathBuilder { + pub fn new() -> Self { + const NEXT_PATH_ID: AtomicU32 = AtomicU32::new(0); + + Self { + vertices: Vec::new(), + start: Default::default(), + current: Default::default(), + bounds: Default::default(), + } + } + + pub fn line_to(&mut self, to: Point) { + if let Some(start) = self.start { + self.push_triangle( + (start, self.current, to), + (point(0., 1.), point(0., 1.), point(0., 1.)), + ); + } else { + self.start = Some(to); + } + self.current = to; + } + + pub fn curve_to(&mut self, to: Point, ctrl: Point) { + self.line_to(to); + self.push_triangle( + (self.current, ctrl, to), + (point(0., 0.), point(0.5, 0.), point(1., 1.)), + ); + } + + fn push_triangle( + &mut self, + xy: (Point, Point, Point), + st: (Point, Point, Point), + ) { + if self.vertices.is_empty() { + self.bounds = Bounds { + origin: xy.0, + size: Default::default(), + }; + } + 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, + }); + self.vertices.push(PathVertex { + xy_position: xy.1, + st_position: st.1, + }); + self.vertices.push(PathVertex { + xy_position: xy.2, + st_position: st.2, + }); + } +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub struct PathVertex { + pub xy_position: Point, + pub st_position: Point, +} + #[derive(Copy, Clone, Debug)] pub struct AtlasId(pub(crate) usize); From d889cdecdecf459befc746de0a5fdf1eb680e7bf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 9 Oct 2023 18:50:42 +0200 Subject: [PATCH 02/13] Checkpoint --- crates/gpui3/build.rs | 2 +- crates/gpui3/src/platform.rs | 32 +- crates/gpui3/src/platform/mac/metal_atlas.rs | 118 ++++--- .../gpui3/src/platform/mac/metal_renderer.rs | 26 +- crates/gpui3/src/platform/mac/window.rs | 6 +- crates/gpui3/src/scene.rs | 319 ++++++++++++------ crates/gpui3/src/style.rs | 59 +--- crates/gpui3/src/window.rs | 159 ++++++--- 8 files changed, 463 insertions(+), 258 deletions(-) diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index d0d4a5c74b73fead8cbb56bd083f3b6b10cc04ad..0131d9f1482479b57a4d0f386cf663156abbde9b 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -47,7 +47,7 @@ fn generate_shader_bindings() -> PathBuf { "Pixels".into(), "PointF".into(), "Hsla".into(), - "ScaledContentMask".into(), + "ContentMask".into(), "Uniforms".into(), "AtlasTile".into(), "ShadowInputIndex".into(), diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 53a35fadcc9717753ef7248099af252f9908795b..1589a26ee91cb25fae27c7d3621c2ecdb8c71e8a 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -40,7 +40,7 @@ pub(crate) fn current_platform() -> Arc { Arc::new(MacPlatform::new()) } -pub trait Platform: 'static { +pub(crate) trait Platform: 'static { fn executor(&self) -> Executor; fn display_linker(&self) -> Arc; fn text_system(&self) -> Arc; @@ -113,7 +113,7 @@ impl Debug for DisplayId { unsafe impl Send for DisplayId {} -pub trait PlatformWindow { +pub(crate) trait PlatformWindow { fn bounds(&self) -> WindowBounds; fn content_size(&self) -> Size; fn scale_factor(&self) -> f32; @@ -194,11 +194,17 @@ pub enum AtlasKey { } impl AtlasKey { - pub fn is_monochrome(&self) -> bool { + pub(crate) fn texture_kind(&self) -> AtlasTextureKind { match self { - AtlasKey::Glyph(params) => !params.is_emoji, - AtlasKey::Svg(_) => true, - AtlasKey::Image(_) => false, + AtlasKey::Glyph(params) => { + if params.is_emoji { + AtlasTextureKind::Polychrome + } else { + AtlasTextureKind::Monochrome + } + } + AtlasKey::Svg(_) => AtlasTextureKind::Monochrome, + AtlasKey::Image(_) => AtlasTextureKind::Polychrome, } } } @@ -241,7 +247,19 @@ pub struct AtlasTile { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] -pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility +pub(crate) struct AtlasTextureId { + // We use u32 instead of usize for Metal Shader Language compatibility + pub(crate) index: u32, + pub(crate) kind: AtlasTextureKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub(crate) enum AtlasTextureKind { + Monochrome = 0, + Polychrome = 1, + Path = 2, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[repr(C)] diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 05ec7ab71ca1e7dabc6ad051105719e88fc0adb7..f6392d2fc0570054dcdd922b753365ba058b3d8a 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -1,14 +1,14 @@ -use std::borrow::Cow; - use crate::{ - AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size, + AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, + Point, Size, }; -use anyhow::{anyhow, Result}; +use anyhow::Result; use collections::HashMap; use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use metal::Device; use parking_lot::Mutex; +use std::borrow::Cow; pub struct MetalAtlas(Mutex); @@ -16,19 +16,31 @@ impl MetalAtlas { pub fn new(device: Device) -> Self { MetalAtlas(Mutex::new(MetalAtlasState { device: AssertSend(device), - textures: Default::default(), + monochrome_textures: Default::default(), + polychrome_textures: Default::default(), + path_textures: Default::default(), tiles_by_key: Default::default(), })) } - pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture { - self.0.lock().textures[id.0 as usize].metal_texture.clone() + pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture { + self.0.lock().texture(id).metal_texture.clone() + } + + pub(crate) fn allocate( + &self, + size: Size, + texture_kind: AtlasTextureKind, + ) -> AtlasTile { + self.0.lock().allocate(size, texture_kind) } } struct MetalAtlasState { device: AssertSend, - textures: Vec, + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, tiles_by_key: HashMap, } @@ -43,23 +55,9 @@ impl PlatformAtlas for MetalAtlas { return Ok(tile.clone()); } else { let (size, bytes) = build()?; - let tile = lock - .textures - .iter_mut() - .rev() - .find_map(|texture| { - if texture.monochrome == key.is_monochrome() { - texture.upload(size, &bytes) - } else { - None - } - }) - .or_else(|| { - let texture = lock.push_texture(size, key.is_monochrome()); - texture.upload(size, &bytes) - }) - .ok_or_else(|| anyhow!("could not allocate in new texture"))?; - lock.tiles_by_key.insert(key.clone(), tile.clone()); + let tile = lock.allocate(size, key.texture_kind()); + let texture = lock.texture(tile.texture_id); + texture.upload(tile.bounds, &bytes); Ok(tile) } } @@ -70,10 +68,26 @@ impl PlatformAtlas for MetalAtlas { } impl MetalAtlasState { + fn allocate(&mut self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + .unwrap_or_else(|| { + let texture = self.push_texture(size, texture_kind); + texture.allocate(size).unwrap() + }) + } + fn push_texture( &mut self, min_size: Size, - monochrome: bool, + kind: AtlasTextureKind, ) -> &mut MetalAtlasTexture { const DEFAULT_ATLAS_SIZE: Size = Size { width: DevicePixels(1024), @@ -84,21 +98,38 @@ impl MetalAtlasState { let texture_descriptor = metal::TextureDescriptor::new(); texture_descriptor.set_width(size.width.into()); texture_descriptor.set_height(size.height.into()); - if monochrome { - texture_descriptor.set_pixel_format(metal::MTLPixelFormat::A8Unorm); - } else { - texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm); - } + let pixel_format = match kind { + AtlasTextureKind::Monochrome => metal::MTLPixelFormat::A8Unorm, + AtlasTextureKind::Polychrome => metal::MTLPixelFormat::BGRA8Unorm, + AtlasTextureKind::Path => metal::MTLPixelFormat::R16Float, + }; + texture_descriptor.set_pixel_format(pixel_format); let metal_texture = self.device.new_texture(&texture_descriptor); + let textures = match kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; let atlas_texture = MetalAtlasTexture { - id: AtlasTextureId(self.textures.len() as u32), + id: AtlasTextureId { + index: textures.len() as u32, + kind, + }, allocator: etagere::BucketedAtlasAllocator::new(size.into()), metal_texture: AssertSend(metal_texture), - monochrome, }; - self.textures.push(atlas_texture); - self.textures.last_mut().unwrap() + textures.push(atlas_texture); + textures.last_mut().unwrap() + } + + fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + &textures[id.index as usize] } } @@ -106,11 +137,10 @@ struct MetalAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, metal_texture: AssertSend, - monochrome: bool, } impl MetalAtlasTexture { - fn upload(&mut self, size: Size, bytes: &[u8]) -> Option { + fn allocate(&mut self, size: Size) -> Option { let allocation = self.allocator.allocate(size.into())?; let tile = AtlasTile { texture_id: self.id, @@ -120,20 +150,22 @@ impl MetalAtlasTexture { size, }, }; + Some(tile) + } + fn upload(&self, bounds: Bounds, bytes: &[u8]) { let region = metal::MTLRegion::new_2d( - tile.bounds.origin.x.into(), - tile.bounds.origin.y.into(), - tile.bounds.size.width.into(), - tile.bounds.size.height.into(), + bounds.origin.x.into(), + bounds.origin.y.into(), + bounds.size.width.into(), + bounds.size.height.into(), ); self.metal_texture.replace_region( region, 0, bytes.as_ptr() as *const _, - u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64, + u32::from(bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64, ); - Some(tile) } fn bytes_per_pixel(&self) -> u8 { diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 9b93a8a5619ccf006f81fd34090206fa1f6cb705..f4c735c6068250eb786f0c4b8679ada19308e85b 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,12 +1,14 @@ use crate::{ - point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite, - PrimitiveBatch, Quad, Scene, Shadow, Size, Underline, + point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, DevicePixels, MetalAtlas, + MonochromeSprite, PathId, PolychromeSprite, PrimitiveBatch, Quad, Scene, Shadow, Size, + Underline, }; use cocoa::{ base::{NO, YES}, foundation::NSUInteger, quartzcore::AutoresizingMask, }; +use collections::HashMap; use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use objc::{self, msg_send, sel, sel_impl}; use std::{ffi::c_void, mem, ptr, sync::Arc}; @@ -14,7 +16,7 @@ use std::{ffi::c_void, mem, ptr, sync::Arc}; const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value. -pub struct MetalRenderer { +pub(crate) struct MetalRenderer { layer: metal::MetalLayer, command_queue: CommandQueue, shadows_pipeline_state: metal::RenderPipelineState, @@ -150,7 +152,7 @@ impl MetalRenderer { &self.sprite_atlas } - pub fn draw(&mut self, scene: &mut Scene) { + pub fn draw(&mut self, scene: &Scene) { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); let viewport_size: Size = size( @@ -192,6 +194,15 @@ impl MetalRenderer { }); let mut instance_offset = 0; + + let mut path_tiles: HashMap = HashMap::default(); + for path in scene.paths() { + let tile = self + .sprite_atlas + .allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path); + path_tiles.insert(path.id, tile); + } + for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => { @@ -205,6 +216,9 @@ impl MetalRenderer { PrimitiveBatch::Quads(quads) => { self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder); } + PrimitiveBatch::Paths(paths) => { + // self.draw_paths(paths, &mut instance_offset, viewport_size, command_encoder); + } PrimitiveBatch::Underlines(underlines) => { self.draw_underlines( underlines, @@ -441,7 +455,7 @@ impl MetalRenderer { } align_offset(offset); - let texture = self.sprite_atlas.texture(texture_id); + let texture = self.sprite_atlas.metal_texture(texture_id); let texture_size = size( DevicePixels(texture.width() as i32), DevicePixels(texture.height() as i32), @@ -512,7 +526,7 @@ impl MetalRenderer { } align_offset(offset); - let texture = self.sprite_atlas.texture(texture_id); + let texture = self.sprite_atlas.metal_texture(texture_id); let texture_size = size( DevicePixels(texture.width() as i32), DevicePixels(texture.height() as i32), diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index b16e85f08df43fc3c22ece34b1eb6a4f67a7f152..d994942002b723a20862a72b5623008684f76375 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -911,7 +911,7 @@ impl PlatformWindow for MacWindow { } } - fn draw(&self, scene: crate::Scene) { + fn draw(&self, scene: Scene) { let mut this = self.0.lock(); this.scene_to_render = Some(scene); unsafe { @@ -1395,8 +1395,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) { unsafe { let window_state = get_window_state(this); let mut window_state = window_state.as_ref().lock(); - if let Some(mut scene) = window_state.scene_to_render.take() { - window_state.renderer.draw(&mut scene); + if let Some(scene) = window_state.scene_to_render.take() { + window_state.renderer.draw(&scene); } } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 9ddb69ccae659b630136fcd4dba402f216a715d6..11e121413ef29e0bec8aa0444be9cabce92954b7 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,16 +1,12 @@ use crate::{ - point, px, AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Pixels, Point, - ScaledContentMask, ScaledPixels, + point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point, + ScaledPixels, }; use collections::BTreeMap; use etagere::euclid::{Point3D, Vector3D}; use plane_split::{BspSplitter, Polygon as BspPolygon}; use smallvec::SmallVec; -use std::{ - iter::Peekable, - mem, slice, - sync::atomic::{AtomicU32, Ordering::SeqCst}, -}; +use std::{fmt::Debug, iter::Peekable, mem, slice}; // Exported to metal pub type PointF = Point; @@ -18,94 +14,57 @@ pub type StackingOrder = SmallVec<[u32; 16]>; pub type LayerId = u32; pub type DrawOrder = u32; -#[derive(Debug)] -pub struct Scene { - pub(crate) scale_factor: f32, - pub(crate) layers: BTreeMap, - pub shadows: Vec, - pub quads: Vec, - pub underlines: Vec, - pub monochrome_sprites: Vec, - pub polychrome_sprites: Vec, -} - -impl Scene { - pub fn new(scale_factor: f32) -> Scene { - Scene { - scale_factor, - layers: BTreeMap::new(), +pub(crate) struct SceneBuilder { + layers_by_order: BTreeMap, + splitter: BspSplitter<(PrimitiveKind, usize)>, + shadows: Vec, + quads: Vec, + paths: Vec>, + underlines: Vec, + monochrome_sprites: Vec, + polychrome_sprites: Vec, +} + +impl SceneBuilder { + pub fn new() -> SceneBuilder { + SceneBuilder { + layers_by_order: BTreeMap::new(), + splitter: BspSplitter::new(), shadows: Vec::new(), quads: Vec::new(), + paths: Vec::new(), underlines: Vec::new(), monochrome_sprites: Vec::new(), polychrome_sprites: Vec::new(), } } - pub fn take(&mut self) -> Scene { - Scene { - scale_factor: self.scale_factor, - layers: mem::take(&mut self.layers), - shadows: mem::take(&mut self.shadows), - quads: mem::take(&mut self.quads), - underlines: mem::take(&mut self.underlines), - monochrome_sprites: mem::take(&mut self.monochrome_sprites), - polychrome_sprites: mem::take(&mut self.polychrome_sprites), - } - } - - pub fn insert(&mut self, layer_id: StackingOrder, primitive: impl Into) { - let next_id = self.layers.len() as LayerId; - let layer_id = *self.layers.entry(layer_id).or_insert(next_id); - let primitive = primitive.into(); - match primitive { - Primitive::Shadow(mut shadow) => { - shadow.order = layer_id; - self.shadows.push(shadow); - } - Primitive::Quad(mut quad) => { - quad.order = layer_id; - self.quads.push(quad); - } - Primitive::Underline(mut underline) => { - underline.order = layer_id; - self.underlines.push(underline); - } - Primitive::MonochromeSprite(mut sprite) => { - sprite.order = layer_id; - self.monochrome_sprites.push(sprite); - } - Primitive::PolychromeSprite(mut sprite) => { - sprite.order = layer_id; - self.polychrome_sprites.push(sprite); - } - } - } - - pub(crate) fn batches(&mut self) -> impl Iterator { + pub fn build(&mut self) -> Scene { // Map each layer id to a float between 0. and 1., with 1. closer to the viewer. - let mut layer_z_values = vec![0.; self.layers.len()]; - for (ix, layer_id) in self.layers.values().enumerate() { - layer_z_values[*layer_id as usize] = ix as f32 / self.layers.len() as f32; + let mut layer_z_values = vec![0.; self.layers_by_order.len()]; + for (ix, layer_id) in self.layers_by_order.values().enumerate() { + layer_z_values[*layer_id as usize] = ix as f32 / self.layers_by_order.len() as f32; } + self.layers_by_order.clear(); // Add all primitives to the BSP splitter to determine draw order - // todo!("reuse the same splitter") - let mut splitter = BspSplitter::new(); + self.splitter.reset(); for (ix, shadow) in self.shadows.iter().enumerate() { let z = layer_z_values[shadow.order as LayerId as usize]; - splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix))); + self.splitter + .add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix))); } for (ix, quad) in self.quads.iter().enumerate() { let z = layer_z_values[quad.order as LayerId as usize]; - splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix))); + self.splitter + .add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix))); } for (ix, underline) in self.underlines.iter().enumerate() { let z = layer_z_values[underline.order as LayerId as usize]; - splitter.add( + self.splitter.add( underline .bounds .to_bsp_polygon(z, (PrimitiveKind::Underline, ix)), @@ -114,7 +73,7 @@ impl Scene { for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() { let z = layer_z_values[monochrome_sprite.order as LayerId as usize]; - splitter.add( + self.splitter.add( monochrome_sprite .bounds .to_bsp_polygon(z, (PrimitiveKind::MonochromeSprite, ix)), @@ -123,7 +82,7 @@ impl Scene { for (ix, polychrome_sprite) in self.polychrome_sprites.iter().enumerate() { let z = layer_z_values[polychrome_sprite.order as LayerId as usize]; - splitter.add( + self.splitter.add( polychrome_sprite .bounds .to_bsp_polygon(z, (PrimitiveKind::PolychromeSprite, ix)), @@ -132,10 +91,16 @@ impl Scene { // Sort all polygons, then reassign the order field of each primitive to `draw_order` // We need primitives to be repr(C), hence the weird reuse of the order field for two different types. - for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() { + for (draw_order, polygon) in self + .splitter + .sort(Vector3D::new(0., 0., 1.)) + .iter() + .enumerate() + { match polygon.anchor { (PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder, (PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder, + (PrimitiveKind::Path, ix) => self.paths[ix].order = draw_order as DrawOrder, (PrimitiveKind::Underline, ix) => { self.underlines[ix].order = draw_order as DrawOrder } @@ -148,13 +113,78 @@ impl Scene { } } - // Sort the primitives self.shadows.sort_unstable(); self.quads.sort_unstable(); + self.paths.sort_unstable(); self.underlines.sort_unstable(); self.monochrome_sprites.sort_unstable(); self.polychrome_sprites.sort_unstable(); + Scene { + shadows: mem::take(&mut self.shadows), + quads: mem::take(&mut self.quads), + paths: mem::take(&mut self.paths), + underlines: mem::take(&mut self.underlines), + monochrome_sprites: mem::take(&mut self.monochrome_sprites), + polychrome_sprites: mem::take(&mut self.polychrome_sprites), + } + } + + pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into) { + let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) { + *layer_id + } else { + let next_id = self.layers_by_order.len() as LayerId; + self.layers_by_order.insert(order.clone(), next_id); + next_id + }; + + let primitive = primitive.into(); + match primitive { + Primitive::Shadow(mut shadow) => { + shadow.order = layer_id; + self.shadows.push(shadow); + } + Primitive::Quad(mut quad) => { + quad.order = layer_id; + self.quads.push(quad); + } + Primitive::Path(mut path) => { + path.order = layer_id; + path.id = PathId(self.paths.len()); + self.paths.push(path); + } + Primitive::Underline(mut underline) => { + underline.order = layer_id; + self.underlines.push(underline); + } + Primitive::MonochromeSprite(mut sprite) => { + sprite.order = layer_id; + self.monochrome_sprites.push(sprite); + } + Primitive::PolychromeSprite(mut sprite) => { + sprite.order = layer_id; + self.polychrome_sprites.push(sprite); + } + } + } +} + +pub(crate) struct Scene { + pub shadows: Vec, + pub quads: Vec, + pub paths: Vec>, + pub underlines: Vec, + pub monochrome_sprites: Vec, + pub polychrome_sprites: Vec, +} + +impl Scene { + pub fn paths(&self) -> impl Iterator> { + self.paths.iter() + } + + pub fn batches(&self) -> impl Iterator { BatchIterator { shadows: &self.shadows, shadows_start: 0, @@ -162,6 +192,9 @@ impl Scene { quads: &self.quads, quads_start: 0, quads_iter: self.quads.iter().peekable(), + paths: &self.paths, + paths_start: 0, + paths_iter: self.paths.iter().peekable(), underlines: &self.underlines, underlines_start: 0, underlines_iter: self.underlines.iter().peekable(), @@ -176,12 +209,15 @@ impl Scene { } struct BatchIterator<'a> { - quads: &'a [Quad], - quads_start: usize, - quads_iter: Peekable>, shadows: &'a [Shadow], shadows_start: usize, shadows_iter: Peekable>, + quads: &'a [Quad], + quads_start: usize, + quads_iter: Peekable>, + paths: &'a [Path], + paths_start: usize, + paths_iter: Peekable>>, underlines: &'a [Underline], underlines_start: usize, underlines_iter: Peekable>, @@ -255,6 +291,19 @@ impl<'a> Iterator for BatchIterator<'a> { self.quads_start = quads_end; Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) } + PrimitiveKind::Path => { + let paths_start = self.paths_start; + let mut paths_end = paths_start; + while self + .paths_iter + .next_if(|path| path.order <= max_order) + .is_some() + { + paths_end += 1; + } + self.paths_start = paths_end; + Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end])) + } PrimitiveKind::Underline => { let underlines_start = self.underlines_start; let mut underlines_end = underlines_start; @@ -317,15 +366,16 @@ pub enum PrimitiveKind { Shadow, #[default] Quad, + Path, Underline, MonochromeSprite, PolychromeSprite, } -#[derive(Clone, Debug)] pub enum Primitive { Shadow(Shadow), Quad(Quad), + Path(Path), Underline(Underline), MonochromeSprite(MonochromeSprite), PolychromeSprite(PolychromeSprite), @@ -335,6 +385,7 @@ pub enum Primitive { pub(crate) enum PrimitiveBatch<'a> { Shadows(&'a [Shadow]), Quads(&'a [Quad]), + Paths(&'a [Path]), Underlines(&'a [Underline]), MonochromeSprites { texture_id: AtlasTextureId, @@ -351,7 +402,7 @@ pub(crate) enum PrimitiveBatch<'a> { pub struct Quad { pub order: u32, // Initially a LayerId, then a DrawOrder. pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub background: Hsla, pub border_color: Hsla, pub corner_radii: Corners, @@ -381,7 +432,7 @@ impl From for Primitive { pub struct Underline { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub thickness: ScaledPixels, pub color: Hsla, pub wavy: bool, @@ -411,7 +462,7 @@ pub struct Shadow { pub order: u32, pub bounds: Bounds, pub corner_radii: Corners, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub color: Hsla, pub blur_radius: ScaledPixels, } @@ -439,7 +490,7 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub color: Hsla, pub tile: AtlasTile, } @@ -470,7 +521,7 @@ impl From for Primitive { pub struct PolychromeSprite { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub corner_radii: Corners, pub tile: AtlasTile, pub grayscale: bool, @@ -497,21 +548,24 @@ impl From for Primitive { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct PathId(u32); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) struct PathId(pub(crate) usize); -pub struct PathBuilder { - bounds: Bounds, - vertices: Vec, - start: Option>, - current: Point, +#[derive(Debug)] +pub struct Path { + pub(crate) id: PathId, + order: u32, + pub(crate) bounds: Bounds

, + pub(crate) vertices: Vec>, + start: Option>, + current: Point

, } -impl PathBuilder { +impl Path { pub fn new() -> Self { - const NEXT_PATH_ID: AtomicU32 = AtomicU32::new(0); - Self { + id: PathId(0), + order: 0, vertices: Vec::new(), start: Default::default(), current: Default::default(), @@ -519,6 +573,21 @@ impl PathBuilder { } } + pub fn scale(&self, factor: f32) -> Path { + Path { + id: self.id, + order: self.order, + bounds: self.bounds.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), + } + } + pub fn line_to(&mut self, to: Point) { if let Some(start) = self.start { self.push_triangle( @@ -568,23 +637,63 @@ impl PathBuilder { 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(), }); } } -#[derive(Clone, Debug)] +impl Eq for Path {} + +impl PartialEq for Path { + fn eq(&self, other: &Self) -> bool { + self.order == other.order + } +} + +impl Ord for Path { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.order.cmp(&other.order) + } +} + +impl PartialOrd for Path { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From> for Primitive { + fn from(path: Path) -> Self { + Primitive::Path(path) + } +} + +#[derive(Debug)] #[repr(C)] -pub struct PathVertex { - pub xy_position: Point, - pub st_position: Point, +pub struct PathVertex { + pub(crate) xy_position: Point

, + pub(crate) st_position: Point, + pub(crate) content_mask: ContentMask

, +} + +impl PathVertex { + pub fn scale(&self, factor: f32) -> PathVertex { + PathVertex { + xy_position: self.xy_position.scale(factor), + st_position: self.st_position, + content_mask: self.content_mask.scale(factor), + } + } } #[derive(Copy, Clone, Debug)] @@ -619,15 +728,15 @@ mod tests { #[test] fn test_scene() { - let mut scene = Scene::new(1.0); - assert_eq!(scene.layers.len(), 0); + let mut scene = SceneBuilder::new(); + assert_eq!(scene.layers_by_order.len(), 0); - scene.insert(smallvec![1], quad()); - scene.insert(smallvec![2], shadow()); - scene.insert(smallvec![3], quad()); + scene.insert(&smallvec![1], quad()); + scene.insert(&smallvec![2], shadow()); + scene.insert(&smallvec![3], quad()); let mut batches_count = 0; - for _ in scene.batches() { + for _ in scene.build().batches() { batches_count += 1; } assert_eq!(batches_count, 3); diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index af2b87d0bbe00340eb571455b37a7d4d1fa38840..3a7131ab9e60299075f73165e94d3fe21a0bec33 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -1,8 +1,8 @@ use crate::{ phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle, - FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Quad, Rems, Result, RunStyle, Shadow, - SharedString, Size, SizeRefinement, ViewContext, WindowContext, + FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString, + Size, SizeRefinement, ViewContext, WindowContext, }; use refineable::Refineable; use smallvec::SmallVec; @@ -243,51 +243,24 @@ impl Style { /// Paints the background of an element styled with this style. pub fn paint(&self, bounds: Bounds, cx: &mut ViewContext) { let rem_size = cx.rem_size(); - let scale = cx.scale_factor(); - - for shadow in &self.box_shadow { - let content_mask = cx.content_mask(); - let mut shadow_bounds = bounds; - shadow_bounds.origin += shadow.offset; - shadow_bounds.dilate(shadow.spread_radius); - cx.stack(0, |cx| { - let layer_id = cx.current_stacking_order(); - cx.scene().insert( - layer_id, - Shadow { - order: 0, - bounds: shadow_bounds.scale(scale), - content_mask: content_mask.scale(scale), - corner_radii: self - .corner_radii - .to_pixels(shadow_bounds.size, rem_size) - .scale(scale), - color: shadow.color, - blur_radius: shadow.blur_radius.scale(scale), - }, - ); - }) - } + + cx.stack(0, |cx| { + cx.paint_shadows( + bounds, + self.corner_radii.to_pixels(bounds.size, rem_size), + &self.box_shadow, + ); + }); let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { - let content_mask = cx.content_mask(); cx.stack(1, |cx| { - let order = cx.current_stacking_order(); - cx.scene().insert( - order, - Quad { - order: 0, - bounds: bounds.scale(scale), - content_mask: content_mask.scale(scale), - background: background_color.unwrap_or_default(), - border_color: self.border_color.unwrap_or_default(), - corner_radii: self - .corner_radii - .to_pixels(bounds.size, rem_size) - .scale(scale), - border_widths: self.border_widths.to_pixels(rem_size).scale(scale), - }, + cx.paint_quad( + bounds, + self.corner_radii.to_pixels(bounds.size, rem_size), + background_color.unwrap_or_default(), + self.border_widths.to_pixels(rem_size), + self.border_color.unwrap_or_default(), ); }); } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 123ec128acefd45a797a2347765fb2fef5719f69..96b97427afa39c0393d97c5e496b39c037d43e20 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,15 +1,17 @@ use crate::{ image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext, - AvailableSpace, BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, - Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread, - MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, - PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, - SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle, - WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, + Edges, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, + MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas, PlatformWindow, + Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, + Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use smallvec::SmallVec; -use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc}; +use std::{ + any::TypeId, borrow::Cow, fmt::Debug, future::Future, marker::PhantomData, mem, sync::Arc, +}; use util::ResultExt; pub struct AnyWindow {} @@ -25,8 +27,9 @@ pub struct Window { pub(crate) root_view: Option>, mouse_position: Point, current_stacking_order: StackingOrder, - content_mask_stack: Vec, - pub(crate) scene: Scene, + content_mask_stack: Vec>, + scale_factor: f32, + pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, } @@ -47,7 +50,8 @@ impl Window { let cx = cx.to_async(); move |content_size, scale_factor| { cx.update_window(handle, |cx| { - cx.window.scene = Scene::new(scale_factor); + cx.window.scale_factor = scale_factor; + cx.window.scene_builder = SceneBuilder::new(); cx.window.content_size = content_size; cx.window.display_id = cx .window @@ -75,20 +79,22 @@ impl Window { mouse_position, current_stacking_order: SmallVec::new(), content_mask_stack: Vec::new(), - scene: Scene::new(scale_factor), + scale_factor, + scene_builder: SceneBuilder::new(), dirty: true, } } } -#[derive(Clone, Debug)] -pub struct ContentMask { - pub bounds: Bounds, +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[repr(C)] +pub struct ContentMask { + pub bounds: Bounds

, } -impl ContentMask { - pub fn scale(&self, factor: f32) -> ScaledContentMask { - ScaledContentMask { +impl ContentMask { + pub fn scale(&self, factor: f32) -> ContentMask { + ContentMask { bounds: self.bounds.scale(factor), } } @@ -99,12 +105,6 @@ impl ContentMask { } } -#[derive(Default, Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct ScaledContentMask { - bounds: Bounds, -} - pub struct WindowContext<'a, 'w> { app: Reference<'a, AppContext>, window: Reference<'w, Window>, @@ -234,7 +234,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { } pub fn scale_factor(&self) -> f32 { - self.window.scene.scale_factor + self.window.scale_factor } pub fn rem_size(&self) -> Pixels { @@ -245,10 +245,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.mouse_position } - pub fn scene(&mut self) -> &mut Scene { - &mut self.window.scene - } - pub fn stack(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R { self.window.current_stacking_order.push(order); let result = f(self); @@ -256,8 +252,69 @@ impl<'a, 'w> WindowContext<'a, 'w> { result } - pub fn current_stacking_order(&self) -> StackingOrder { - self.window.current_stacking_order.clone() + pub fn paint_shadows( + &mut self, + bounds: Bounds, + corner_radii: Corners, + shadows: &[BoxShadow], + ) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + let window = &mut *self.window; + for shadow in shadows { + let mut shadow_bounds = bounds; + shadow_bounds.origin += shadow.offset; + shadow_bounds.dilate(shadow.spread_radius); + window.scene_builder.insert( + &window.current_stacking_order, + Shadow { + order: 0, + bounds: shadow_bounds.scale(scale_factor), + content_mask: content_mask.scale(scale_factor), + corner_radii: corner_radii.scale(scale_factor), + color: shadow.color, + blur_radius: shadow.blur_radius.scale(scale_factor), + }, + ); + } + } + + pub fn paint_quad( + &mut self, + bounds: Bounds, + corner_radii: Corners, + background: impl Into, + border_widths: Edges, + border_color: impl Into, + ) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, + Quad { + order: 0, + bounds: bounds.scale(scale_factor), + content_mask: content_mask.scale(scale_factor), + background: background.into(), + border_color: border_color.into(), + corner_radii: corner_radii.scale(scale_factor), + border_widths: border_widths.scale(scale_factor), + }, + ); + } + + pub fn paint_path(&mut self, mut path: Path) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + for vertex in &mut path.vertices { + vertex.content_mask = content_mask.clone(); + } + let window = &mut *self.window; + window + .scene_builder + .insert(&window.current_stacking_order, path.scale(scale_factor)); } pub fn paint_underline( @@ -277,9 +334,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: size(width, height), }; let content_mask = self.content_mask(); - let layer_id = self.current_stacking_order(); - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, Underline { order: 0, bounds: bounds.scale(scale_factor), @@ -317,7 +374,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let raster_bounds = self.text_system().raster_bounds(¶ms)?; if !raster_bounds.is_zero() { - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -330,9 +386,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: tile.bounds.size.map(Into::into), }; let content_mask = self.content_mask().scale(scale_factor); - - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, MonochromeSprite { order: 0, bounds, @@ -366,7 +422,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let raster_bounds = self.text_system().raster_bounds(¶ms)?; if !raster_bounds.is_zero() { - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -379,9 +434,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: tile.bounds.size.map(Into::into), }; let content_mask = self.content_mask().scale(scale_factor); + let window = &mut *self.window; - self.window.scene.insert( - layer_id, + window.scene_builder.insert( + &window.current_stacking_order, PolychromeSprite { order: 0, bounds, @@ -411,7 +467,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)), }; - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -421,8 +476,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { })?; let content_mask = self.content_mask().scale(scale_factor); - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, MonochromeSprite { order: 0, bounds, @@ -446,7 +502,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let bounds = bounds.scale(scale_factor); let params = RenderImageParams { image_id: data.id }; - let order = self.current_stacking_order(); let tile = self .window .sprite_atlas @@ -456,8 +511,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { let content_mask = self.content_mask().scale(scale_factor); let corner_radii = corner_radii.scale(scale_factor); - self.window.scene.insert( - order, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, PolychromeSprite { order: 0, bounds, @@ -467,7 +523,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { grayscale, }, ); - Ok(()) } @@ -485,7 +540,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { root_view.paint(layout, &mut (), &mut frame_state, cx)?; cx.window.root_view = Some(root_view); - let scene = cx.window.scene.take(); + let scene = cx.window.scene_builder.build(); cx.run_on_main(view, |_, cx| { cx.window @@ -557,7 +612,11 @@ pub trait BorrowWindow: BorrowAppContext { fn window(&self) -> &Window; fn window_mut(&mut self) -> &mut Window; - fn with_content_mask(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R { + fn with_content_mask( + &mut self, + mask: ContentMask, + f: impl FnOnce(&mut Self) -> R, + ) -> R { let mask = mask.intersect(&self.content_mask()); self.window_mut().content_mask_stack.push(mask); let result = f(self); @@ -565,7 +624,7 @@ pub trait BorrowWindow: BorrowAppContext { result } - fn content_mask(&self) -> ContentMask { + fn content_mask(&self) -> ContentMask { self.window() .content_mask_stack .last() From 7b610f8dd8c2fb250e945943594249c1e1ffa332 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 9 Oct 2023 19:50:48 +0200 Subject: [PATCH 03/13] WIP --- crates/gpui3/src/platform.rs | 4 +- .../gpui3/src/platform/mac/metal_renderer.rs | 107 +++++++++++++++--- crates/gpui3/src/scene.rs | 6 +- 3 files changed, 98 insertions(+), 19 deletions(-) diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 1589a26ee91cb25fae27c7d3621c2ecdb8c71e8a..fb90bb9c6b51b8e677a042945682fa13837ab1cf 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -245,7 +245,7 @@ pub struct AtlasTile { pub(crate) bounds: Bounds, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr(C)] pub(crate) struct AtlasTextureId { // We use u32 instead of usize for Metal Shader Language compatibility @@ -253,7 +253,7 @@ pub(crate) struct AtlasTextureId { pub(crate) kind: AtlasTextureKind, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[repr(C)] pub(crate) enum AtlasTextureKind { Monochrome = 0, diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index f4c735c6068250eb786f0c4b8679ada19308e85b..4dd3250f08c4b0acdd11189e61273c13e71942a9 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,7 +1,7 @@ use crate::{ - point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, DevicePixels, MetalAtlas, - MonochromeSprite, PathId, PolychromeSprite, PrimitiveBatch, Quad, Scene, Shadow, Size, - Underline, + point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels, + MetalAtlas, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, + ScaledPixels, Scene, Shadow, Size, Underline, }; use cocoa::{ base::{NO, YES}, @@ -170,6 +170,9 @@ impl MetalRenderer { }; let command_queue = self.command_queue.clone(); let command_buffer = command_queue.new_command_buffer(); + let mut instance_offset = 0; + + let path_tiles = self.rasterize_paths(scene.paths(), &mut instance_offset, &command_buffer); let render_pass_descriptor = metal::RenderPassDescriptor::new(); let color_attachment = render_pass_descriptor @@ -192,17 +195,6 @@ impl MetalRenderer { znear: 0.0, zfar: 1.0, }); - - let mut instance_offset = 0; - - let mut path_tiles: HashMap = HashMap::default(); - for path in scene.paths() { - let tile = self - .sprite_atlas - .allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path); - path_tiles.insert(path.id, tile); - } - for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => { @@ -266,6 +258,93 @@ impl MetalRenderer { drawable.present(); } + fn rasterize_paths( + &mut self, + paths: &[Path], + offset: &mut usize, + command_buffer: &metal::CommandBufferRef, + ) -> HashMap { + let mut tiles = HashMap::default(); + let mut vertices_by_texture_id = HashMap::default(); + for path in paths { + let tile = self + .sprite_atlas + .allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path); + vertices_by_texture_id + .entry(tile.texture_id) + .or_insert(Vec::new()) + .extend(path.vertices.iter().map(|vertex| PathVertex { + xy_position: vertex.xy_position - path.bounds.origin + + tile.bounds.origin.map(Into::into), + st_position: vertex.st_position, + content_mask: ContentMask { + bounds: Bounds { + origin: vertex.xy_position - path.bounds.origin + + tile.bounds.origin.map(Into::into), + size: vertex.content_mask.bounds.size, + }, + }, + })); + tiles.insert(path.id, tile); + } + + for (texture_id, vertices) in vertices_by_texture_id { + todo!(); + // align_offset(offset); + // let next_offset = *offset + vertices.len() * mem::size_of::>(); + // assert!( + // next_offset <= INSTANCE_BUFFER_SIZE, + // "instance buffer exhausted" + // ); + + // let render_pass_descriptor = metal::RenderPassDescriptor::new(); + // let color_attachment = render_pass_descriptor + // .color_attachments() + // .object_at(0) + // .unwrap(); + + // let texture = self.sprite_atlas.metal_texture(texture_id); + // color_attachment.set_texture(Some(&texture)); + // color_attachment.set_load_action(metal::MTLLoadAction::Clear); + // color_attachment.set_store_action(metal::MTLStoreAction::Store); + // color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.)); + // let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor); + // command_encoder.set_render_pipeline_state(&self.path_atlas_pipeline_state); + // command_encoder.set_vertex_buffer( + // shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexVertices as u64, + // Some(&self.instances), + // *offset as u64, + // ); + // command_encoder.set_vertex_bytes( + // shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexAtlasSize + // as u64, + // mem::size_of::() as u64, + // [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr() + // as *const c_void, + // ); + + // let buffer_contents = unsafe { + // (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIPathVertex + // }; + + // for (ix, vertex) in vertices.iter().enumerate() { + // unsafe { + // *buffer_contents.add(ix) = *vertex; + // } + // } + + // command_encoder.draw_primitives( + // metal::MTLPrimitiveType::Triangle, + // 0, + // vertices.len() as u64, + // ); + // command_encoder.end_encoding(); + // *offset = next_offset; + } + + tiles + } + fn draw_shadows( &mut self, shadows: &[Shadow], diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 11e121413ef29e0bec8aa0444be9cabce92954b7..aa5bf002ddc9a1aebdf0d62c6c1a38c776d344c2 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -180,8 +180,8 @@ pub(crate) struct Scene { } impl Scene { - pub fn paths(&self) -> impl Iterator> { - self.paths.iter() + pub fn paths(&self) -> &[Path] { + &self.paths } pub fn batches(&self) -> impl Iterator { @@ -678,7 +678,7 @@ impl From> for Primitive { } } -#[derive(Debug)] +#[derive(Clone, Debug)] #[repr(C)] pub struct PathVertex { pub(crate) xy_position: Point

, From f763ed9a7e480c572ef21c6b82a1ccd447485991 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 9 Oct 2023 19:54:23 -0600 Subject: [PATCH 04/13] Checkpoint --- crates/gpui3/src/elements.rs | 2 + crates/gpui3/src/elements/interactive.rs | 152 ++++++++++++++++++ crates/gpui3/src/platform/mac/metal_atlas.rs | 1 + .../gpui3/src/platform/mac/metal_renderer.rs | 11 +- crates/gpui3/src/platform/mac/platform.rs | 2 +- crates/gpui3/src/scene.rs | 13 +- crates/gpui3/src/window.rs | 150 +++++++++++++++-- 7 files changed, 309 insertions(+), 22 deletions(-) create mode 100644 crates/gpui3/src/elements/interactive.rs diff --git a/crates/gpui3/src/elements.rs b/crates/gpui3/src/elements.rs index 5bd95ccebb69bdc6ac536d5bb89d990ae66aafb1..98f448138a215475e7db972d6746f5c97f5d18fe 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -1,11 +1,13 @@ mod div; mod img; +mod interactive; mod stateless; mod svg; mod text; pub use div::*; pub use img::*; +pub use interactive::*; pub use stateless::*; pub use svg::*; pub use text::*; diff --git a/crates/gpui3/src/elements/interactive.rs b/crates/gpui3/src/elements/interactive.rs new file mode 100644 index 0000000000000000000000000000000000000000..dcdfa0fc92471025fb1584883597786bf37d10f7 --- /dev/null +++ b/crates/gpui3/src/elements/interactive.rs @@ -0,0 +1,152 @@ +use crate::{ + Bounds, DispatchPhase, MouseButton, MouseDownEvent, MouseUpEvent, Pixels, ViewContext, +}; +use parking_lot::Mutex; +use smallvec::SmallVec; +use std::sync::Arc; + +pub trait Interactive { + fn interaction_listeners(&mut self) -> &mut InteractionHandlers; + + fn on_mouse_down( + mut self, + button: MouseButton, + handler: impl Fn(&mut S, &MouseDownEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self + where + Self: Sized, + { + self.interaction_listeners() + .mouse_down + .push(Arc::new(move |view, event, phase, cx| { + if phase == DispatchPhase::Bubble && event.button == button { + handler(view, event, cx) + } + })); + self + } + + fn on_mouse_up( + mut self, + button: MouseButton, + handler: impl Fn(&mut S, &MouseUpEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self + where + Self: Sized, + { + self.interaction_listeners() + .mouse_up + .push(Arc::new(move |view, event, phase, cx| { + if phase == DispatchPhase::Bubble && event.button == button { + handler(view, event, cx) + } + })); + self + } + + fn on_mouse_down_out( + mut self, + button: MouseButton, + handler: impl Fn(&mut S, &MouseDownEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self + where + Self: Sized, + { + self.interaction_listeners() + .mouse_down + .push(Arc::new(move |view, event, phase, cx| { + if phase == DispatchPhase::Capture && event.button == button { + handler(view, event, cx) + } + })); + self + } + + fn on_mouse_up_out( + mut self, + button: MouseButton, + handler: impl Fn(&mut S, &MouseUpEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self + where + Self: Sized, + { + self.interaction_listeners() + .mouse_up + .push(Arc::new(move |view, event, phase, cx| { + if event.button == button && phase == DispatchPhase::Capture { + handler(view, event, cx); + } + })); + self + } + + fn on_click( + self, + button: MouseButton, + handler: impl Fn(&mut S, &MouseDownEvent, &MouseUpEvent, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + let down_event = Arc::new(Mutex::new(None)); + self.on_mouse_down(button, { + let down_event = down_event.clone(); + move |_, event, _| { + down_event.lock().replace(event.clone()); + } + }) + .on_mouse_up_out(button, { + let down_event = down_event.clone(); + move |_, _, _| { + down_event.lock().take(); + } + }) + .on_mouse_up(button, move |view, event, cx| { + if let Some(down_event) = down_event.lock().take() { + handler(view, &down_event, event, cx); + } + }) + } +} + +type MouseDownHandler = Arc< + dyn Fn(&mut V, &MouseDownEvent, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, +>; +type MouseUpHandler = + Arc) + Send + Sync + 'static>; + +pub struct InteractionHandlers { + mouse_down: SmallVec<[MouseDownHandler; 2]>, + mouse_up: SmallVec<[MouseUpHandler; 2]>, +} + +impl InteractionHandlers { + pub fn paint(&self, bounds: Bounds, cx: &mut ViewContext) { + for handler in self.mouse_down.iter().cloned() { + cx.on_mouse_event(move |view, event: &MouseDownEvent, phase, cx| { + if bounds.contains_point(event.position) { + handler(view, event, phase, cx); + } + }) + } + for handler in self.mouse_up.iter().cloned() { + cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| { + if bounds.contains_point(event.position) { + handler(view, event, phase, cx); + } + }) + } + } +} + +impl Default for InteractionHandlers { + fn default() -> Self { + Self { + mouse_down: Default::default(), + mouse_up: Default::default(), + } + } +} diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index f6392d2fc0570054dcdd922b753365ba058b3d8a..6e7baf076320e0a252bc663ab7d5b6a7e0fecd37 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -27,6 +27,7 @@ impl MetalAtlas { self.0.lock().texture(id).metal_texture.clone() } + #[allow(dead_code)] pub(crate) fn allocate( &self, size: Size, diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 4dd3250f08c4b0acdd11189e61273c13e71942a9..187f1b82ce0879e10e53024655e06b7b82546a15 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -172,7 +172,7 @@ impl MetalRenderer { let command_buffer = command_queue.new_command_buffer(); let mut instance_offset = 0; - let path_tiles = self.rasterize_paths(scene.paths(), &mut instance_offset, &command_buffer); + // let path_tiles = self.rasterize_paths(scene.paths(), &mut instance_offset, &command_buffer); let render_pass_descriptor = metal::RenderPassDescriptor::new(); let color_attachment = render_pass_descriptor @@ -208,7 +208,7 @@ impl MetalRenderer { PrimitiveBatch::Quads(quads) => { self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder); } - PrimitiveBatch::Paths(paths) => { + PrimitiveBatch::Paths(_paths) => { // self.draw_paths(paths, &mut instance_offset, viewport_size, command_encoder); } PrimitiveBatch::Underlines(underlines) => { @@ -258,11 +258,12 @@ impl MetalRenderer { drawable.present(); } + #[allow(dead_code)] fn rasterize_paths( &mut self, paths: &[Path], - offset: &mut usize, - command_buffer: &metal::CommandBufferRef, + _offset: &mut usize, + _command_buffer: &metal::CommandBufferRef, ) -> HashMap { let mut tiles = HashMap::default(); let mut vertices_by_texture_id = HashMap::default(); @@ -288,7 +289,7 @@ impl MetalRenderer { tiles.insert(path.id, tile); } - for (texture_id, vertices) in vertices_by_texture_id { + for (_texture_id, _vertices) in vertices_by_texture_id { todo!(); // align_offset(offset); // let next_offset = *offset + vertices.len() * mem::size_of::>(); diff --git a/crates/gpui3/src/platform/mac/platform.rs b/crates/gpui3/src/platform/mac/platform.rs index a3f6fbfbe693d2902320bb892e2868dde146990f..f2d21c7068c3b34c252c6e0ffc8bf69f613cd40d 100644 --- a/crates/gpui3/src/platform/mac/platform.rs +++ b/crates/gpui3/src/platform/mac/platform.rs @@ -923,7 +923,7 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) { if let Some(event) = Event::from_native(native_event, None) { let platform = get_foreground_platform(this); if let Some(callback) = platform.0.lock().event.as_mut() { - if callback(event) { + if !callback(event) { return; } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index aa5bf002ddc9a1aebdf0d62c6c1a38c776d344c2..19948b9ffa68f9a137fc1be50a845b68a1210a7c 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,17 +1,17 @@ use crate::{ point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point, - ScaledPixels, + ScaledPixels, StackingOrder, }; use collections::BTreeMap; use etagere::euclid::{Point3D, Vector3D}; use plane_split::{BspSplitter, Polygon as BspPolygon}; -use smallvec::SmallVec; use std::{fmt::Debug, iter::Peekable, mem, slice}; // Exported to metal pub type PointF = Point; -pub type StackingOrder = SmallVec<[u32; 16]>; + pub type LayerId = u32; + pub type DrawOrder = u32; pub(crate) struct SceneBuilder { @@ -180,6 +180,7 @@ pub(crate) struct Scene { } impl Scene { + #[allow(dead_code)] pub fn paths(&self) -> &[Path] { &self.paths } @@ -731,9 +732,9 @@ mod tests { let mut scene = SceneBuilder::new(); assert_eq!(scene.layers_by_order.len(), 0); - scene.insert(&smallvec![1], quad()); - scene.insert(&smallvec![2], shadow()); - scene.insert(&smallvec![3], quad()); + scene.insert(&smallvec![1].into(), quad()); + scene.insert(&smallvec![2].into(), shadow()); + scene.insert(&smallvec![3].into(), quad()); let mut batches_count = 0; for _ in scene.build().batches() { diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 96b97427afa39c0393d97c5e496b39c037d43e20..0aa5584d9bb7e3218271378c9999c61d49a927e8 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,20 +1,45 @@ use crate::{ image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext, AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, - Edges, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, - MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas, PlatformWindow, - Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, - SceneBuilder, Shadow, SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, + Edges, Effect, Element, EntityId, Event, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, + LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas, + PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams, + ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; +use collections::HashMap; +use derive_more::{Deref, DerefMut}; use smallvec::SmallVec; use std::{ - any::TypeId, borrow::Cow, fmt::Debug, future::Future, marker::PhantomData, mem, sync::Arc, + any::{Any, TypeId}, + borrow::Cow, + fmt::Debug, + future::Future, + marker::PhantomData, + mem, + sync::Arc, }; use util::ResultExt; -pub struct AnyWindow {} +#[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default)] +pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>); + +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] +pub enum DispatchPhase { + /// After the capture phase comes the bubble phase, in which event handlers are + /// invoked front to back. This is the phase you'll usually want to use for event handlers. + #[default] + Bubble, + /// During the initial capture phase, event handlers are invoked back to front. This phase + /// is used for special purposes such as clearing the "pressed" state for click events. If + /// you stop event propagation during this phase, you need to know what you're doing. Handlers + /// outside of the immediate region may rely on detecting non-local events during this phase. + Capture, +} + +type MouseEventHandler = + Arc; pub struct Window { handle: AnyWindowHandle, @@ -25,9 +50,11 @@ pub struct Window { content_size: Size, layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, - mouse_position: Point, current_stacking_order: StackingOrder, content_mask_stack: Vec>, + mouse_event_handlers: HashMap>, + propagate_event: bool, + mouse_position: Point, scale_factor: f32, pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, @@ -46,7 +73,6 @@ impl Window { let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); platform_window.on_resize(Box::new({ - let handle = handle; let cx = cx.to_async(); move |content_size, scale_factor| { cx.update_window(handle, |cx| { @@ -65,6 +91,15 @@ impl Window { } })); + platform_window.on_event({ + let cx = cx.to_async(); + Box::new(move |event| { + cx.update_window(handle, |cx| cx.dispatch_event(event)) + .log_err() + .unwrap_or(true) + }) + }); + let platform_window = MainThreadOnly::new(Arc::new(platform_window), cx.executor.clone()); Window { @@ -76,9 +111,11 @@ impl Window { content_size, layout_engine: TaffyLayoutEngine::new(), root_view: None, - mouse_position, - current_stacking_order: SmallVec::new(), + current_stacking_order: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), + mouse_event_handlers: HashMap::default(), + propagate_event: true, + mouse_position, scale_factor, scene_builder: SceneBuilder::new(), dirty: true, @@ -241,6 +278,27 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.rem_size } + pub fn stop_event_propagation(&mut self) { + self.window.propagate_event = false; + } + + pub fn on_mouse_event( + &mut self, + handler: impl Fn(&Event, DispatchPhase, &mut WindowContext) + Send + Sync + 'static, + ) { + let order = self.window.current_stacking_order.clone(); + self.window + .mouse_event_handlers + .entry(TypeId::of::()) + .or_default() + .push(( + order, + Arc::new(move |event: &dyn Any, phase, cx| { + handler(event.downcast_ref().unwrap(), phase, cx) + }), + )) + } + pub fn mouse_position(&self) -> Point { self.window.mouse_position } @@ -529,6 +587,11 @@ impl<'a, 'w> WindowContext<'a, 'w> { pub(crate) fn draw(&mut self) -> Result<()> { let unit_entity = self.unit_entity.clone(); self.update_entity(&unit_entity, |view, cx| { + cx.window + .mouse_event_handlers + .values_mut() + .for_each(Vec::clear); + let mut root_view = cx.window.root_view.take().unwrap(); let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?; let available_space = cx.window.content_size.map(Into::into); @@ -554,6 +617,54 @@ impl<'a, 'w> WindowContext<'a, 'w> { Ok(()) }) } + + fn dispatch_event(&mut self, event: Event) -> bool { + if let Some(any_mouse_event) = event.mouse_event() { + if let Some(mut handlers) = self + .window + .mouse_event_handlers + .remove(&any_mouse_event.type_id()) + { + // We sort these every time, because handlers may add handlers. Probably fast enough. + handlers.sort_by(|(a, _), (b, _)| a.cmp(b)); + + // Handlers may set this to false by calling `stop_propagation`; + self.window.propagate_event = true; + + // Capture phase, events bubble from back to front. Handlers for this phase are used for + // special purposes, such as detecting events outside of a given Bounds. + for (_, handler) in &handlers { + handler(any_mouse_event, DispatchPhase::Capture, self); + if !self.window.propagate_event { + break; + } + } + + // Bubble phase + if self.window.propagate_event { + for (_, handler) in handlers.iter().rev() { + handler(any_mouse_event, DispatchPhase::Bubble, self); + if !self.window.propagate_event { + break; + } + } + } + + handlers.extend( + self.window + .mouse_event_handlers + .get_mut(&any_mouse_event.type_id()) + .into_iter() + .flat_map(|handlers| handlers.drain(..)), + ); + self.window + .mouse_event_handlers + .insert(any_mouse_event.type_id(), handlers); + } + } + + true + } } impl Context for WindowContext<'_, '_> { @@ -786,6 +897,18 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { }) } + pub fn on_mouse_event( + &mut self, + handler: impl Fn(&mut S, &Event, DispatchPhase, &mut ViewContext) + Send + Sync + 'static, + ) { + let handle = self.handle().upgrade(self).unwrap(); + self.window_cx.on_mouse_event(move |event, phase, cx| { + handle.update(cx, |view, cx| { + handler(view, event, phase, cx); + }) + }); + } + pub(crate) fn erase_state(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R { let entity_id = self.unit_entity.id; let mut cx = ViewContext::mutable( @@ -874,3 +997,10 @@ pub struct AnyWindowHandle { pub(crate) id: WindowId, state_type: TypeId, } + +#[cfg(any(test, feature = "test"))] +impl From> for StackingOrder { + fn from(small_vec: SmallVec<[u32; 16]>) -> Self { + StackingOrder(small_vec) + } +} From 8074e6b46ac624b5597bd1309ffcca91b7b604e4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 9 Oct 2023 21:17:56 -0600 Subject: [PATCH 05/13] Add basic mouse event handling --- crates/gpui3/src/elements.rs | 2 - crates/gpui3/src/elements/div.rs | 28 ++++---- crates/gpui3/src/gpui3.rs | 2 + .../gpui3/src/{elements => }/interactive.rs | 65 ++++++++++++------- crates/gpui3/src/window.rs | 9 +-- crates/storybook2/src/collab_panel.rs | 7 +- 6 files changed, 70 insertions(+), 43 deletions(-) rename crates/gpui3/src/{elements => }/interactive.rs (63%) diff --git a/crates/gpui3/src/elements.rs b/crates/gpui3/src/elements.rs index 98f448138a215475e7db972d6746f5c97f5d18fe..5bd95ccebb69bdc6ac536d5bb89d990ae66aafb1 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -1,13 +1,11 @@ mod div; mod img; -mod interactive; mod stateless; mod svg; mod text; pub use div::*; pub use img::*; -pub use interactive::*; pub use stateless::*; pub use svg::*; pub use text::*; diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index f577d9b5c63a44b7290c8e4fae3865fdf0ad11c6..646890e1b319baff62d2be92f71c5b8c2b64e13f 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,6 +1,7 @@ use crate::{ - AnyElement, Bounds, Element, LayoutId, Overflow, ParentElement, Pixels, Point, Refineable, - RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext, + AnyElement, Bounds, Element, Interactive, LayoutId, MouseEventListeners, Overflow, + ParentElement, Pixels, Point, Refineable, RefinementCascade, Result, Style, StyleHelpers, + Styled, ViewContext, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -9,7 +10,7 @@ use util::ResultExt; pub struct Div { styles: RefinementCascade