Checkpoint
This commit is contained in:
parent
77e67c19fe
commit
6046ed4f5c
7 changed files with 220 additions and 496 deletions
|
@ -3,7 +3,7 @@ use crate::{
|
|||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
use util::{arc_cow::ArcCow, ResultExt};
|
||||
use util::arc_cow::ArcCow;
|
||||
|
||||
impl<S: 'static> IntoAnyElement<S> for ArcCow<'static, str> {
|
||||
fn into_any(self) -> AnyElement<S> {
|
||||
|
@ -92,8 +92,6 @@ impl<S: 'static> Element for Text<S> {
|
|||
paint_state: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<S>,
|
||||
) -> Result<()> {
|
||||
let bounds = layout.bounds;
|
||||
|
||||
let line;
|
||||
let line_height;
|
||||
{
|
||||
|
@ -108,8 +106,8 @@ impl<S: 'static> Element for Text<S> {
|
|||
let _text_style = cx.text_style();
|
||||
|
||||
// todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder.");
|
||||
let visible_bounds = bounds;
|
||||
line.paint(bounds.origin, visible_bounds, line_height, cx)?;
|
||||
let visible_bounds = layout.bounds;
|
||||
line.paint(&layout, visible_bounds, line_height, cx)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -425,7 +425,9 @@ unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Corners<T> {}
|
|||
|
||||
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Corners<T> {}
|
||||
|
||||
#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)]
|
||||
#[derive(
|
||||
Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, Zeroable, Pod,
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
pub struct Pixels(pub(crate) f32);
|
||||
|
||||
|
@ -445,6 +447,12 @@ impl Mul<Pixels> for f32 {
|
|||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f32> for Pixels {
|
||||
fn mul_assign(&mut self, other: f32) {
|
||||
self.0 *= other;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixels {
|
||||
pub fn round(&self) -> Self {
|
||||
Self(self.0.round())
|
||||
|
@ -489,9 +497,6 @@ impl From<f32> for Pixels {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Pixels {}
|
||||
unsafe impl bytemuck::Zeroable for Pixels {}
|
||||
|
||||
impl Debug for Pixels {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} px", self.0)
|
||||
|
|
|
@ -7,174 +7,194 @@ float4 hsla_to_rgba(Hsla hsla);
|
|||
float4 to_device_position(float2 pixel_position, float2 viewport_size);
|
||||
|
||||
struct QuadVertexOutput {
|
||||
float4 position [[position]];
|
||||
float4 background_color;
|
||||
float4 border_color;
|
||||
uint quad_id;
|
||||
float4 position [[position]];
|
||||
float4 background_color;
|
||||
float4 border_color;
|
||||
uint quad_id;
|
||||
};
|
||||
|
||||
vertex QuadVertexOutput quad_vertex(
|
||||
uint unit_vertex_id [[vertex_id]],
|
||||
uint quad_id [[instance_id]],
|
||||
uint unit_vertex_id [[vertex_id]], uint quad_id [[instance_id]],
|
||||
constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]],
|
||||
constant Quad *quads [[buffer(QuadInputIndex_Quads)]],
|
||||
constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]
|
||||
) {
|
||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
Quad quad = quads[quad_id];
|
||||
float2 position_2d = unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + float2(quad.bounds.origin.x, quad.bounds.origin.y);
|
||||
position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x);
|
||||
position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width, position_2d.x);
|
||||
position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y);
|
||||
position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height, position_2d.y);
|
||||
constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]) {
|
||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||
Quad quad = quads[quad_id];
|
||||
float2 position_2d =
|
||||
unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) +
|
||||
float2(quad.bounds.origin.x, quad.bounds.origin.y);
|
||||
position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x);
|
||||
position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width,
|
||||
position_2d.x);
|
||||
position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y);
|
||||
position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height,
|
||||
position_2d.y);
|
||||
|
||||
float2 viewport_size = float2((float)uniforms->viewport_size.width, (float)uniforms->viewport_size.height);
|
||||
float4 device_position = to_device_position(position_2d, viewport_size);
|
||||
float4 background_color = hsla_to_rgba(quad.background);
|
||||
float4 border_color = hsla_to_rgba(quad.border_color);
|
||||
return QuadVertexOutput {
|
||||
device_position,
|
||||
background_color,
|
||||
border_color,
|
||||
quad_id
|
||||
};
|
||||
float2 viewport_size = float2((float)uniforms->viewport_size.width,
|
||||
(float)uniforms->viewport_size.height);
|
||||
float4 device_position = to_device_position(position_2d, viewport_size);
|
||||
float4 background_color = hsla_to_rgba(quad.background);
|
||||
float4 border_color = hsla_to_rgba(quad.border_color);
|
||||
return QuadVertexOutput{device_position, background_color, border_color,
|
||||
quad_id};
|
||||
}
|
||||
|
||||
float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels 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;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = corner_radii.top_left;
|
||||
} else {
|
||||
corner_radius = corner_radii.bottom_left;
|
||||
}
|
||||
float quad_sdf(float2 point, Bounds_Pixels bounds,
|
||||
Corners_Pixels 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;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = corner_radii.top_left;
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = corner_radii.top_right;
|
||||
} else {
|
||||
corner_radius = corner_radii.bottom_right;
|
||||
}
|
||||
corner_radius = corner_radii.bottom_left;
|
||||
}
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = corner_radii.top_right;
|
||||
} else {
|
||||
corner_radius = corner_radii.bottom_right;
|
||||
}
|
||||
}
|
||||
|
||||
float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
|
||||
float distance = length(max(0., rounded_edge_to_point))
|
||||
+ min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y))
|
||||
- corner_radius;
|
||||
float2 rounded_edge_to_point =
|
||||
abs(center_to_point) - half_size + corner_radius;
|
||||
float distance =
|
||||
length(max(0., rounded_edge_to_point)) +
|
||||
min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
|
||||
corner_radius;
|
||||
|
||||
return distance;
|
||||
return distance;
|
||||
}
|
||||
|
||||
fragment float4 quad_fragment(
|
||||
QuadVertexOutput input [[stage_in]],
|
||||
constant Quad *quads [[buffer(QuadInputIndex_Quads)]]
|
||||
) {
|
||||
Quad quad = quads[input.quad_id];
|
||||
float2 half_size = float2(quad.bounds.size.width, quad.bounds.size.height) / 2.;
|
||||
float2 center = float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size;
|
||||
float2 center_to_point = input.position.xy - center;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = quad.corner_radii.top_left;
|
||||
} else {
|
||||
corner_radius = quad.corner_radii.bottom_left;
|
||||
}
|
||||
fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
|
||||
constant Quad *quads
|
||||
[[buffer(QuadInputIndex_Quads)]]) {
|
||||
Quad quad = quads[input.quad_id];
|
||||
float2 half_size =
|
||||
float2(quad.bounds.size.width, quad.bounds.size.height) / 2.;
|
||||
float2 center =
|
||||
float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size;
|
||||
float2 center_to_point = input.position.xy - center;
|
||||
float corner_radius;
|
||||
if (center_to_point.x < 0.) {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = quad.corner_radii.top_left;
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = quad.corner_radii.top_right;
|
||||
} else {
|
||||
corner_radius = quad.corner_radii.bottom_right;
|
||||
}
|
||||
corner_radius = quad.corner_radii.bottom_left;
|
||||
}
|
||||
|
||||
float2 rounded_edge_to_point = fabs(center_to_point) - half_size + corner_radius;
|
||||
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
|
||||
|
||||
float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left : quad.border_widths.right;
|
||||
float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top : quad.border_widths.bottom;
|
||||
float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
|
||||
float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
|
||||
float border_width;
|
||||
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
|
||||
border_width = 0.;
|
||||
} else if (point_to_inset_corner.y > point_to_inset_corner.x) {
|
||||
border_width = horizontal_border;
|
||||
} else {
|
||||
if (center_to_point.y < 0.) {
|
||||
corner_radius = quad.corner_radii.top_right;
|
||||
} else {
|
||||
border_width = vertical_border;
|
||||
corner_radius = quad.corner_radii.bottom_right;
|
||||
}
|
||||
}
|
||||
|
||||
float4 color;
|
||||
if (border_width == 0.) {
|
||||
color = input.background_color;
|
||||
} else {
|
||||
float inset_distance = distance + border_width;
|
||||
float2 rounded_edge_to_point =
|
||||
fabs(center_to_point) - half_size + corner_radius;
|
||||
float distance =
|
||||
length(max(0., rounded_edge_to_point)) +
|
||||
min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
|
||||
corner_radius;
|
||||
|
||||
// Decrease border's opacity as we move inside the background.
|
||||
input.border_color.a *= 1. - saturate(0.5 - inset_distance);
|
||||
float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left
|
||||
: quad.border_widths.right;
|
||||
float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top
|
||||
: quad.border_widths.bottom;
|
||||
float2 inset_size =
|
||||
half_size - corner_radius - float2(vertical_border, horizontal_border);
|
||||
float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
|
||||
float border_width;
|
||||
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
|
||||
border_width = 0.;
|
||||
} else if (point_to_inset_corner.y > point_to_inset_corner.x) {
|
||||
border_width = horizontal_border;
|
||||
} else {
|
||||
border_width = vertical_border;
|
||||
}
|
||||
|
||||
// Alpha-blend the border and the background.
|
||||
float output_alpha = quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
|
||||
float3 premultiplied_border_rgb = input.border_color.rgb * quad.border_color.a;
|
||||
float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a;
|
||||
float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a);
|
||||
color = float4(premultiplied_output_rgb, output_alpha);
|
||||
}
|
||||
float4 color;
|
||||
if (border_width == 0.) {
|
||||
color = input.background_color;
|
||||
} else {
|
||||
float inset_distance = distance + border_width;
|
||||
|
||||
float clip_distance = quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
|
||||
return color * float4(1., 1., 1., saturate(0.5 - distance) * saturate(0.5 - clip_distance));
|
||||
// Decrease border's opacity as we move inside the background.
|
||||
input.border_color.a *= 1. - saturate(0.5 - inset_distance);
|
||||
|
||||
// Alpha-blend the border and the background.
|
||||
float output_alpha =
|
||||
quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
|
||||
float3 premultiplied_border_rgb =
|
||||
input.border_color.rgb * quad.border_color.a;
|
||||
float3 premultiplied_background_rgb =
|
||||
input.background_color.rgb * input.background_color.a;
|
||||
float3 premultiplied_output_rgb =
|
||||
premultiplied_border_rgb +
|
||||
premultiplied_background_rgb * (1. - input.border_color.a);
|
||||
color = float4(premultiplied_output_rgb, output_alpha);
|
||||
}
|
||||
|
||||
float clip_distance =
|
||||
quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
|
||||
return color *
|
||||
float4(1., 1., 1.,
|
||||
saturate(0.5 - distance) * saturate(0.5 - clip_distance));
|
||||
}
|
||||
|
||||
float4 hsla_to_rgba(Hsla hsla) {
|
||||
float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
|
||||
float s = hsla.s;
|
||||
float l = hsla.l;
|
||||
float a = hsla.a;
|
||||
float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
|
||||
float s = hsla.s;
|
||||
float l = hsla.l;
|
||||
float a = hsla.a;
|
||||
|
||||
float c = (1.0 - fabs(2.0*l - 1.0)) * s;
|
||||
float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
|
||||
float m = l - c/2.0;
|
||||
float c = (1.0 - fabs(2.0 * l - 1.0)) * s;
|
||||
float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
|
||||
float m = l - c / 2.0;
|
||||
|
||||
float r = 0.0;
|
||||
float g = 0.0;
|
||||
float b = 0.0;
|
||||
float r = 0.0;
|
||||
float g = 0.0;
|
||||
float b = 0.0;
|
||||
|
||||
if (h >= 0.0 && h < 1.0) {
|
||||
r = c;
|
||||
g = x;
|
||||
b = 0.0;
|
||||
} else if (h >= 1.0 && h < 2.0) {
|
||||
r = x;
|
||||
g = c;
|
||||
b = 0.0;
|
||||
} else if (h >= 2.0 && h < 3.0) {
|
||||
r = 0.0;
|
||||
g = c;
|
||||
b = x;
|
||||
} else if (h >= 3.0 && h < 4.0) {
|
||||
r = 0.0;
|
||||
g = x;
|
||||
b = c;
|
||||
} else if (h >= 4.0 && h < 5.0) {
|
||||
r = x;
|
||||
g = 0.0;
|
||||
b = c;
|
||||
} else {
|
||||
r = c;
|
||||
g = 0.0;
|
||||
b = x;
|
||||
}
|
||||
if (h >= 0.0 && h < 1.0) {
|
||||
r = c;
|
||||
g = x;
|
||||
b = 0.0;
|
||||
} else if (h >= 1.0 && h < 2.0) {
|
||||
r = x;
|
||||
g = c;
|
||||
b = 0.0;
|
||||
} else if (h >= 2.0 && h < 3.0) {
|
||||
r = 0.0;
|
||||
g = c;
|
||||
b = x;
|
||||
} else if (h >= 3.0 && h < 4.0) {
|
||||
r = 0.0;
|
||||
g = x;
|
||||
b = c;
|
||||
} else if (h >= 4.0 && h < 5.0) {
|
||||
r = x;
|
||||
g = 0.0;
|
||||
b = c;
|
||||
} else {
|
||||
r = c;
|
||||
g = 0.0;
|
||||
b = x;
|
||||
}
|
||||
|
||||
float4 rgba;
|
||||
rgba.x = (r + m);
|
||||
rgba.y = (g + m);
|
||||
rgba.z = (b + m);
|
||||
rgba.w = a;
|
||||
return rgba;
|
||||
float4 rgba;
|
||||
rgba.x = (r + m);
|
||||
rgba.y = (g + m);
|
||||
rgba.z = (b + m);
|
||||
rgba.w = a;
|
||||
return rgba;
|
||||
}
|
||||
|
||||
float4 to_device_position(float2 pixel_position, float2 viewport_size) {
|
||||
return float4(pixel_position / viewport_size * float2(2., -2.) + float2(-1., 1.), 0., 1.);
|
||||
return float4(pixel_position / viewport_size * float2(2., -2.) +
|
||||
float2(-1., 1.),
|
||||
0., 1.);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::mem;
|
||||
|
||||
use super::{Bounds, Hsla, Pixels, Point};
|
||||
use crate::{Corners, Edges};
|
||||
use crate::{Corners, Edges, FontId, GlyphId};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use collections::BTreeMap;
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub struct Scene {
|
|||
#[derive(Default, Debug)]
|
||||
pub struct SceneLayer {
|
||||
pub quads: Vec<Quad>,
|
||||
pub symbol: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
|
@ -40,6 +41,7 @@ impl Scene {
|
|||
let layer = self.layers.entry(primitive.order()).or_default();
|
||||
match primitive {
|
||||
Primitive::Quad(quad) => layer.quads.push(quad),
|
||||
Primitive::MonochromeGlyph(glyph) => layer.symbol.push(glyph),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,20 +53,14 @@ impl Scene {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum Primitive {
|
||||
Quad(Quad),
|
||||
MonochromeGlyph(Symbol),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
pub fn order(&self) -> u32 {
|
||||
match self {
|
||||
Primitive::Quad(quad) => quad.order,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
match self {
|
||||
Primitive::Quad(quad) => {
|
||||
quad.background.is_transparent() && quad.border_color.is_transparent()
|
||||
}
|
||||
Primitive::MonochromeGlyph(glyph) => glyph.order,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +69,9 @@ impl Primitive {
|
|||
Primitive::Quad(quad) => {
|
||||
quad.scale(factor);
|
||||
}
|
||||
Primitive::MonochromeGlyph(glyph) => {
|
||||
glyph.scale(factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,3 +118,27 @@ impl From<Quad> for Primitive {
|
|||
Primitive::Quad(quad)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Symbol {
|
||||
pub order: u32,
|
||||
pub origin: Point<Pixels>,
|
||||
pub font_id: FontId,
|
||||
pub font_size: Pixels,
|
||||
pub id: GlyphId,
|
||||
pub color: Hsla,
|
||||
}
|
||||
|
||||
impl Symbol {
|
||||
pub fn scale(&mut self, factor: f32) {
|
||||
self.font_size *= factor;
|
||||
self.origin *= factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Symbol> for Primitive {
|
||||
fn from(glyph: Symbol) -> Self {
|
||||
Primitive::MonochromeGlyph(glyph)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
mod font_features;
|
||||
mod line;
|
||||
mod line_wrapper;
|
||||
mod text_layout_cache;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
pub use font_features::*;
|
||||
pub use line::*;
|
||||
use line_wrapper::*;
|
||||
pub use text_layout_cache::*;
|
||||
|
||||
|
@ -20,7 +23,8 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct FontId(pub usize);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
|
||||
|
@ -51,7 +55,6 @@ impl TextSystem {
|
|||
|
||||
pub fn font_id(&self, font: &Font) -> Result<FontId> {
|
||||
let font_id = self.font_ids_by_font.read().get(font).copied();
|
||||
|
||||
if let Some(font_id) = font_id {
|
||||
Ok(font_id)
|
||||
} else {
|
||||
|
@ -160,29 +163,18 @@ impl TextSystem {
|
|||
) -> Result<Line> {
|
||||
let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default();
|
||||
|
||||
dbg!("got font runs from pool");
|
||||
let mut last_font: Option<&Font> = None;
|
||||
for (len, style) in runs {
|
||||
dbg!(len);
|
||||
if let Some(last_font) = last_font.as_ref() {
|
||||
dbg!("a");
|
||||
if **last_font == style.font {
|
||||
dbg!("b");
|
||||
font_runs.last_mut().unwrap().0 += len;
|
||||
dbg!("c");
|
||||
continue;
|
||||
}
|
||||
dbg!("d");
|
||||
}
|
||||
dbg!("e");
|
||||
last_font = Some(&style.font);
|
||||
dbg!("f");
|
||||
font_runs.push((*len, self.font_id(&style.font)?));
|
||||
dbg!("g");
|
||||
}
|
||||
|
||||
dbg!("built font runs");
|
||||
|
||||
let layout = self
|
||||
.text_layout_cache
|
||||
.layout_line(text, font_size, &font_runs);
|
||||
|
@ -332,7 +324,8 @@ pub struct RunStyle {
|
|||
pub underline: Option<UnderlineStyle>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct GlyphId(u32);
|
||||
|
||||
impl From<GlyphId> for u32 {
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use crate::{
|
||||
black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point,
|
||||
Run, RunStyle, UnderlineStyle, WindowContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use crate::{FontId, Glyph, LineLayout, Pixels, PlatformTextSystem, Run};
|
||||
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -40,7 +36,6 @@ impl TextLayoutCache {
|
|||
font_size: Pixels,
|
||||
runs: &[(usize, FontId)],
|
||||
) -> Arc<LineLayout> {
|
||||
dbg!("layout line");
|
||||
let key = &CacheKeyRef {
|
||||
text,
|
||||
font_size,
|
||||
|
@ -145,332 +140,12 @@ impl<'a> Hash for CacheKeyRef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ShapedBoundary {
|
||||
pub run_ix: usize,
|
||||
pub glyph_ix: usize,
|
||||
}
|
||||
|
||||
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,
|
||||
origin: Point<Pixels>,
|
||||
visible_bounds: Bounds<Pixels>,
|
||||
line_height: Pixels,
|
||||
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 style_runs = self.style_runs.iter();
|
||||
let mut run_end = 0;
|
||||
let mut color = black();
|
||||
let mut underline = None;
|
||||
|
||||
for run in &self.layout.runs {
|
||||
cx.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 {
|
||||
// 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 {
|
||||
// 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 {
|
||||
// cx.scene().push_glyph(scene::Glyph {
|
||||
// font_id: run.font_id,
|
||||
// font_size: self.layout.font_size,
|
||||
// id: glyph.id,
|
||||
// origin: glyph_origin,
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Run {
|
||||
pub fn glyphs(&self) -> &[Glyph] {
|
||||
&self.glyphs
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
};
|
||||
use anyhow::Result;
|
||||
use futures::Future;
|
||||
use smallvec::SmallVec;
|
||||
use std::{any::TypeId, marker::PhantomData, mem, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
||||
|
@ -18,6 +19,7 @@ pub struct Window {
|
|||
layout_engine: TaffyLayoutEngine,
|
||||
pub(crate) root_view: Option<AnyView<()>>,
|
||||
mouse_position: Point<Pixels>,
|
||||
z_index_stack: SmallVec<[u32; 8]>,
|
||||
pub(crate) scene: Scene,
|
||||
pub(crate) dirty: bool,
|
||||
}
|
||||
|
@ -56,6 +58,7 @@ impl Window {
|
|||
layout_engine: TaffyLayoutEngine::new(),
|
||||
root_view: None,
|
||||
mouse_position,
|
||||
z_index_stack: SmallVec::new(),
|
||||
scene: Scene::new(scale_factor),
|
||||
dirty: true,
|
||||
}
|
||||
|
@ -126,6 +129,13 @@ 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);
|
||||
let result = f(self);
|
||||
self.window.z_index_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
pub fn run_on_main<R>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue