Checkpoint: underlines
This commit is contained in:
parent
65c7765c07
commit
ca6eb5511c
9 changed files with 495 additions and 155 deletions
|
@ -50,10 +50,12 @@ fn generate_shader_bindings() -> PathBuf {
|
||||||
"ScaledContentMask".into(),
|
"ScaledContentMask".into(),
|
||||||
"Uniforms".into(),
|
"Uniforms".into(),
|
||||||
"AtlasTile".into(),
|
"AtlasTile".into(),
|
||||||
"QuadInputIndex".into(),
|
|
||||||
"Quad".into(),
|
|
||||||
"ShadowInputIndex".into(),
|
"ShadowInputIndex".into(),
|
||||||
"Shadow".into(),
|
"Shadow".into(),
|
||||||
|
"QuadInputIndex".into(),
|
||||||
|
"Underline".into(),
|
||||||
|
"UnderlineInputIndex".into(),
|
||||||
|
"Quad".into(),
|
||||||
"SpriteInputIndex".into(),
|
"SpriteInputIndex".into(),
|
||||||
"MonochromeSprite".into(),
|
"MonochromeSprite".into(),
|
||||||
"PolychromeSprite".into(),
|
"PolychromeSprite".into(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
|
point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite,
|
||||||
Quad, Scene, Shadow, Size,
|
PrimitiveBatch, Quad, Scene, Shadow, Size, Underline,
|
||||||
};
|
};
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
base::{NO, YES},
|
base::{NO, YES},
|
||||||
|
@ -17,8 +17,9 @@ const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decisio
|
||||||
pub struct MetalRenderer {
|
pub struct MetalRenderer {
|
||||||
layer: metal::MetalLayer,
|
layer: metal::MetalLayer,
|
||||||
command_queue: CommandQueue,
|
command_queue: CommandQueue,
|
||||||
quads_pipeline_state: metal::RenderPipelineState,
|
|
||||||
shadows_pipeline_state: metal::RenderPipelineState,
|
shadows_pipeline_state: metal::RenderPipelineState,
|
||||||
|
quads_pipeline_state: metal::RenderPipelineState,
|
||||||
|
underlines_pipeline_state: metal::RenderPipelineState,
|
||||||
monochrome_sprites_pipeline_state: metal::RenderPipelineState,
|
monochrome_sprites_pipeline_state: metal::RenderPipelineState,
|
||||||
polychrome_sprites_pipeline_state: metal::RenderPipelineState,
|
polychrome_sprites_pipeline_state: metal::RenderPipelineState,
|
||||||
unit_vertices: metal::Buffer,
|
unit_vertices: metal::Buffer,
|
||||||
|
@ -83,6 +84,14 @@ impl MetalRenderer {
|
||||||
MTLResourceOptions::StorageModeManaged,
|
MTLResourceOptions::StorageModeManaged,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let shadows_pipeline_state = build_pipeline_state(
|
||||||
|
&device,
|
||||||
|
&library,
|
||||||
|
"shadows",
|
||||||
|
"shadow_vertex",
|
||||||
|
"shadow_fragment",
|
||||||
|
PIXEL_FORMAT,
|
||||||
|
);
|
||||||
let quads_pipeline_state = build_pipeline_state(
|
let quads_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
&library,
|
&library,
|
||||||
|
@ -91,12 +100,12 @@ impl MetalRenderer {
|
||||||
"quad_fragment",
|
"quad_fragment",
|
||||||
PIXEL_FORMAT,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let shadows_pipeline_state = build_pipeline_state(
|
let underlines_pipeline_state = build_pipeline_state(
|
||||||
&device,
|
&device,
|
||||||
&library,
|
&library,
|
||||||
"shadows",
|
"underlines",
|
||||||
"shadow_vertex",
|
"underline_vertex",
|
||||||
"shadow_fragment",
|
"underline_fragment",
|
||||||
PIXEL_FORMAT,
|
PIXEL_FORMAT,
|
||||||
);
|
);
|
||||||
let monochrome_sprites_pipeline_state = build_pipeline_state(
|
let monochrome_sprites_pipeline_state = build_pipeline_state(
|
||||||
|
@ -122,8 +131,9 @@ impl MetalRenderer {
|
||||||
Self {
|
Self {
|
||||||
layer,
|
layer,
|
||||||
command_queue,
|
command_queue,
|
||||||
quads_pipeline_state,
|
|
||||||
shadows_pipeline_state,
|
shadows_pipeline_state,
|
||||||
|
quads_pipeline_state,
|
||||||
|
underlines_pipeline_state,
|
||||||
monochrome_sprites_pipeline_state,
|
monochrome_sprites_pipeline_state,
|
||||||
polychrome_sprites_pipeline_state,
|
polychrome_sprites_pipeline_state,
|
||||||
unit_vertices,
|
unit_vertices,
|
||||||
|
@ -184,10 +194,7 @@ impl MetalRenderer {
|
||||||
let mut instance_offset = 0;
|
let mut instance_offset = 0;
|
||||||
for batch in scene.batches() {
|
for batch in scene.batches() {
|
||||||
match batch {
|
match batch {
|
||||||
crate::PrimitiveBatch::Quads(quads) => {
|
PrimitiveBatch::Shadows(shadows) => {
|
||||||
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
|
|
||||||
}
|
|
||||||
crate::PrimitiveBatch::Shadows(shadows) => {
|
|
||||||
self.draw_shadows(
|
self.draw_shadows(
|
||||||
shadows,
|
shadows,
|
||||||
&mut instance_offset,
|
&mut instance_offset,
|
||||||
|
@ -195,7 +202,18 @@ impl MetalRenderer {
|
||||||
command_encoder,
|
command_encoder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
crate::PrimitiveBatch::MonochromeSprites {
|
PrimitiveBatch::Quads(quads) => {
|
||||||
|
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder);
|
||||||
|
}
|
||||||
|
PrimitiveBatch::Underlines(underlines) => {
|
||||||
|
self.draw_underlines(
|
||||||
|
underlines,
|
||||||
|
&mut instance_offset,
|
||||||
|
viewport_size,
|
||||||
|
command_encoder,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PrimitiveBatch::MonochromeSprites {
|
||||||
texture_id,
|
texture_id,
|
||||||
sprites,
|
sprites,
|
||||||
} => {
|
} => {
|
||||||
|
@ -207,7 +225,7 @@ impl MetalRenderer {
|
||||||
command_encoder,
|
command_encoder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
crate::PrimitiveBatch::PolychromeSprites {
|
PrimitiveBatch::PolychromeSprites {
|
||||||
texture_id,
|
texture_id,
|
||||||
sprites,
|
sprites,
|
||||||
} => {
|
} => {
|
||||||
|
@ -234,62 +252,6 @@ impl MetalRenderer {
|
||||||
drawable.present();
|
drawable.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_quads(
|
|
||||||
&mut self,
|
|
||||||
quads: &[Quad],
|
|
||||||
offset: &mut usize,
|
|
||||||
viewport_size: Size<DevicePixels>,
|
|
||||||
command_encoder: &metal::RenderCommandEncoderRef,
|
|
||||||
) {
|
|
||||||
if quads.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
align_offset(offset);
|
|
||||||
|
|
||||||
command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
|
|
||||||
command_encoder.set_vertex_buffer(
|
|
||||||
QuadInputIndex::Vertices as u64,
|
|
||||||
Some(&self.unit_vertices),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
command_encoder.set_vertex_buffer(
|
|
||||||
QuadInputIndex::Quads as u64,
|
|
||||||
Some(&self.instances),
|
|
||||||
*offset as u64,
|
|
||||||
);
|
|
||||||
command_encoder.set_fragment_buffer(
|
|
||||||
QuadInputIndex::Quads as u64,
|
|
||||||
Some(&self.instances),
|
|
||||||
*offset as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
command_encoder.set_vertex_bytes(
|
|
||||||
QuadInputIndex::ViewportSize as u64,
|
|
||||||
mem::size_of_val(&viewport_size) as u64,
|
|
||||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
|
||||||
);
|
|
||||||
|
|
||||||
let quad_bytes_len = mem::size_of::<Quad>() * quads.len();
|
|
||||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
let next_offset = *offset + quad_bytes_len;
|
|
||||||
assert!(
|
|
||||||
next_offset <= INSTANCE_BUFFER_SIZE,
|
|
||||||
"instance buffer exhausted"
|
|
||||||
);
|
|
||||||
|
|
||||||
command_encoder.draw_primitives_instanced(
|
|
||||||
metal::MTLPrimitiveType::Triangle,
|
|
||||||
0,
|
|
||||||
6,
|
|
||||||
quads.len() as u64,
|
|
||||||
);
|
|
||||||
*offset = next_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_shadows(
|
fn draw_shadows(
|
||||||
&mut self,
|
&mut self,
|
||||||
shadows: &[Shadow],
|
shadows: &[Shadow],
|
||||||
|
@ -350,6 +312,122 @@ impl MetalRenderer {
|
||||||
*offset = next_offset;
|
*offset = next_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_quads(
|
||||||
|
&mut self,
|
||||||
|
quads: &[Quad],
|
||||||
|
offset: &mut usize,
|
||||||
|
viewport_size: Size<DevicePixels>,
|
||||||
|
command_encoder: &metal::RenderCommandEncoderRef,
|
||||||
|
) {
|
||||||
|
if quads.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
align_offset(offset);
|
||||||
|
|
||||||
|
command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
QuadInputIndex::Vertices as u64,
|
||||||
|
Some(&self.unit_vertices),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
QuadInputIndex::Quads as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
command_encoder.set_fragment_buffer(
|
||||||
|
QuadInputIndex::Quads as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
command_encoder.set_vertex_bytes(
|
||||||
|
QuadInputIndex::ViewportSize as u64,
|
||||||
|
mem::size_of_val(&viewport_size) as u64,
|
||||||
|
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||||
|
);
|
||||||
|
|
||||||
|
let quad_bytes_len = mem::size_of::<Quad>() * quads.len();
|
||||||
|
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_offset = *offset + quad_bytes_len;
|
||||||
|
assert!(
|
||||||
|
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||||
|
"instance buffer exhausted"
|
||||||
|
);
|
||||||
|
|
||||||
|
command_encoder.draw_primitives_instanced(
|
||||||
|
metal::MTLPrimitiveType::Triangle,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
quads.len() as u64,
|
||||||
|
);
|
||||||
|
*offset = next_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_underlines(
|
||||||
|
&mut self,
|
||||||
|
underlines: &[Underline],
|
||||||
|
offset: &mut usize,
|
||||||
|
viewport_size: Size<DevicePixels>,
|
||||||
|
command_encoder: &metal::RenderCommandEncoderRef,
|
||||||
|
) {
|
||||||
|
if underlines.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
align_offset(offset);
|
||||||
|
|
||||||
|
command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
UnderlineInputIndex::Vertices as u64,
|
||||||
|
Some(&self.unit_vertices),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
command_encoder.set_vertex_buffer(
|
||||||
|
UnderlineInputIndex::Underlines as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
command_encoder.set_fragment_buffer(
|
||||||
|
UnderlineInputIndex::Underlines as u64,
|
||||||
|
Some(&self.instances),
|
||||||
|
*offset as u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
command_encoder.set_vertex_bytes(
|
||||||
|
UnderlineInputIndex::ViewportSize as u64,
|
||||||
|
mem::size_of_val(&viewport_size) as u64,
|
||||||
|
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||||
|
);
|
||||||
|
|
||||||
|
let quad_bytes_len = mem::size_of::<Underline>() * underlines.len();
|
||||||
|
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
underlines.as_ptr() as *const u8,
|
||||||
|
buffer_contents,
|
||||||
|
quad_bytes_len,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_offset = *offset + quad_bytes_len;
|
||||||
|
assert!(
|
||||||
|
next_offset <= INSTANCE_BUFFER_SIZE,
|
||||||
|
"instance buffer exhausted"
|
||||||
|
);
|
||||||
|
|
||||||
|
command_encoder.draw_primitives_instanced(
|
||||||
|
metal::MTLPrimitiveType::Triangle,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
underlines.len() as u64,
|
||||||
|
);
|
||||||
|
*offset = next_offset;
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_monochrome_sprites(
|
fn draw_monochrome_sprites(
|
||||||
&mut self,
|
&mut self,
|
||||||
texture_id: AtlasTextureId,
|
texture_id: AtlasTextureId,
|
||||||
|
@ -533,6 +611,13 @@ fn align_offset(offset: &mut usize) {
|
||||||
*offset = ((*offset + 255) / 256) * 256;
|
*offset = ((*offset + 255) / 256) * 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
enum ShadowInputIndex {
|
||||||
|
Vertices = 0,
|
||||||
|
Shadows = 1,
|
||||||
|
ViewportSize = 2,
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
enum QuadInputIndex {
|
enum QuadInputIndex {
|
||||||
Vertices = 0,
|
Vertices = 0,
|
||||||
|
@ -541,9 +626,9 @@ enum QuadInputIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
enum ShadowInputIndex {
|
enum UnderlineInputIndex {
|
||||||
Vertices = 0,
|
Vertices = 0,
|
||||||
Shadows = 1,
|
Underlines = 1,
|
||||||
ViewportSize = 2,
|
ViewportSize = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,53 @@ fragment float4 shadow_fragment(ShadowVertexOutput input [[stage_in]],
|
||||||
return input.color * float4(1., 1., 1., alpha);
|
return input.color * float4(1., 1., 1., alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UnderlineVertexOutput {
|
||||||
|
float4 position [[position]];
|
||||||
|
float4 color [[flat]];
|
||||||
|
uint underline_id [[flat]];
|
||||||
|
};
|
||||||
|
|
||||||
|
vertex UnderlineVertexOutput underline_vertex(
|
||||||
|
uint unit_vertex_id [[vertex_id]], uint underline_id [[instance_id]],
|
||||||
|
constant float2 *unit_vertices [[buffer(UnderlineInputIndex_Vertices)]],
|
||||||
|
constant Underline *underlines [[buffer(UnderlineInputIndex_Underlines)]],
|
||||||
|
constant Size_DevicePixels *viewport_size
|
||||||
|
[[buffer(ShadowInputIndex_ViewportSize)]]) {
|
||||||
|
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||||
|
Underline underline = underlines[underline_id];
|
||||||
|
float4 device_position =
|
||||||
|
to_device_position(unit_vertex, underline.bounds,
|
||||||
|
underline.content_mask.bounds, viewport_size);
|
||||||
|
float4 color = hsla_to_rgba(underline.color);
|
||||||
|
return UnderlineVertexOutput{device_position, color, underline_id};
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment float4 underline_fragment(UnderlineVertexOutput input [[stage_in]],
|
||||||
|
constant Underline *underlines
|
||||||
|
[[buffer(UnderlineInputIndex_Underlines)]]) {
|
||||||
|
Underline underline = underlines[input.underline_id];
|
||||||
|
if (underline.wavy) {
|
||||||
|
float half_thickness = underline.thickness * 0.5;
|
||||||
|
float2 origin =
|
||||||
|
float2(underline.bounds.origin.x, underline.bounds.origin.y);
|
||||||
|
float2 st = ((input.position.xy - origin) / underline.bounds.size.height) -
|
||||||
|
float2(0., 0.5);
|
||||||
|
float frequency = (M_PI_F * (3. * underline.thickness)) / 8.;
|
||||||
|
float amplitude = 1. / (2. * underline.thickness);
|
||||||
|
float sine = sin(st.x * frequency) * amplitude;
|
||||||
|
float dSine = cos(st.x * frequency) * amplitude * frequency;
|
||||||
|
float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
|
||||||
|
float distance_in_pixels = distance * underline.bounds.size.height;
|
||||||
|
float distance_from_top_border = distance_in_pixels - half_thickness;
|
||||||
|
float distance_from_bottom_border = distance_in_pixels + half_thickness;
|
||||||
|
float alpha = saturate(
|
||||||
|
0.5 - max(-distance_from_bottom_border, distance_from_top_border));
|
||||||
|
return input.color * float4(1., 1., 1., alpha);
|
||||||
|
} else {
|
||||||
|
return input.color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct MonochromeSpriteVertexOutput {
|
struct MonochromeSpriteVertexOutput {
|
||||||
float4 position [[position]];
|
float4 position [[position]];
|
||||||
float2 tile_position;
|
float2 tile_position;
|
||||||
|
@ -211,8 +258,8 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
|
||||||
|
|
||||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||||
MonochromeSprite sprite = sprites[sprite_id];
|
MonochromeSprite sprite = sprites[sprite_id];
|
||||||
// Don't apply content mask at the vertex level because we don't have time to
|
// Don't apply content mask at the vertex level because we don't have time
|
||||||
// make sampling from the texture match the mask.
|
// to make sampling from the texture match the mask.
|
||||||
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
||||||
sprite.bounds, viewport_size);
|
sprite.bounds, viewport_size);
|
||||||
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
||||||
|
@ -254,8 +301,8 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
|
||||||
|
|
||||||
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
float2 unit_vertex = unit_vertices[unit_vertex_id];
|
||||||
PolychromeSprite sprite = sprites[sprite_id];
|
PolychromeSprite sprite = sprites[sprite_id];
|
||||||
// Don't apply content mask at the vertex level because we don't have time to
|
// Don't apply content mask at the vertex level because we don't have time
|
||||||
// make sampling from the texture match the mask.
|
// to make sampling from the texture match the mask.
|
||||||
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
|
||||||
sprite.bounds, viewport_size);
|
sprite.bounds, viewport_size);
|
||||||
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
|
||||||
|
|
|
@ -17,8 +17,9 @@ pub type DrawOrder = u32;
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub(crate) scale_factor: f32,
|
pub(crate) scale_factor: f32,
|
||||||
pub(crate) layers: BTreeMap<StackingOrder, LayerId>,
|
pub(crate) layers: BTreeMap<StackingOrder, LayerId>,
|
||||||
pub quads: Vec<Quad>,
|
|
||||||
pub shadows: Vec<Shadow>,
|
pub shadows: Vec<Shadow>,
|
||||||
|
pub quads: Vec<Quad>,
|
||||||
|
pub underlines: Vec<Underline>,
|
||||||
pub monochrome_sprites: Vec<MonochromeSprite>,
|
pub monochrome_sprites: Vec<MonochromeSprite>,
|
||||||
pub polychrome_sprites: Vec<PolychromeSprite>,
|
pub polychrome_sprites: Vec<PolychromeSprite>,
|
||||||
}
|
}
|
||||||
|
@ -28,8 +29,9 @@ impl Scene {
|
||||||
Scene {
|
Scene {
|
||||||
scale_factor,
|
scale_factor,
|
||||||
layers: BTreeMap::new(),
|
layers: BTreeMap::new(),
|
||||||
quads: Vec::new(),
|
|
||||||
shadows: Vec::new(),
|
shadows: Vec::new(),
|
||||||
|
quads: Vec::new(),
|
||||||
|
underlines: Vec::new(),
|
||||||
monochrome_sprites: Vec::new(),
|
monochrome_sprites: Vec::new(),
|
||||||
polychrome_sprites: Vec::new(),
|
polychrome_sprites: Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -39,8 +41,9 @@ impl Scene {
|
||||||
Scene {
|
Scene {
|
||||||
scale_factor: self.scale_factor,
|
scale_factor: self.scale_factor,
|
||||||
layers: mem::take(&mut self.layers),
|
layers: mem::take(&mut self.layers),
|
||||||
quads: mem::take(&mut self.quads),
|
|
||||||
shadows: mem::take(&mut self.shadows),
|
shadows: mem::take(&mut self.shadows),
|
||||||
|
quads: mem::take(&mut self.quads),
|
||||||
|
underlines: mem::take(&mut self.underlines),
|
||||||
monochrome_sprites: mem::take(&mut self.monochrome_sprites),
|
monochrome_sprites: mem::take(&mut self.monochrome_sprites),
|
||||||
polychrome_sprites: mem::take(&mut self.polychrome_sprites),
|
polychrome_sprites: mem::take(&mut self.polychrome_sprites),
|
||||||
}
|
}
|
||||||
|
@ -51,13 +54,17 @@ impl Scene {
|
||||||
let layer_id = *self.layers.entry(layer_id).or_insert(next_id);
|
let layer_id = *self.layers.entry(layer_id).or_insert(next_id);
|
||||||
let primitive = primitive.into();
|
let primitive = primitive.into();
|
||||||
match primitive {
|
match primitive {
|
||||||
|
Primitive::Shadow(mut shadow) => {
|
||||||
|
shadow.order = layer_id;
|
||||||
|
self.shadows.push(shadow);
|
||||||
|
}
|
||||||
Primitive::Quad(mut quad) => {
|
Primitive::Quad(mut quad) => {
|
||||||
quad.order = layer_id;
|
quad.order = layer_id;
|
||||||
self.quads.push(quad);
|
self.quads.push(quad);
|
||||||
}
|
}
|
||||||
Primitive::Shadow(mut shadow) => {
|
Primitive::Underline(mut underline) => {
|
||||||
shadow.order = layer_id;
|
underline.order = layer_id;
|
||||||
self.shadows.push(shadow);
|
self.underlines.push(underline);
|
||||||
}
|
}
|
||||||
Primitive::MonochromeSprite(mut sprite) => {
|
Primitive::MonochromeSprite(mut sprite) => {
|
||||||
sprite.order = layer_id;
|
sprite.order = layer_id;
|
||||||
|
@ -78,15 +85,26 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all primitives to the BSP splitter to determine draw order
|
// Add all primitives to the BSP splitter to determine draw order
|
||||||
|
// todo!("reuse the same splitter")
|
||||||
let mut splitter = BspSplitter::new();
|
let mut splitter = BspSplitter::new();
|
||||||
|
|
||||||
|
for (ix, shadow) in self.shadows.iter().enumerate() {
|
||||||
|
let z = layer_z_values[shadow.order as LayerId as usize];
|
||||||
|
splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
|
||||||
|
}
|
||||||
|
|
||||||
for (ix, quad) in self.quads.iter().enumerate() {
|
for (ix, quad) in self.quads.iter().enumerate() {
|
||||||
let z = layer_z_values[quad.order as LayerId as usize];
|
let z = layer_z_values[quad.order as LayerId as usize];
|
||||||
splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
|
splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ix, shadow) in self.shadows.iter().enumerate() {
|
for (ix, underline) in self.underlines.iter().enumerate() {
|
||||||
let z = layer_z_values[shadow.order as LayerId as usize];
|
let z = layer_z_values[underline.order as LayerId as usize];
|
||||||
splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix)));
|
splitter.add(
|
||||||
|
underline
|
||||||
|
.bounds
|
||||||
|
.to_bsp_polygon(z, (PrimitiveKind::Underline, ix)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() {
|
for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() {
|
||||||
|
@ -111,8 +129,11 @@ impl Scene {
|
||||||
// We need primitives to be repr(C), hence the weird reuse of the order field for two different types.
|
// We need primitives to be repr(C), hence the weird reuse of the order field for two different types.
|
||||||
for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() {
|
for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() {
|
||||||
match polygon.anchor {
|
match polygon.anchor {
|
||||||
(PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
|
|
||||||
(PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder,
|
(PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder,
|
||||||
|
(PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder,
|
||||||
|
(PrimitiveKind::Underline, ix) => {
|
||||||
|
self.underlines[ix].order = draw_order as DrawOrder
|
||||||
|
}
|
||||||
(PrimitiveKind::MonochromeSprite, ix) => {
|
(PrimitiveKind::MonochromeSprite, ix) => {
|
||||||
self.monochrome_sprites[ix].order = draw_order as DrawOrder
|
self.monochrome_sprites[ix].order = draw_order as DrawOrder
|
||||||
}
|
}
|
||||||
|
@ -123,18 +144,22 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the primitives
|
// Sort the primitives
|
||||||
self.quads.sort_unstable();
|
|
||||||
self.shadows.sort_unstable();
|
self.shadows.sort_unstable();
|
||||||
|
self.quads.sort_unstable();
|
||||||
|
self.underlines.sort_unstable();
|
||||||
self.monochrome_sprites.sort_unstable();
|
self.monochrome_sprites.sort_unstable();
|
||||||
self.polychrome_sprites.sort_unstable();
|
self.polychrome_sprites.sort_unstable();
|
||||||
|
|
||||||
BatchIterator {
|
BatchIterator {
|
||||||
quads: &self.quads,
|
|
||||||
quads_start: 0,
|
|
||||||
quads_iter: self.quads.iter().peekable(),
|
|
||||||
shadows: &self.shadows,
|
shadows: &self.shadows,
|
||||||
shadows_start: 0,
|
shadows_start: 0,
|
||||||
shadows_iter: self.shadows.iter().peekable(),
|
shadows_iter: self.shadows.iter().peekable(),
|
||||||
|
quads: &self.quads,
|
||||||
|
quads_start: 0,
|
||||||
|
quads_iter: self.quads.iter().peekable(),
|
||||||
|
underlines: &self.underlines,
|
||||||
|
underlines_start: 0,
|
||||||
|
underlines_iter: self.underlines.iter().peekable(),
|
||||||
monochrome_sprites: &self.monochrome_sprites,
|
monochrome_sprites: &self.monochrome_sprites,
|
||||||
monochrome_sprites_start: 0,
|
monochrome_sprites_start: 0,
|
||||||
monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
|
monochrome_sprites_iter: self.monochrome_sprites.iter().peekable(),
|
||||||
|
@ -152,6 +177,9 @@ struct BatchIterator<'a> {
|
||||||
shadows: &'a [Shadow],
|
shadows: &'a [Shadow],
|
||||||
shadows_start: usize,
|
shadows_start: usize,
|
||||||
shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
|
shadows_iter: Peekable<slice::Iter<'a, Shadow>>,
|
||||||
|
underlines: &'a [Underline],
|
||||||
|
underlines_start: usize,
|
||||||
|
underlines_iter: Peekable<slice::Iter<'a, Underline>>,
|
||||||
monochrome_sprites: &'a [MonochromeSprite],
|
monochrome_sprites: &'a [MonochromeSprite],
|
||||||
monochrome_sprites_start: usize,
|
monochrome_sprites_start: usize,
|
||||||
monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
|
monochrome_sprites_iter: Peekable<slice::Iter<'a, MonochromeSprite>>,
|
||||||
|
@ -165,11 +193,15 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let mut orders_and_kinds = [
|
let mut orders_and_kinds = [
|
||||||
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
|
|
||||||
(
|
(
|
||||||
self.shadows_iter.peek().map(|s| s.order),
|
self.shadows_iter.peek().map(|s| s.order),
|
||||||
PrimitiveKind::Shadow,
|
PrimitiveKind::Shadow,
|
||||||
),
|
),
|
||||||
|
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
|
||||||
|
(
|
||||||
|
self.underlines_iter.peek().map(|u| u.order),
|
||||||
|
PrimitiveKind::Underline,
|
||||||
|
),
|
||||||
(
|
(
|
||||||
self.monochrome_sprites_iter.peek().map(|s| s.order),
|
self.monochrome_sprites_iter.peek().map(|s| s.order),
|
||||||
PrimitiveKind::MonochromeSprite,
|
PrimitiveKind::MonochromeSprite,
|
||||||
|
@ -190,19 +222,6 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match batch_kind {
|
match batch_kind {
|
||||||
PrimitiveKind::Quad => {
|
|
||||||
let quads_start = self.quads_start;
|
|
||||||
let mut quads_end = quads_start;
|
|
||||||
while self
|
|
||||||
.quads_iter
|
|
||||||
.next_if(|quad| quad.order <= max_order)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
quads_end += 1;
|
|
||||||
}
|
|
||||||
self.quads_start = quads_end;
|
|
||||||
Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
|
|
||||||
}
|
|
||||||
PrimitiveKind::Shadow => {
|
PrimitiveKind::Shadow => {
|
||||||
let shadows_start = self.shadows_start;
|
let shadows_start = self.shadows_start;
|
||||||
let mut shadows_end = shadows_start;
|
let mut shadows_end = shadows_start;
|
||||||
|
@ -218,6 +237,34 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||||
&self.shadows[shadows_start..shadows_end],
|
&self.shadows[shadows_start..shadows_end],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
PrimitiveKind::Quad => {
|
||||||
|
let quads_start = self.quads_start;
|
||||||
|
let mut quads_end = quads_start;
|
||||||
|
while self
|
||||||
|
.quads_iter
|
||||||
|
.next_if(|quad| quad.order <= max_order)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
quads_end += 1;
|
||||||
|
}
|
||||||
|
self.quads_start = quads_end;
|
||||||
|
Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end]))
|
||||||
|
}
|
||||||
|
PrimitiveKind::Underline => {
|
||||||
|
let underlines_start = self.underlines_start;
|
||||||
|
let mut underlines_end = underlines_start;
|
||||||
|
while self
|
||||||
|
.underlines_iter
|
||||||
|
.next_if(|underline| underline.order <= max_order)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
underlines_end += 1;
|
||||||
|
}
|
||||||
|
self.underlines_start = underlines_end;
|
||||||
|
Some(PrimitiveBatch::Underlines(
|
||||||
|
&self.underlines[underlines_start..underlines_end],
|
||||||
|
))
|
||||||
|
}
|
||||||
PrimitiveKind::MonochromeSprite => {
|
PrimitiveKind::MonochromeSprite => {
|
||||||
let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
|
let texture_id = self.monochrome_sprites_iter.peek().unwrap().tile.texture_id;
|
||||||
let sprites_start = self.monochrome_sprites_start;
|
let sprites_start = self.monochrome_sprites_start;
|
||||||
|
@ -265,22 +312,25 @@ pub enum PrimitiveKind {
|
||||||
Shadow,
|
Shadow,
|
||||||
#[default]
|
#[default]
|
||||||
Quad,
|
Quad,
|
||||||
|
Underline,
|
||||||
MonochromeSprite,
|
MonochromeSprite,
|
||||||
PolychromeSprite,
|
PolychromeSprite,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Primitive {
|
pub enum Primitive {
|
||||||
Quad(Quad),
|
|
||||||
Shadow(Shadow),
|
Shadow(Shadow),
|
||||||
|
Quad(Quad),
|
||||||
|
Underline(Underline),
|
||||||
MonochromeSprite(MonochromeSprite),
|
MonochromeSprite(MonochromeSprite),
|
||||||
PolychromeSprite(PolychromeSprite),
|
PolychromeSprite(PolychromeSprite),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum PrimitiveBatch<'a> {
|
pub(crate) enum PrimitiveBatch<'a> {
|
||||||
Quads(&'a [Quad]),
|
|
||||||
Shadows(&'a [Shadow]),
|
Shadows(&'a [Shadow]),
|
||||||
|
Quads(&'a [Quad]),
|
||||||
|
Underlines(&'a [Underline]),
|
||||||
MonochromeSprites {
|
MonochromeSprites {
|
||||||
texture_id: AtlasTextureId,
|
texture_id: AtlasTextureId,
|
||||||
sprites: &'a [MonochromeSprite],
|
sprites: &'a [MonochromeSprite],
|
||||||
|
@ -321,6 +371,35 @@ impl From<Quad> for Primitive {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Underline {
|
||||||
|
pub order: u32,
|
||||||
|
pub bounds: Bounds<ScaledPixels>,
|
||||||
|
pub content_mask: ScaledContentMask,
|
||||||
|
pub thickness: ScaledPixels,
|
||||||
|
pub color: Hsla,
|
||||||
|
pub wavy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Underline {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.order.cmp(&other.order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Underline {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Underline> for Primitive {
|
||||||
|
fn from(underline: Underline) -> Self {
|
||||||
|
Primitive::Underline(underline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Shadow {
|
pub struct Shadow {
|
||||||
|
|
|
@ -344,7 +344,7 @@ impl Default for Style {
|
||||||
pub struct UnderlineStyle {
|
pub struct UnderlineStyle {
|
||||||
pub thickness: Pixels,
|
pub thickness: Pixels,
|
||||||
pub color: Option<Hsla>,
|
pub color: Option<Hsla>,
|
||||||
pub squiggly: bool,
|
pub wavy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -416,6 +416,96 @@ pub trait StyleHelpers: Styled<Style = Style> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_decoration_none(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.text_style()
|
||||||
|
.get_or_insert_with(Default::default)
|
||||||
|
.underline = None;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_color(mut self, color: impl Into<Hsla>) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.color = Some(color.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_solid(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.wavy = false;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_wavy(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.wavy = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_0(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.thickness = px(0.);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_1(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.thickness = px(1.);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_2(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.thickness = px(2.);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_4(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.thickness = px(4.);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_decoration_8(mut self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let style = self.text_style().get_or_insert_with(Default::default);
|
||||||
|
let underline = style.underline.get_or_insert_with(Default::default);
|
||||||
|
underline.thickness = px(8.);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn font(mut self, family_name: impl Into<SharedString>) -> Self
|
fn font(mut self, family_name: impl Into<SharedString>) -> Self
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
|
|
|
@ -134,9 +134,11 @@ impl Line {
|
||||||
origin.y + baseline_offset.y + (self.layout.descent * 0.618),
|
origin.y + baseline_offset.y + (self.layout.descent * 0.618),
|
||||||
),
|
),
|
||||||
UnderlineStyle {
|
UnderlineStyle {
|
||||||
color: style_run.underline.color,
|
color: Some(
|
||||||
|
style_run.underline.color.unwrap_or(style_run.color),
|
||||||
|
),
|
||||||
thickness: style_run.underline.thickness,
|
thickness: style_run.underline.thickness,
|
||||||
squiggly: style_run.underline.squiggly,
|
wavy: style_run.underline.wavy,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -153,8 +155,12 @@ impl Line {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_underline_origin, _underline_style)) = finished_underline {
|
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||||
todo!()
|
cx.paint_underline(
|
||||||
|
underline_origin,
|
||||||
|
glyph_origin.x - underline_origin.x,
|
||||||
|
&underline_style,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if glyph.is_emoji {
|
if glyph.is_emoji {
|
||||||
|
@ -171,15 +177,13 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_underline_start, _underline_style)) = underline.take() {
|
if let Some((underline_start, underline_style)) = underline.take() {
|
||||||
let _line_end_x = origin.x + self.layout.width;
|
let line_end_x = origin.x + self.layout.width;
|
||||||
// cx.scene().push_underline(Underline {
|
cx.paint_underline(
|
||||||
// origin: underline_start,
|
underline_start,
|
||||||
// width: line_end_x - underline_start.x,
|
line_end_x - underline_start.x,
|
||||||
// color: underline_style.color,
|
&underline_style,
|
||||||
// thickness: underline_style.thickness.into(),
|
)?;
|
||||||
// squiggly: underline_style.squiggly,
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -188,7 +192,7 @@ impl Line {
|
||||||
pub fn paint_wrapped(
|
pub fn paint_wrapped(
|
||||||
&self,
|
&self,
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
_visible_bounds: Bounds<Pixels>,
|
_visible_bounds: Bounds<Pixels>, // todo!("use clipping")
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
boundaries: &[ShapedBoundary],
|
boundaries: &[ShapedBoundary],
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -213,14 +217,12 @@ impl Line {
|
||||||
.map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
|
.map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
|
||||||
{
|
{
|
||||||
boundaries.next();
|
boundaries.next();
|
||||||
if let Some((_underline_origin, _underline_style)) = underline.take() {
|
if let Some((underline_origin, underline_style)) = underline.take() {
|
||||||
// cx.scene().push_underline(Underline {
|
cx.paint_underline(
|
||||||
// origin: underline_origin,
|
underline_origin,
|
||||||
// width: glyph_origin.x - underline_origin.x,
|
glyph_origin.x - underline_origin.x,
|
||||||
// thickness: underline_style.thickness.into(),
|
&underline_style,
|
||||||
// color: underline_style.color.unwrap(),
|
)?;
|
||||||
// squiggly: underline_style.squiggly,
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph_origin = point(origin.x, glyph_origin.y + line_height);
|
glyph_origin = point(origin.x, glyph_origin.y + line_height);
|
||||||
|
@ -249,7 +251,7 @@ impl Line {
|
||||||
style_run.underline.color.unwrap_or(style_run.color),
|
style_run.underline.color.unwrap_or(style_run.color),
|
||||||
),
|
),
|
||||||
thickness: style_run.underline.thickness,
|
thickness: style_run.underline.thickness,
|
||||||
squiggly: style_run.underline.squiggly,
|
wavy: style_run.underline.wavy,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -260,14 +262,12 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_underline_origin, _underline_style)) = finished_underline {
|
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||||
// cx.scene().push_underline(Underline {
|
cx.paint_underline(
|
||||||
// origin: underline_origin,
|
underline_origin,
|
||||||
// width: glyph_origin.x - underline_origin.x,
|
glyph_origin.x - underline_origin.x,
|
||||||
// thickness: underline_style.thickness.into(),
|
&underline_style,
|
||||||
// color: underline_style.color.unwrap(),
|
)?;
|
||||||
// squiggly: underline_style.squiggly,
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_system = cx.text_system();
|
let text_system = cx.text_system();
|
||||||
|
@ -298,15 +298,13 @@ impl Line {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_underline_origin, _underline_style)) = underline.take() {
|
if let Some((underline_origin, underline_style)) = underline.take() {
|
||||||
// let line_end_x = glyph_origin.x + self.layout.width - prev_position;
|
let line_end_x = glyph_origin.x + self.layout.width - prev_position;
|
||||||
// cx.scene().push_underline(Underline {
|
cx.paint_underline(
|
||||||
// origin: underline_origin,
|
underline_origin,
|
||||||
// width: line_end_x - underline_origin.x,
|
line_end_x - underline_origin.x,
|
||||||
// thickness: underline_style.thickness.into(),
|
&underline_style,
|
||||||
// color: underline_style.color,
|
)?;
|
||||||
// squiggly: underline_style.squiggly,
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
|
image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext,
|
||||||
BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, Element, EntityId,
|
AvailableSpace, BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect,
|
||||||
FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread, MainThreadOnly,
|
Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread,
|
||||||
MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
|
MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point,
|
||||||
RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, StackingOrder,
|
PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene,
|
||||||
Style, TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle,
|
||||||
|
WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -259,6 +260,38 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||||
self.window.current_stacking_order.clone()
|
self.window.current_stacking_order.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paint_underline(
|
||||||
|
&mut self,
|
||||||
|
origin: Point<Pixels>,
|
||||||
|
width: Pixels,
|
||||||
|
style: &UnderlineStyle,
|
||||||
|
) -> Result<()> {
|
||||||
|
let scale_factor = self.scale_factor();
|
||||||
|
let height = if style.wavy {
|
||||||
|
style.thickness * 3.
|
||||||
|
} else {
|
||||||
|
style.thickness
|
||||||
|
};
|
||||||
|
let bounds = Bounds {
|
||||||
|
origin,
|
||||||
|
size: size(width, height),
|
||||||
|
};
|
||||||
|
let content_mask = self.content_mask();
|
||||||
|
let layer_id = self.current_stacking_order();
|
||||||
|
self.window.scene.insert(
|
||||||
|
layer_id,
|
||||||
|
Underline {
|
||||||
|
order: 0,
|
||||||
|
bounds: bounds.scale(scale_factor),
|
||||||
|
content_mask: content_mask.scale(scale_factor),
|
||||||
|
thickness: style.thickness.scale(scale_factor),
|
||||||
|
color: style.color.unwrap_or_default(),
|
||||||
|
wavy: style.wavy,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn paint_glyph(
|
pub fn paint_glyph(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: Point<Pixels>,
|
origin: Point<Pixels>,
|
||||||
|
|
|
@ -160,7 +160,13 @@ impl Titlebar {
|
||||||
// .fill(theme.lowest.base.hovered.background)
|
// .fill(theme.lowest.base.hovered.background)
|
||||||
// .active()
|
// .active()
|
||||||
// .fill(theme.lowest.base.pressed.background)
|
// .fill(theme.lowest.base.pressed.background)
|
||||||
.child(div().text_sm().child("branch")),
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.text_decoration_1()
|
||||||
|
.text_decoration_wavy()
|
||||||
|
.child("branch"),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue