Detailed changes
@@ -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<S: 'static> IntoAnyElement<S> for ArcCow<'static, str> {
fn into_any(self) -> AnyElement<S> {
@@ -31,10 +31,10 @@ impl<T: Clone + Debug> Point<T> {
impl<T, Rhs> Mul<Rhs> for Point<T>
where
- T: Mul<Rhs, Output = Rhs> + Clone + Debug,
+ T: Mul<Rhs, Output = T> + Clone + Debug,
Rhs: Clone + Debug,
{
- type Output = Point<Rhs>;
+ type Output = Point<T>;
fn mul(self, rhs: Rhs) -> Self::Output {
Point {
@@ -105,7 +105,7 @@ impl<T: Clone + Debug> Clone for Point<T> {
unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Point<T> {}
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Point<T> {}
-#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div)]
+#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div, Hash)]
#[refineable(debug)]
#[repr(C)]
pub struct Size<T: Clone + Debug> {
@@ -129,6 +129,23 @@ impl<T: Clone + Debug> Size<T> {
}
}
+impl<T: Clone + Debug + Ord> Size<T> {
+ 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<T, Rhs> Mul<Rhs> for Size<T>
where
T: Mul<Rhs, Output = Rhs> + Debug + Clone,
@@ -151,6 +168,8 @@ impl<T: Clone + Debug + Mul<S, Output = T>, S: Clone> MulAssign<S> for Size<T> {
}
}
+impl<T: Eq + Debug + Clone> Eq for Size<T> {}
+
impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
fn from(val: Size<Option<Pixels>>) -> Self {
Size {
@@ -202,6 +221,7 @@ unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Bounds<T> {}
impl<T, Rhs> Mul<Rhs> for Bounds<T>
where
T: Mul<Rhs, Output = Rhs> + Clone + Debug,
+ Point<T>: Mul<Rhs, Output = Point<Rhs>>,
Rhs: Clone + Debug,
{
type Output = Bounds<Rhs>;
@@ -522,11 +542,30 @@ impl From<Pixels> 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<u32> for DevicePixels {
}
}
+impl From<DevicePixels> for u64 {
+ fn from(device_pixels: DevicePixels) -> Self {
+ device_pixels.0 as u64
+ }
+}
+
+impl From<u64> for DevicePixels {
+ fn from(val: u64) -> Self {
+ DevicePixels(val as u32)
+ }
+}
+
#[derive(Clone, Copy, Default, Add, Sub, Mul, Div)]
pub struct Rems(f32);
@@ -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::*;
@@ -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<dyn PlatformScreen>;
fn mouse_position(&self) -> Point<Pixels>;
fn as_any_mut(&mut self) -> &mut dyn Any;
- fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
+ fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
fn prompt(
&self,
level: WindowPromptLevel,
@@ -180,7 +180,15 @@ pub trait PlatformTextSystem: Send + Sync {
) -> Vec<usize>;
}
-pub trait InputHandler {
+pub trait PlatformSpriteSystem<Key> {
+ fn get_or_insert_with(
+ &self,
+ key: Key,
+ build: impl FnOnce() -> (Size<DevicePixels>, Vec<u8>),
+ ) -> MonochromeSprite;
+}
+
+pub trait PlatformInputHandler {
fn selected_text_range(&self) -> Option<Range<usize>>;
fn marked_text_range(&self) -> Option<Range<usize>>;
fn text_for_range(&self, range_utf16: Range<usize>) -> Option<String>;
@@ -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::*;
@@ -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<DevicePixels> = 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<DevicePixels>,
+ command_encoder: &metal::RenderCommandEncoderRef,
+ ) {
+ todo!()
+ }
}
fn build_pipeline_state(
@@ -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<float> 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;
+// }
@@ -0,0 +1 @@
+
@@ -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<Box<dyn FnMut() -> bool>>,
close_callback: Option<Box<dyn FnOnce()>>,
appearance_changed_callback: Option<Box<dyn FnMut()>>,
- input_handler: Option<Box<dyn InputHandler>>,
+ input_handler: Option<Box<dyn PlatformInputHandler>>,
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
last_key_equivalent: Option<KeyDownEvent>,
synthetic_drag_counter: usize,
@@ -671,7 +671,7 @@ impl PlatformWindow for MacWindow {
self
}
- fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
+ fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
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<F, R>(window: &Object, f: F) -> Option<R>
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();
@@ -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<f32>;
+pub type StackingOrder = SmallVec<[u32; 16]>;
#[derive(Debug)]
pub struct Scene {
- layers: BTreeMap<u32, SceneLayer>,
- pub(crate) scale_factor: f32,
-}
-
-#[derive(Default, Debug)]
-pub struct SceneLayer {
- pub quads: Vec<Quad>,
- pub symbol: Vec<Symbol>,
+ scale_factor: f32,
+ pub(crate) layers: BTreeMap<StackingOrder, SceneLayer>,
}
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<Primitive>) {
- 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<Primitive>) {
+ 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<Item = &SceneLayer> {
- self.layers.values()
+ pub(crate) fn layers(&mut self) -> impl Iterator<Item = &mut SceneLayer> {
+ self.layers.values_mut()
}
}
-#[derive(Clone, Debug)]
-pub enum Primitive {
- Quad(Quad),
- MonochromeGlyph(Symbol),
+#[derive(Debug, Default)]
+pub(crate) struct SceneLayer {
+ pub quads: Vec<Quad>,
+ pub sprites: Vec<MonochromeSprite>,
+}
+
+impl SceneLayer {
+ pub fn batches(&mut self) -> impl Iterator<Item = PrimitiveBatch> {
+ 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<Item = &'a Quad>,
+ S: Iterator<Item = &'a MonochromeSprite>,
+{
+ next_batch_kind: Option<PrimitiveKind>,
+ quads: &'a [Quad],
+ sprites: &'a [MonochromeSprite],
+ quads_start: usize,
+ sprites_start: usize,
+ quads_iter: Peekable<Q>,
+ sprites_iter: Peekable<S>,
+}
+
+impl<'a, Q: 'a, S: 'a> Iterator for BatchIterator<'a, Q, S>
+where
+ Q: Iterator<Item = &'a Quad>,
+ S: Iterator<Item = &'a MonochromeSprite>,
+{
+ type Item = PrimitiveBatch<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ 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<Item = &'a Quad>,
+ S: Iterator<Item = &'a MonochromeSprite>,
+{
+ fn new(
+ quads: &'a [Quad],
+ quads_iter: Peekable<Q>,
+ sprites: &'a [MonochromeSprite],
+ sprites_iter: Peekable<S>,
+ ) -> 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<PrimitiveKind>) -> Option<u32> {
+ 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<Quad> for Primitive {
}
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Clone, Debug)]
#[repr(C)]
-pub struct Symbol {
+pub struct MonochromeSprite {
pub order: u32,
- pub origin: Point<Pixels>,
- pub font_id: FontId,
- pub font_size: Pixels,
- pub id: GlyphId,
- pub color: Hsla,
+ pub bounds: Bounds<Pixels>,
+ pub atlas_id: AtlasId,
+ pub tile_id: TileId,
+ pub bounds_in_atlas: Bounds<DevicePixels>,
+ pub color: Option<Hsla>,
}
-impl Symbol {
+impl MonochromeSprite {
pub fn scale(&mut self, factor: f32) {
- self.font_size *= factor;
- self.origin *= factor;
+ self.bounds *= factor;
}
}
-impl From<Symbol> for Primitive {
- fn from(glyph: Symbol) -> Self {
- Primitive::MonochromeGlyph(glyph)
+impl From<MonochromeSprite> 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;
@@ -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)),
+ },
+ );
}
}
@@ -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<LineLayout>,
+ style_runs: SmallVec<[StyleRun; 32]>,
+}
+
+#[derive(Debug, Clone)]
+struct StyleRun {
+ len: u32,
+ color: Hsla,
+ underline: UnderlineStyle,
+}
+
+impl Line {
+ pub fn new(layout: Arc<LineLayout>, 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<FontId> {
+ 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<usize> {
+ 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<Pixels>,
+ 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<Pixels>, 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<Pixels>,
+ _visible_bounds: Bounds<Pixels>,
+ 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<Pixels>, 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(())
+ }
+}
@@ -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<AnyView<()>>,
mouse_position: Point<Pixels>,
- 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<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
- self.window.z_index_stack.push(z_index);
+ pub fn stack<R>(&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<R>(
&self,
f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,