Skip to content

Commit

Permalink
Implement tree-style builder
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoburns committed Jul 21, 2024
1 parent a5b7214 commit 045b263
Show file tree
Hide file tree
Showing 8 changed files with 595 additions and 150 deletions.
199 changes: 199 additions & 0 deletions parley/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2021 the Parley Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Context for layout.

#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};

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_uncomitted_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)
}
}

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();

// println!("BUILD INTO ({})", text.len());
// for span in &lcx.styles {
// let stack = lcx.rcx.stack(span.style.font_stack);
// println!(
// "{:?} weight:{}, family: {:?}",
// span.range, span.style.font_weight, stack
// );
// }

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);

// dbg!(&lcx.inline_boxes);

{
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();
}
Loading

0 comments on commit 045b263

Please sign in to comment.