-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Builds on top of #84 which should be merged first to preserve history ## Changes made - ### Entire unresolved style struct - Adds an "unresolved style struct" (`TextStyle`) to `style/mod.rs` - The new `TreeBuilder` allows you to directly pass an entire style struct (rather than just "changed styles" relative to a previous node in the tree) as this is easier for integrating with Stylo which already resolves inherited styles. So I have added an "unresolved" version of the style struct to allow this API to still plug into the other parts of Parley's style resolution functionality. - Adds a corresponding`resolve_entire_style_set` method to `LayoutContext` to convert `TextStyle` into `ResolvedStyle`. - ### Moved code - Moves the `RangedBuilder` from `context.rs` to a new `builder.rs` module (as there are now two builders, justifying a separate module). - Extract most of `RangedBuilder::build_into` into a standalone `build_into_layout` function (`builder.rs`) that can be shared between the ranged and tree builders. - Moves the `RangedStyle` and `RangedProperty` types from `resolve/range.rs` to `resolve/mod.rs`. These types are shared between the `RangedBuilder` and the `TreeBuilder`. - ### Tree builder - Adds a `TreeBuilder` (also to `builder.rs`). This mostly delegates to `TreeStyleBuilder` - Add a `TreeStyleBuilder` (`resolve/tree.rs`). This is the vast majority of the new code - The `TreeStyleBuilder` implements HTML-style whitespace collapsing (opt-in). This probably ought to become a style rather than being a flag on the style builder. - Updated swash example to optionally use the tree builder (depending on command line opt)
- Loading branch information
Showing
11 changed files
with
653 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Copyright 2021 the Parley Authors | ||
// SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
||
//! Context for layout. | ||
use super::context::*; | ||
use super::style::*; | ||
use super::FontContext; | ||
|
||
#[cfg(feature = "std")] | ||
use super::layout::Layout; | ||
|
||
use core::ops::RangeBounds; | ||
|
||
use crate::inline_box::InlineBox; | ||
|
||
/// Builder for constructing a text layout with ranged attributes. | ||
pub struct RangedBuilder<'a, B: Brush> { | ||
pub(crate) scale: f32, | ||
pub(crate) lcx: &'a mut LayoutContext<B>, | ||
pub(crate) fcx: &'a mut FontContext, | ||
} | ||
|
||
impl<'a, B: Brush> RangedBuilder<'a, B> { | ||
pub fn push_default(&mut self, property: &StyleProperty<B>) { | ||
let resolved = self | ||
.lcx | ||
.rcx | ||
.resolve_property(self.fcx, property, self.scale); | ||
self.lcx.ranged_style_builder.push_default(resolved); | ||
} | ||
|
||
pub fn push(&mut self, property: &StyleProperty<B>, range: impl RangeBounds<usize>) { | ||
let resolved = self | ||
.lcx | ||
.rcx | ||
.resolve_property(self.fcx, property, self.scale); | ||
self.lcx.ranged_style_builder.push(resolved, range); | ||
} | ||
|
||
pub fn push_inline_box(&mut self, inline_box: InlineBox) { | ||
self.lcx.inline_boxes.push(inline_box); | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub fn build_into(&mut self, layout: &mut Layout<B>, text: impl AsRef<str>) { | ||
// Apply RangedStyleBuilder styles to LayoutContext | ||
self.lcx.ranged_style_builder.finish(&mut self.lcx.styles); | ||
|
||
// Call generic layout builder method | ||
build_into_layout(layout, self.scale, text.as_ref(), self.lcx, self.fcx); | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub fn build(&mut self, text: impl AsRef<str>) -> Layout<B> { | ||
let mut layout = Layout::default(); | ||
self.build_into(&mut layout, text); | ||
layout | ||
} | ||
} | ||
|
||
/// Builder for constructing a text layout with a tree of attributes. | ||
pub struct TreeBuilder<'a, B: Brush> { | ||
pub(crate) scale: f32, | ||
pub(crate) lcx: &'a mut LayoutContext<B>, | ||
pub(crate) fcx: &'a mut FontContext, | ||
} | ||
|
||
impl<'a, B: Brush> TreeBuilder<'a, B> { | ||
pub fn push_style_span(&mut self, style: TextStyle<B>) { | ||
let resolved = self | ||
.lcx | ||
.rcx | ||
.resolve_entire_style_set(self.fcx, &style, self.scale); | ||
self.lcx.tree_style_builder.push_style_span(resolved); | ||
} | ||
|
||
pub fn push_style_modification_span<'s, 'iter>( | ||
&mut self, | ||
properties: impl IntoIterator<Item = &'iter StyleProperty<'s, B>>, | ||
) where | ||
's: 'iter, | ||
B: 'iter, | ||
{ | ||
self.lcx.tree_style_builder.push_style_modification_span( | ||
properties | ||
.into_iter() | ||
.map(|p| self.lcx.rcx.resolve_property(self.fcx, p, self.scale)), | ||
); | ||
} | ||
|
||
pub fn pop_style_span(&mut self) { | ||
self.lcx.tree_style_builder.pop_style_span(); | ||
} | ||
|
||
pub fn push_text(&mut self, text: &str) { | ||
self.lcx.tree_style_builder.push_text(text); | ||
} | ||
|
||
pub fn push_inline_box(&mut self, mut inline_box: InlineBox) { | ||
self.lcx.tree_style_builder.push_uncommitted_text(false); | ||
// TODO: arrange type better here to factor out the index | ||
inline_box.index = self.lcx.tree_style_builder.current_text_len(); | ||
self.lcx.inline_boxes.push(inline_box); | ||
} | ||
|
||
pub fn set_white_space_mode(&mut self, white_space_collapse: WhiteSpaceCollapse) { | ||
self.lcx | ||
.tree_style_builder | ||
.set_white_space_mode(white_space_collapse); | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub fn build_into(&mut self, layout: &mut Layout<B>) -> String { | ||
// Apply TreeStyleBuilder styles to LayoutContext | ||
let text = self.lcx.tree_style_builder.finish(&mut self.lcx.styles); | ||
|
||
self.lcx.analyze_text(&text); | ||
|
||
// Call generic layout builder method | ||
build_into_layout(layout, self.scale, &text, self.lcx, self.fcx); | ||
|
||
text | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
pub fn build(&mut self) -> (Layout<B>, String) { | ||
let mut layout = Layout::default(); | ||
let text = self.build_into(&mut layout); | ||
(layout, text) | ||
} | ||
} | ||
|
||
#[cfg(feature = "std")] | ||
fn build_into_layout<B: Brush>( | ||
layout: &mut Layout<B>, | ||
scale: f32, | ||
text: &str, | ||
lcx: &mut LayoutContext<B>, | ||
fcx: &mut FontContext, | ||
) { | ||
layout.data.clear(); | ||
layout.data.scale = scale; | ||
layout.data.has_bidi = !lcx.bidi.levels().is_empty(); | ||
layout.data.base_level = lcx.bidi.base_level(); | ||
layout.data.text_len = text.len(); | ||
|
||
let mut char_index = 0; | ||
for (i, style) in lcx.styles.iter().enumerate() { | ||
for _ in text[style.range.clone()].chars() { | ||
lcx.info[char_index].1 = i as u16; | ||
char_index += 1; | ||
} | ||
} | ||
|
||
// Copy the visual styles into the layout | ||
layout | ||
.data | ||
.styles | ||
.extend(lcx.styles.iter().map(|s| s.style.as_layout_style())); | ||
|
||
// Sort the inline boxes as subsequent code assumes that they are in text index order. | ||
// Note: It's important that this is a stable sort to allow users to control the order of contiguous inline boxes | ||
lcx.inline_boxes.sort_by_key(|b| b.index); | ||
|
||
{ | ||
let query = fcx.collection.query(&mut fcx.source_cache); | ||
super::shape::shape_text( | ||
&lcx.rcx, | ||
query, | ||
&lcx.styles, | ||
&lcx.inline_boxes, | ||
&lcx.info, | ||
lcx.bidi.levels(), | ||
&mut lcx.scx, | ||
text, | ||
layout, | ||
); | ||
} | ||
|
||
// Move inline boxes into the layout | ||
layout.data.inline_boxes.clear(); | ||
core::mem::swap(&mut layout.data.inline_boxes, &mut lcx.inline_boxes); | ||
|
||
layout.data.finish(); | ||
} |
Oops, something went wrong.