diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index 64d14ee28536c9e62e7ac9569563f401ad175f90..15e655ced682aaca8c92e7b27367ef47d67c6e74 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -45,6 +45,7 @@ fn generate_shader_bindings() -> PathBuf { "Pixels".into(), "PointF".into(), "Hsla".into(), + "ScaledContentMask".into(), "Uniforms".into(), "AtlasTile".into(), "Quad".into(), @@ -58,12 +59,14 @@ fn generate_shader_bindings() -> PathBuf { .with_src(crate_dir.join("src/scene.rs")) .with_src(crate_dir.join("src/geometry.rs")) .with_src(crate_dir.join("src/color.rs")) + .with_src(crate_dir.join("src/window.rs")) .with_src(crate_dir.join("src/platform.rs")) .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs")) .with_config(config) .generate() .expect("Unable to generate bindings") .write_to_file(&output_path); + output_path } diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 6bae65c5a26ba5b7d33f26bdc0987027b6a7c167..535ccb995306a14252565f99ebb9eb130cb11230 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,6 +1,6 @@ use crate::{ AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point, - Refineable, RefinementCascade, Result, StackContext, Style, StyleHelpers, Styled, ViewContext, + Refineable, RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -33,16 +33,9 @@ impl Element for Div { cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)> { let style = self.computed_style(); - let child_layout_ids = if let Some(text_style) = style.text_style(cx) { - cx.with_text_style(text_style.clone(), |cx| self.layout_children(view, cx))? - } else { - self.layout_children(view, cx)? - }; - - Ok(( - cx.request_layout(style.into(), child_layout_ids.clone())?, - child_layout_ids, - )) + let child_layout_ids = style.apply_text_style(cx, |cx| self.layout_children(view, cx))?; + let layout_id = cx.request_layout(style.into(), child_layout_ids.clone())?; + Ok((layout_id, child_layout_ids)) } fn paint( @@ -56,20 +49,18 @@ impl Element for Div { let style = self.computed_style(); style.paint(order, bounds, cx); - let overflow = &style.overflow; + // // todo!("support only one dimension being hidden") + let overflow = &style.overflow; // if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible { - // cx.scene().push_layer(Some(bounds)); - // pop_layer = true; + // cx.clip(layout.bounds, style.corner_radii, || ) // } - if let Some(text_style) = style.text_style(cx) { - cx.with_text_style(text_style.clone(), |cx| { - self.paint_children(overflow, state, cx) - })?; - } else { - self.paint_children(overflow, state, cx)?; - } + style.apply_text_style(cx, |cx| { + style.apply_overflow(layout.bounds, cx, |cx| { + self.paint_children(overflow, state, cx) + }) + })?; self.handle_scroll(order, bounds, style.overflow.clone(), child_layouts, cx); // todo!("enable inspector") diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 4ab7fc803031cae9f1f02c4411e7c95fa28c2b06..183554204493ff8f4acbd4043f6d64793e4b4c84 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -225,6 +225,20 @@ pub struct Bounds { pub size: Size, } +impl> Bounds { + pub fn from_corners(upper_left: Point, lower_right: Point) -> Self { + let origin = Point { + x: upper_left.x.clone(), + y: upper_left.y.clone(), + }; + let size = Size { + width: lower_right.x - upper_left.x, + height: lower_right.y - upper_left.y, + }; + Bounds { origin, size } + } +} + impl Mul for Bounds where T: Mul + Clone + Debug, @@ -418,6 +432,28 @@ pub struct Corners { pub bottom_left: T, } +impl Corners { + pub fn to_pixels(&self, rem_size: Pixels) -> Corners { + Corners { + top_left: self.top_left.to_pixels(rem_size), + top_right: self.top_right.to_pixels(rem_size), + bottom_right: self.bottom_right.to_pixels(rem_size), + bottom_left: self.bottom_left.to_pixels(rem_size), + } + } +} + +impl Corners { + pub fn scale(&self, factor: f32) -> Corners { + Corners { + top_left: self.top_left.scale(factor), + top_right: self.top_right.scale(factor), + bottom_right: self.bottom_right.scale(factor), + bottom_left: self.bottom_left.scale(factor), + } + } +} + impl Corners { pub fn map U>(&self, f: F) -> Corners { Corners { diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index d143c979df061083ddd5b1de7553940aaa815a41..8f6a2680519fb2d17279a8b99056db6ab397b223 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -84,16 +84,16 @@ impl DerefMut for MainThread { } } -pub trait StackContext { - fn app(&mut self) -> &mut AppContext; +pub trait BorrowAppContext { + fn app_mut(&mut self) -> &mut AppContext; fn with_text_style(&mut self, style: TextStyleRefinement, f: F) -> R where F: FnOnce(&mut Self) -> R, { - self.app().push_text_style(style); + self.app_mut().push_text_style(style); let result = f(self); - self.app().pop_text_style(); + self.app_mut().pop_text_style(); result } @@ -101,9 +101,9 @@ pub trait StackContext { where F: FnOnce(&mut Self) -> R, { - self.app().push_state(state); + self.app_mut().push_state(state); let result = f(self); - self.app().pop_state::(); + self.app_mut().pop_state::(); result } } diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 7c98db779bc9f8f8ed19fd76bc771f8a967ab92f..3de028deee4863fbc7e1af21b195e65f86ef49f6 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -4,10 +4,10 @@ using namespace metal; float4 hsla_to_rgba(Hsla hsla); -float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, - Bounds_Pixels clip_bounds, +float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, + Bounds_ScaledPixels clip_bounds, constant Size_DevicePixels *viewport_size); -float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii); +float quad_sdf(float2 point, Bounds_ScaledPixels bounds, Corners_ScaledPixels corner_radii); struct QuadVertexOutput { float4 position [[position]]; @@ -131,7 +131,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex( float2 unit_vertex = unit_vertices[unit_vertex_id]; MonochromeSprite sprite = sprites[sprite_id]; float4 device_position = to_device_position( - unit_vertex, sprite.bounds, sprite.clip_bounds, viewport_size); + unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size); float2 tile_origin = float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y); @@ -157,7 +157,7 @@ fragment float4 monochrome_sprite_fragment( float4 sample = atlas_texture.sample(atlas_texture_sampler, input.tile_position); float clip_distance = - quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii); + quad_sdf(input.position.xy, sprite.content_mask.bounds, sprite.content_mask.corner_radii); float4 color = input.color; color.a *= sample.a * saturate(0.5 - clip_distance); return color; @@ -211,8 +211,8 @@ float4 hsla_to_rgba(Hsla hsla) { return rgba; } -float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, - Bounds_Pixels clip_bounds, +float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, + Bounds_ScaledPixels clip_bounds, constant Size_DevicePixels *input_viewport_size) { float2 position = unit_vertex * float2(bounds.size.width, bounds.size.height) + @@ -229,8 +229,8 @@ float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, return float4(device_position, 0., 1.); } -float quad_sdf(float2 point, Bounds_Pixels bounds, - Corners_Pixels corner_radii) { +float quad_sdf(float2 point, Bounds_ScaledPixels bounds, + Corners_ScaledPixels corner_radii) { float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; float2 center_to_point = point - center; diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index fb66da7f5ccd6b8b4bc89b0a8b483eb994dfb7a6..6ffc6e0bb3aa698296ce2b4e27b0b2cbe34597ed 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Point}; -use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels}; +use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels}; use collections::BTreeMap; use smallvec::SmallVec; @@ -234,8 +234,7 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub clip_bounds: Bounds, - pub clip_corner_radii: Corners, + pub content_mask: ScaledContentMask, pub color: Hsla, pub tile: AtlasTile, } diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index 431a87491f1f5d6f9a6a87114b48c119b881b2c3..c4ce8250c0bbde2c7298698116bc912f8dc2bc9e 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -1,8 +1,8 @@ use crate::{ - phi, rems, AbsoluteLength, Bounds, Corners, CornersRefinement, DefiniteLength, Edges, - EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, - PointRefinement, Quad, Rems, Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext, - WindowContext, + 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, + SharedString, Size, SizeRefinement, ViewContext, WindowContext, }; use refineable::Refineable; pub use taffy::style::{ @@ -179,6 +179,57 @@ impl Style { } } + pub fn apply_text_style(&self, cx: &mut C, f: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&mut C) -> R, + { + if self.text.is_some() { + cx.with_text_style(self.text.clone(), f) + } else { + f(cx) + } + } + + /// Apply overflow to content mask + pub fn apply_overflow(&self, bounds: Bounds, cx: &mut C, f: F) -> R + where + C: BorrowWindow, + F: FnOnce(&mut C) -> R, + { + let current_mask = cx.content_mask(); + + let min = current_mask.bounds.origin; + let max = current_mask.bounds.lower_right(); + + let mask_corner_radii = Corners::default(); + let mask_bounds = match ( + self.overflow.x == Overflow::Visible, + self.overflow.y == Overflow::Visible, + ) { + // x and y both visible + (true, true) => return f(cx), + // x visible, y hidden + (true, false) => Bounds::from_corners( + point(min.x, bounds.origin.y), + point(max.x, bounds.lower_right().y), + ), + // x hidden, y visible + (false, true) => Bounds::from_corners( + point(bounds.origin.x, min.y), + point(bounds.lower_right().x, max.y), + ), + // both hidden + (false, false) => bounds, + }; + let mask = ContentMask { + bounds: mask_bounds, + corner_radii: mask_corner_radii, + }; + + cx.with_content_mask(mask, f) + } + /// Paints the background of an element styled with this style. pub fn paint(&self, order: u32, bounds: Bounds, cx: &mut ViewContext) { let rem_size = cx.rem_size(); diff --git a/crates/gpui3/src/util.rs b/crates/gpui3/src/util.rs index 11cdfdc3ab7db1862c1e5e0827e94ebbbcc4e3d8..c76408392402a7a151ec93f54ea0a6aaeca0b720 100644 --- a/crates/gpui3/src/util.rs +++ b/crates/gpui3/src/util.rs @@ -2,12 +2,6 @@ use smol::future::FutureExt; use std::{future::Future, time::Duration}; pub use util::*; -pub fn post_inc(value: &mut usize) -> usize { - let prev = *value; - *value += 1; - prev -} - pub async fn timeout(timeout: Duration, f: F) -> Result where F: Future, diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 50533f8f2840d516db3b02acbe8032ed570cba45..34048b54519a8c80509d31698ae9b20897860748 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,8 @@ use crate::{ - px, AnyView, AppContext, AvailableSpace, Bounds, Context, Corners, Effect, Element, EntityId, - FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread, - MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, - Scene, Size, StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners, Effect, + Element, EntityId, FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, + MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, + Reference, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; @@ -74,8 +74,24 @@ impl Window { #[derive(Clone, Debug)] pub struct ContentMask { - bounds: Bounds, - corner_radii: Corners, + pub bounds: Bounds, + pub corner_radii: Corners, +} + +impl ContentMask { + pub fn scale(&self, factor: f32) -> ScaledContentMask { + ScaledContentMask { + bounds: self.bounds.scale(factor), + corner_radii: self.corner_radii.scale(factor), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct ScaledContentMask { + bounds: Bounds, + corner_radii: Corners, } pub struct WindowContext<'a, 'w> { @@ -174,20 +190,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.current_layer_id.clone() } - pub fn current_clipping_mask(&self) -> ContentMask { - self.window - .content_mask_stack - .last() - .cloned() - .unwrap_or_else(|| ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.window.content_size, - }, - corner_radii: Default::default(), - }) - } - pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, @@ -239,14 +241,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), size: tile.bounds.size.map(Into::into), }; + let content_mask = self.content_mask().scale(scale_factor); self.window.scene.insert( layer_id, MonochromeSprite { order, bounds, - clip_bounds: bounds, - clip_corner_radii: Default::default(), + content_mask, color, tile, }, @@ -330,8 +332,60 @@ impl<'a, 'w> std::ops::DerefMut for WindowContext<'a, 'w> { } } -impl StackContext for ViewContext<'_, '_, S> { - fn app(&mut self) -> &mut AppContext { +impl BorrowAppContext for WindowContext<'_, '_> { + fn app_mut(&mut self) -> &mut AppContext { + &mut *self.app + } +} + +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 { + self.window_mut().content_mask_stack.push(mask); + let result = f(self); + self.window_mut().content_mask_stack.pop(); + result + } + + fn content_mask(&self) -> ContentMask { + self.window() + .content_mask_stack + .last() + .cloned() + .unwrap_or_else(|| ContentMask { + bounds: Bounds { + origin: Point::default(), + size: self.window().content_size, + }, + corner_radii: Default::default(), + }) + } + + fn rem_size(&self) -> Pixels { + self.window().rem_size + } +} + +impl BorrowWindow for WindowContext<'_, '_> { + fn window(&self) -> &Window { + &*self.window + } + + fn window_mut(&mut self) -> &mut Window { + &mut *self.window + } +} + +pub struct ViewContext<'a, 'w, S> { + window_cx: WindowContext<'a, 'w>, + entity_type: PhantomData, + entity_id: EntityId, +} + +impl BorrowAppContext for ViewContext<'_, '_, S> { + fn app_mut(&mut self) -> &mut AppContext { &mut *self.window_cx.app } @@ -356,10 +410,14 @@ impl StackContext for ViewContext<'_, '_, S> { } } -pub struct ViewContext<'a, 'w, S> { - window_cx: WindowContext<'a, 'w>, - entity_type: PhantomData, - entity_id: EntityId, +impl BorrowWindow for ViewContext<'_, '_, S> { + fn window(&self) -> &Window { + &self.window_cx.window + } + + fn window_mut(&mut self) -> &mut Window { + &mut *self.window_cx.window + } } impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { diff --git a/crates/storybook2/src/theme.rs b/crates/storybook2/src/theme.rs index 88bc21775cbe7ee69655f61b7784a0adf3b73e68..b28a0f289cca4d4ece5f6b6a340890018bd5ca61 100644 --- a/crates/storybook2/src/theme.rs +++ b/crates/storybook2/src/theme.rs @@ -1,4 +1,6 @@ -use gpui3::{Element, Hsla, Layout, LayoutId, Result, StackContext, ViewContext, WindowContext}; +use gpui3::{ + BorrowAppContext, Element, Hsla, Layout, LayoutId, Result, ViewContext, WindowContext, +}; use serde::{de::Visitor, Deserialize, Deserializer}; use std::{collections::HashMap, fmt};