diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index 8044e1ab9ee3c0ffd03846fc0549a2c7d3878e62..1c1bd89c0e996d4df53cee355181d2a5035a220d 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -3,7 +3,7 @@ use crate::{ }; use parking_lot::Mutex; use std::{marker::PhantomData, sync::Arc}; -use util::arc_cow::ArcCow; +use util::{arc_cow::ArcCow, ResultExt}; impl IntoAnyElement for ArcCow<'static, str> { fn into_any(self) -> AnyElement { diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index ec5788be25aef116cca6f75c89ab0d156e107d95..7cffe4b59bac1881954ce359424efc3b4bfea730 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -31,10 +31,10 @@ impl Point { impl Mul for Point where - T: Mul + Clone + Debug, + T: Mul + Clone + Debug, Rhs: Clone + Debug, { - type Output = Point; + type Output = Point; fn mul(self, rhs: Rhs) -> Self::Output { Point { @@ -105,7 +105,7 @@ impl Clone for Point { unsafe impl Zeroable for Point {} unsafe impl Pod for Point {} -#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div)] +#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div, Hash)] #[refineable(debug)] #[repr(C)] pub struct Size { @@ -129,6 +129,23 @@ impl Size { } } +impl Size { + pub fn max(&self, other: &Self) -> Self { + Size { + width: if self.width >= other.width { + self.width.clone() + } else { + other.width.clone() + }, + height: if self.height >= other.height { + self.height.clone() + } else { + other.height.clone() + }, + } + } +} + impl Mul for Size where T: Mul + Debug + Clone, @@ -151,6 +168,8 @@ impl, S: Clone> MulAssign for Size { } } +impl Eq for Size {} + impl From>> for Size> { fn from(val: Size>) -> Self { Size { @@ -202,6 +221,7 @@ unsafe impl Pod for Bounds {} impl Mul for Bounds where T: Mul + Clone + Debug, + Point: Mul>, Rhs: Clone + Debug, { type Output = Bounds; @@ -522,11 +542,30 @@ impl From for f64 { } #[derive( - Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, + Add, + AddAssign, + Clone, + Copy, + Debug, + Default, + Div, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Sub, + SubAssign, )] #[repr(transparent)] pub struct DevicePixels(pub(crate) u32); +impl DevicePixels { + pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 { + self.0 * bytes_per_pixel as u32 + } +} + unsafe impl bytemuck::Pod for DevicePixels {} unsafe impl bytemuck::Zeroable for DevicePixels {} @@ -542,6 +581,18 @@ impl From for DevicePixels { } } +impl From for u64 { + fn from(device_pixels: DevicePixels) -> Self { + device_pixels.0 as u64 + } +} + +impl From for DevicePixels { + fn from(val: u64) -> Self { + DevicePixels(val as u32) + } +} + #[derive(Clone, Copy, Default, Add, Sub, Mul, Div)] pub struct Rems(f32); diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index d62b42ed747edc31dcddffec669cf5fd30ea239b..d143c979df061083ddd5b1de7553940aaa815a41 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -23,6 +23,7 @@ pub use elements::*; pub use executor::*; pub use geometry::*; pub use gpui3_macros::*; + pub use platform::*; pub use refineable::*; pub use scene::*; diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index be407702fd65f0cf5fc2c661da53f62befea9595..81d02af0839aa706eae73a97f4025c19debbc03e 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, Font, FontId, FontMetrics, GlyphId, LineLayout, Pixels, Point, Result, - Scene, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, LineLayout, + MonochromeSprite, Pixels, Point, Result, Scene, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -122,7 +122,7 @@ pub trait PlatformWindow { fn screen(&self) -> Rc; fn mouse_position(&self) -> Point; fn as_any_mut(&mut self) -> &mut dyn Any; - fn set_input_handler(&mut self, input_handler: Box); + fn set_input_handler(&mut self, input_handler: Box); fn prompt( &self, level: WindowPromptLevel, @@ -180,7 +180,15 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait InputHandler { +pub trait PlatformSpriteSystem { + fn get_or_insert_with( + &self, + key: Key, + build: impl FnOnce() -> (Size, Vec), + ) -> MonochromeSprite; +} + +pub trait PlatformInputHandler { fn selected_text_range(&self) -> Option>; fn marked_text_range(&self) -> Option>; fn text_for_range(&self, range_utf16: Range) -> Option; diff --git a/crates/gpui3/src/platform/mac.rs b/crates/gpui3/src/platform/mac.rs index 8ad9385c114cc73e019857bb0524ae535d802a6b..fec4274168af5af1f66990bfb28e1de5ff2cb388 100644 --- a/crates/gpui3/src/platform/mac.rs +++ b/crates/gpui3/src/platform/mac.rs @@ -6,6 +6,7 @@ mod metal_renderer; mod open_type; mod platform; mod screen; +mod sprite; mod text_system; mod window; mod window_appearence; @@ -32,6 +33,7 @@ use std::{ pub use dispatcher::*; pub use platform::*; pub use screen::*; +pub use sprite::*; pub use text_system::*; pub use window::*; diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index e19f130f96c53d36d4f42684356c7254f1fe6e7a..d49b0d94f4dd82b99469ddaed12729d40070042c 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,4 +1,4 @@ -use crate::{point, size, DevicePixels, Quad, Scene, Size}; +use crate::{point, size, DevicePixels, MonochromeSprite, Quad, Scene, Size}; use bytemuck::{Pod, Zeroable}; use cocoa::{ base::{NO, YES}, @@ -102,9 +102,7 @@ impl MetalRenderer { &*self.layer } - pub fn draw(&mut self, scene: &Scene) { - dbg!(scene); - + pub fn draw(&mut self, scene: &mut Scene) { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); let viewport_size: Size = size( @@ -159,21 +157,35 @@ impl MetalRenderer { zfar: 1.0, }); - let mut buffer_offset = 0; + let mut instance_offset = 0; for layer in scene.layers() { - self.draw_quads( - &layer.quads, - &mut buffer_offset, - viewport_size, - command_encoder, - ); + for batch in layer.batches() { + match batch { + crate::PrimitiveBatch::Quads(quads) => { + self.draw_quads( + quads, + &mut instance_offset, + viewport_size, + command_encoder, + ); + } + crate::PrimitiveBatch::Sprites(sprites) => { + self.draw_monochrome_sprites( + sprites, + &mut instance_offset, + viewport_size, + command_encoder, + ); + } + } + } } command_encoder.end_encoding(); self.instances.did_modify_range(NSRange { location: 0, - length: buffer_offset as NSUInteger, + length: instance_offset as NSUInteger, }); command_buffer.commit(); @@ -238,6 +250,16 @@ impl MetalRenderer { ); *offset = next_offset; } + + fn draw_monochrome_sprites( + &mut self, + monochrome: &[MonochromeSprite], + offset: &mut usize, + viewport_size: Size, + command_encoder: &metal::RenderCommandEncoderRef, + ) { + todo!() + } } fn build_pipeline_state( diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 798ef3d66c1f32914b25253ec93af347846e06ab..76fb0e2d1c9ae67f5c1c4ba4423b29e401de9637 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -198,3 +198,52 @@ float4 to_device_position(float2 pixel_position, float2 viewport_size) { float2(-1., 1.), 0., 1.); } + +// struct SpriteFragmentInput { +// float4 position [[position]]; +// float2 atlas_position; +// float4 color [[flat]]; +// uchar compute_winding [[flat]]; +// }; + +// vertex SpriteFragmentInput sprite_vertex( +// uint unit_vertex_id [[vertex_id]], +// uint sprite_id [[instance_id]], +// constant float2 *unit_vertices +// [[buffer(GPUISpriteVertexInputIndexVertices)]], constant GPUISprite +// *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]], constant float2 +// *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]], +// constant float2 *atlas_size +// [[buffer(GPUISpriteVertexInputIndexAtlasSize)]] +// ) { +// float2 unit_vertex = unit_vertices[unit_vertex_id]; +// GPUISprite sprite = sprites[sprite_id]; +// float2 position = unit_vertex * sprite.target_size + sprite.origin; +// float4 device_position = to_device_position(position, *viewport_size); +// float2 atlas_position = (unit_vertex * sprite.source_size + +// sprite.atlas_origin) / *atlas_size; + +// return SpriteFragmentInput { +// device_position, +// atlas_position, +// coloru_to_colorf(sprite.color), +// sprite.compute_winding +// }; +// } + +// fragment float4 sprite_fragment( +// SpriteFragmentInput input [[stage_in]], +// texture2d atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]] +// ) { +// constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); +// float4 color = input.color; +// float4 sample = atlas.sample(atlas_sampler, input.atlas_position); +// float mask; +// if (input.compute_winding) { +// mask = 1. - abs(1. - fmod(sample.r, 2.)); +// } else { +// mask = sample.a; +// } +// color.a *= mask; +// return color; +// } diff --git a/crates/gpui3/src/platform/mac/sprite.rs b/crates/gpui3/src/platform/mac/sprite.rs new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/crates/gpui3/src/platform/mac/sprite.rs @@ -0,0 +1 @@ + diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 5c0355ddacd11cc57707bcaae7bd9719858c38b8..3cf471b45ae9f27ccfd00e66b58c619ab6388027 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,8 +1,8 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke, - MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, - MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow, + point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, + ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, + Pixels, Platform, PlatformDispatcher, PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; @@ -292,7 +292,7 @@ struct MacWindowState { should_close_callback: Option bool>>, close_callback: Option>, appearance_changed_callback: Option>, - input_handler: Option>, + input_handler: Option>, pending_key_down: Option<(KeyDownEvent, Option)>, last_key_equivalent: Option, synthetic_drag_counter: usize, @@ -671,7 +671,7 @@ impl PlatformWindow for MacWindow { self } - fn set_input_handler(&mut self, input_handler: Box) { + fn set_input_handler(&mut self, input_handler: Box) { self.0.as_ref().lock().input_handler = Some(input_handler); } @@ -1357,9 +1357,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(scene) = window_state.scene_to_render.take() { - dbg!("render", &scene); - window_state.renderer.draw(&scene); + if let Some(mut scene) = window_state.scene_to_render.take() { + window_state.renderer.draw(&mut scene); } } } @@ -1580,7 +1579,7 @@ async fn synthetic_drag( fn with_input_handler(window: &Object, f: F) -> Option where - F: FnOnce(&mut dyn InputHandler) -> R, + F: FnOnce(&mut dyn PlatformInputHandler) -> R, { let window_state = unsafe { get_window_state(window) }; let mut lock = window_state.as_ref().lock(); diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 6e7ee82a4186554ac96b3fa1a596484e382f78eb..7d29c9f7025de1a99f0a7f85726aa5f73c341aa9 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,82 +1,201 @@ -use std::mem; +use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{Corners, Edges, FontId, GlyphId}; +use crate::{Corners, DevicePixels, Edges}; use bytemuck::{Pod, Zeroable}; -use collections::BTreeMap; // Exported to metal pub type PointF = Point; +pub type StackingOrder = SmallVec<[u32; 16]>; #[derive(Debug)] pub struct Scene { - layers: BTreeMap, - pub(crate) scale_factor: f32, -} - -#[derive(Default, Debug)] -pub struct SceneLayer { - pub quads: Vec, - pub symbol: Vec, + scale_factor: f32, + pub(crate) layers: BTreeMap, } impl Scene { pub fn new(scale_factor: f32) -> Scene { Scene { - layers: Default::default(), scale_factor, + layers: BTreeMap::new(), } } pub fn take(&mut self) -> Scene { Scene { - layers: mem::take(&mut self.layers), scale_factor: self.scale_factor, + layers: mem::take(&mut self.layers), } } - pub fn insert(&mut self, primitive: impl Into) { - let mut primitive = primitive.into(); - primitive.scale(self.scale_factor); - let layer = self.layers.entry(primitive.order()).or_default(); + pub fn insert(&mut self, order: StackingOrder, primitive: impl Into) { + let layer = self.layers.entry(order).or_default(); + + let primitive = primitive.into(); match primitive { - Primitive::Quad(quad) => layer.quads.push(quad), - Primitive::MonochromeGlyph(glyph) => layer.symbol.push(glyph), + Primitive::Quad(mut quad) => { + quad.scale(self.scale_factor); + layer.quads.push(quad); + } + Primitive::Sprite(mut sprite) => { + sprite.scale(self.scale_factor); + layer.sprites.push(sprite); + } } } - pub fn layers(&self) -> impl Iterator { - self.layers.values() + pub(crate) fn layers(&mut self) -> impl Iterator { + self.layers.values_mut() } } -#[derive(Clone, Debug)] -pub enum Primitive { - Quad(Quad), - MonochromeGlyph(Symbol), +#[derive(Debug, Default)] +pub(crate) struct SceneLayer { + pub quads: Vec, + pub sprites: Vec, +} + +impl SceneLayer { + pub fn batches(&mut self) -> impl Iterator { + self.quads.sort_unstable_by(|a, b| a.order.cmp(&b.order)); + self.sprites.sort_unstable_by(|a, b| a.order.cmp(&b.order)); + + BatchIterator::new( + &self.quads, + self.quads.iter().peekable(), + &self.sprites, + self.sprites.iter().peekable(), + ) + } } -impl Primitive { - pub fn order(&self) -> u32 { - match self { - Primitive::Quad(quad) => quad.order, - Primitive::MonochromeGlyph(glyph) => glyph.order, +struct BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + next_batch_kind: Option, + quads: &'a [Quad], + sprites: &'a [MonochromeSprite], + quads_start: usize, + sprites_start: usize, + quads_iter: Peekable, + sprites_iter: Peekable, +} + +impl<'a, Q: 'a, S: 'a> Iterator for BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + type Item = PrimitiveBatch<'a>; + + fn next(&mut self) -> Option { + if let Some(batch_kind) = self.next_batch_kind.take() { + match batch_kind { + PrimitiveKind::Quad => { + let max_order = self + .next_order(Some(PrimitiveKind::Quad)) + .unwrap_or(u32::MAX); + let quads_start = self.quads_start; + let quads_end = quads_start + + self + .quads_iter + .by_ref() + .take_while(|quad| quad.order <= max_order) + .count(); + self.quads_start = quads_end; + Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) + } + PrimitiveKind::Sprite => { + let max_order = self + .next_order(Some(PrimitiveKind::Sprite)) + .unwrap_or(u32::MAX); + let sprites_start = self.sprites_start; + let sprites_end = sprites_start + + self + .sprites_iter + .by_ref() + .take_while(|sprite| sprite.order <= max_order) + .count(); + self.sprites_start = sprites_end; + Some(PrimitiveBatch::Sprites( + &self.sprites[sprites_start..sprites_end], + )) + } + } + } else { + None } } +} + +impl<'a, Q: 'a, S: 'a> BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + fn new( + quads: &'a [Quad], + quads_iter: Peekable, + sprites: &'a [MonochromeSprite], + sprites_iter: Peekable, + ) -> Self { + let mut this = Self { + quads, + quads_start: 0, + quads_iter, + sprites, + sprites_start: 0, + sprites_iter, + next_batch_kind: None, + }; + this.next_order(None); // Called for its side effect of setting this.next_batch_kind + this + } - pub fn scale(&mut self, factor: f32) { - match self { - Primitive::Quad(quad) => { - quad.scale(factor); + fn next_order(&mut self, exclude_kind: Option) -> Option { + let mut next_order = u32::MAX; + + if exclude_kind != Some(PrimitiveKind::Quad) { + if let Some(next_quad) = self.quads_iter.peek() { + self.next_batch_kind = Some(PrimitiveKind::Quad); + next_order = next_quad.order; } - Primitive::MonochromeGlyph(glyph) => { - glyph.scale(factor); + } + + if exclude_kind != Some(PrimitiveKind::Sprite) { + if let Some(next_sprite) = self.sprites_iter.peek() { + if next_sprite.order < next_order { + self.next_batch_kind = Some(PrimitiveKind::Sprite); + next_order = next_sprite.order; + } } } + + (next_order < u32::MAX).then_some(next_order) } } -#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PrimitiveKind { + Quad, + Sprite, +} + +#[derive(Clone, Debug)] +pub enum Primitive { + Quad(Quad), + Sprite(MonochromeSprite), +} + +pub enum PrimitiveBatch<'a> { + Quads(&'a [Quad]), + Sprites(&'a [MonochromeSprite]), +} + +#[derive(Debug, Copy, Clone, Zeroable, Pod)] #[repr(C)] pub struct Quad { pub order: u32, @@ -119,26 +238,32 @@ impl From for Primitive { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Debug)] #[repr(C)] -pub struct Symbol { +pub struct MonochromeSprite { pub order: u32, - pub origin: Point, - pub font_id: FontId, - pub font_size: Pixels, - pub id: GlyphId, - pub color: Hsla, + pub bounds: Bounds, + pub atlas_id: AtlasId, + pub tile_id: TileId, + pub bounds_in_atlas: Bounds, + pub color: Option, } -impl Symbol { +impl MonochromeSprite { pub fn scale(&mut self, factor: f32) { - self.font_size *= factor; - self.origin *= factor; + self.bounds *= factor; } } -impl From for Primitive { - fn from(glyph: Symbol) -> Self { - Primitive::MonochromeGlyph(glyph) +impl From for Primitive { + fn from(sprite: MonochromeSprite) -> Self { + Primitive::Sprite(sprite) } } + +#[derive(Copy, Clone, Debug)] +pub struct AtlasId(pub(crate) usize); + +use collections::BTreeMap; +use etagere::AllocId as TileId; +use smallvec::SmallVec; diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index 863736e4a3f7eb614276834b96e796f0cfaa815f..f972b2ead3a436504962930b666ae0375466ea10 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -185,16 +185,19 @@ impl Style { let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { - cx.scene().insert(Quad { - order, - bounds, - clip_bounds: bounds, // todo! - clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), - background: background_color.unwrap_or_default(), - border_color: self.border_color.unwrap_or_default(), - corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), - border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), - }); + cx.scene().insert( + todo!(), + Quad { + order, + bounds, + clip_bounds: bounds, // todo! + clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), + background: background_color.unwrap_or_default(), + border_color: self.border_color.unwrap_or_default(), + corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), + border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), + }, + ); } } diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs new file mode 100644 index 0000000000000000000000000000000000000000..14f1d96f54994a4a24f53f6042081aa35d453726 --- /dev/null +++ b/crates/gpui3/src/text_system/line.rs @@ -0,0 +1,333 @@ +use crate::{ + black, point, px, Bounds, FontId, Hsla, Layout, LineLayout, Pixels, Point, Run, RunStyle, + ShapedBoundary, UnderlineStyle, WindowContext, +}; +use anyhow::Result; +use smallvec::SmallVec; +use std::sync::Arc; + +#[derive(Default, Debug, Clone)] +pub struct Line { + layout: Arc, + style_runs: SmallVec<[StyleRun; 32]>, +} + +#[derive(Debug, Clone)] +struct StyleRun { + len: u32, + color: Hsla, + underline: UnderlineStyle, +} + +impl Line { + pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { + let mut style_runs = SmallVec::new(); + for (len, style) in runs { + style_runs.push(StyleRun { + len: *len as u32, + color: style.color, + underline: style.underline.clone().unwrap_or_default(), + }); + } + Self { layout, style_runs } + } + + pub fn runs(&self) -> &[Run] { + &self.layout.runs + } + + pub fn width(&self) -> Pixels { + self.layout.width + } + + pub fn font_size(&self) -> Pixels { + self.layout.font_size + } + + pub fn x_for_index(&self, index: usize) -> Pixels { + for run in &self.layout.runs { + for glyph in &run.glyphs { + if glyph.index >= index { + return glyph.position.x; + } + } + } + self.layout.width + } + + pub fn font_for_index(&self, index: usize) -> Option { + for run in &self.layout.runs { + for glyph in &run.glyphs { + if glyph.index >= index { + return Some(run.font_id); + } + } + } + + None + } + + pub fn len(&self) -> usize { + self.layout.len + } + + pub fn is_empty(&self) -> bool { + self.layout.len == 0 + } + + pub fn index_for_x(&self, x: Pixels) -> Option { + if x >= self.layout.width { + None + } else { + for run in self.layout.runs.iter().rev() { + for glyph in run.glyphs.iter().rev() { + if glyph.position.x <= x { + return Some(glyph.index); + } + } + } + Some(0) + } + } + + // todo! + pub fn paint( + &self, + layout: &Layout, + visible_bounds: Bounds, + line_height: Pixels, + cx: &mut WindowContext, + ) -> Result<()> { + let origin = layout.bounds.origin; + let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; + let baseline_offset = point(px(0.), padding_top + self.layout.ascent); + + let mut style_runs = self.style_runs.iter(); + let mut run_end = 0; + let mut color = black(); + let mut underline = None; + let text_system = cx.text_system().clone(); + + for run in &self.layout.runs { + text_system.with_font(run.font_id, |system, font| { + let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width; + + for glyph in &run.glyphs { + let glyph_origin = origin + baseline_offset + glyph.position; + if glyph_origin.x > visible_bounds.upper_right().x { + break; + } + + let mut finished_underline: Option<(Point, UnderlineStyle)> = None; + if glyph.index >= run_end { + if let Some(style_run) = style_runs.next() { + if let Some((_, underline_style)) = &mut underline { + if style_run.underline != *underline_style { + finished_underline = underline.take(); + } + } + if style_run.underline.thickness > px(0.) { + underline.get_or_insert(( + point( + glyph_origin.x, + origin.y + + baseline_offset.y + + (self.layout.descent * 0.618), + ), + UnderlineStyle { + color: style_run.underline.color, + thickness: style_run.underline.thickness, + squiggly: style_run.underline.squiggly, + }, + )); + } + + run_end += style_run.len as usize; + color = style_run.color; + } else { + run_end = self.layout.len; + finished_underline = underline.take(); + } + } + + if glyph_origin.x + max_glyph_width < visible_bounds.origin.x { + continue; + } + + if let Some((_underline_origin, _underline_style)) = finished_underline { + todo!() + // cx.scene().insert(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + if glyph.is_emoji { + todo!() + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_origin, + // }); + } else { + todo!() + // cx.scene().insert(Symbol { + // order: layout.order, + // origin, + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // color, + // }); + } + } + + anyhow::Ok(()) + })??; + } + + if let Some((_underline_start, _underline_style)) = underline.take() { + let _line_end_x = origin.x + self.layout.width; + // cx.scene().push_underline(Underline { + // origin: underline_start, + // width: line_end_x - underline_start.x, + // color: underline_style.color, + // thickness: underline_style.thickness.into(), + // squiggly: underline_style.squiggly, + // }); + } + + Ok(()) + } + + pub fn paint_wrapped( + &self, + origin: Point, + _visible_bounds: Bounds, + line_height: Pixels, + boundaries: &[ShapedBoundary], + cx: &mut WindowContext, + ) -> Result<()> { + let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; + let baseline_offset = point(px(0.), padding_top + self.layout.ascent); + + let mut boundaries = boundaries.into_iter().peekable(); + let mut color_runs = self.style_runs.iter(); + let mut style_run_end = 0; + let mut _color = black(); // todo! + let mut underline: Option<(Point, UnderlineStyle)> = None; + + let mut glyph_origin = origin; + let mut prev_position = px(0.); + for (run_ix, run) in self.layout.runs.iter().enumerate() { + for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { + glyph_origin.x += glyph.position.x - prev_position; + + if boundaries + .peek() + .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix) + { + boundaries.next(); + if let Some((_underline_origin, _underline_style)) = underline.take() { + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + glyph_origin = point(origin.x, glyph_origin.y + line_height); + } + prev_position = glyph.position.x; + + let mut finished_underline = None; + if glyph.index >= style_run_end { + if let Some(style_run) = color_runs.next() { + style_run_end += style_run.len as usize; + _color = style_run.color; + if let Some((_, underline_style)) = &mut underline { + if style_run.underline != *underline_style { + finished_underline = underline.take(); + } + } + if style_run.underline.thickness > px(0.) { + underline.get_or_insert(( + glyph_origin + + point( + px(0.), + baseline_offset.y + (self.layout.descent * 0.618), + ), + UnderlineStyle { + color: Some( + style_run.underline.color.unwrap_or(style_run.color), + ), + thickness: style_run.underline.thickness, + squiggly: style_run.underline.squiggly, + }, + )); + } + } else { + style_run_end = self.layout.len; + _color = black(); + finished_underline = underline.take(); + } + } + + if let Some((_underline_origin, _underline_style)) = finished_underline { + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + cx.text_system().with_font(run.font_id, |system, font| { + let _glyph_bounds = Bounds { + origin: glyph_origin, + size: system.bounding_box(font, self.layout.font_size)?.size, + }; + // if glyph_bounds.intersects(visible_bounds) { + // if glyph.is_emoji { + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // }); + // } else { + // cx.scene().push_glyph(scene::Glyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // color, + // }); + // } + // } + anyhow::Ok(()) + })??; + } + } + + if let Some((_underline_origin, _underline_style)) = underline.take() { + // let line_end_x = glyph_origin.x + self.layout.width - prev_position; + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: line_end_x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color, + // squiggly: underline_style.squiggly, + // }); + } + + Ok(()) + } +} diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 8ec4063a275a52308c9d6f8689ec2d9246f62940..b4a893cb5653752c9178dbf41c371a6017ef2e64 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,7 +1,7 @@ use crate::{ px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformWindow, Point, Reference, Scene, Size, - StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, }; use anyhow::Result; use futures::Future; @@ -19,7 +19,7 @@ pub struct Window { layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, mouse_position: Point, - z_index_stack: SmallVec<[u32; 8]>, + current_stacking_order: StackingOrder, pub(crate) scene: Scene, pub(crate) dirty: bool, } @@ -58,7 +58,7 @@ impl Window { layout_engine: TaffyLayoutEngine::new(), root_view: None, mouse_position, - z_index_stack: SmallVec::new(), + current_stacking_order: SmallVec::new(), scene: Scene::new(scale_factor), dirty: true, } @@ -129,13 +129,17 @@ impl<'a, 'w> WindowContext<'a, 'w> { &mut self.window.scene } - pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { - self.window.z_index_stack.push(z_index); + 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); - self.window.z_index_stack.pop(); + self.window.current_stacking_order.pop(); result } + pub fn current_stack_order(&self) -> StackingOrder { + self.window.current_stacking_order.clone() + } + pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static,