Adjust `LayoutContext` and `LayoutBox` to construct a `Vec` of boxes rather than an intrusive, garbage-collected linked list.
This commit is contained in:
parent
d281452fe1
commit
d7a257f93f
|
@ -89,7 +89,7 @@ pub struct EditTextData<'gc> {
|
||||||
autosize: AutoSizeMode,
|
autosize: AutoSizeMode,
|
||||||
|
|
||||||
/// The calculated layout box.
|
/// The calculated layout box.
|
||||||
layout: Option<GcCell<'gc, LayoutBox<'gc>>>,
|
layout: Vec<LayoutBox<'gc>>,
|
||||||
|
|
||||||
/// The intrinsic bounds of the laid-out text.
|
/// The intrinsic bounds of the laid-out text.
|
||||||
intrinsic_bounds: BoxBounds<Twips>,
|
intrinsic_bounds: BoxBounds<Twips>,
|
||||||
|
@ -523,12 +523,8 @@ impl<'gc> EditText<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a layout box, plus it's children.
|
/// Render a layout box, plus it's children.
|
||||||
fn render_layout_box(
|
fn render_layout_box(self, context: &mut RenderContext<'_, 'gc>, lbox: &LayoutBox<'gc>) {
|
||||||
self,
|
let box_transform: Transform = lbox.bounds().origin().into();
|
||||||
context: &mut RenderContext<'_, 'gc>,
|
|
||||||
lbox: GcCell<'gc, LayoutBox<'gc>>,
|
|
||||||
) {
|
|
||||||
let box_transform: Transform = lbox.read().bounds().origin().into();
|
|
||||||
context.transform_stack.push(&box_transform);
|
context.transform_stack.push(&box_transform);
|
||||||
|
|
||||||
let edit_text = self.0.read();
|
let edit_text = self.0.read();
|
||||||
|
@ -538,7 +534,7 @@ impl<'gc> EditText<'gc> {
|
||||||
// Instead, we embed an SWF version of Noto Sans to use as the "device font", and render
|
// Instead, we embed an SWF version of Noto Sans to use as the "device font", and render
|
||||||
// it the same as any other SWF outline text.
|
// it the same as any other SWF outline text.
|
||||||
if let Some((text, _tf, font, params, color)) =
|
if let Some((text, _tf, font, params, color)) =
|
||||||
lbox.read().as_renderable_text(edit_text.text_spans.text())
|
lbox.as_renderable_text(edit_text.text_spans.text())
|
||||||
{
|
{
|
||||||
font.evaluate(
|
font.evaluate(
|
||||||
text,
|
text,
|
||||||
|
@ -555,7 +551,7 @@ impl<'gc> EditText<'gc> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(drawing) = lbox.read().as_renderable_drawing() {
|
if let Some(drawing) = lbox.as_renderable_drawing() {
|
||||||
drawing.render(context);
|
drawing.render(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +608,10 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
.duplicate(context.gc_context, true)
|
.duplicate(context.gc_context, true)
|
||||||
.document();
|
.document();
|
||||||
|
|
||||||
text.layout = text.layout.map(|l| l.read().duplicate(context.gc_context));
|
let mut new_layout = Vec::new();
|
||||||
|
for layout_box in text.layout.iter() {
|
||||||
|
new_layout.push(layout_box.duplicate(context.gc_context));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn object(&self) -> Value<'gc> {
|
fn object(&self) -> Value<'gc> {
|
||||||
|
@ -710,12 +709,8 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
|
|
||||||
self.0.read().drawing.render(context);
|
self.0.read().drawing.render(context);
|
||||||
|
|
||||||
let mut ptr = self.0.read().layout;
|
for layout_box in self.0.read().layout.iter() {
|
||||||
|
self.render_layout_box(context, layout_box);
|
||||||
while let Some(lbox) = ptr {
|
|
||||||
self.render_layout_box(context, lbox);
|
|
||||||
|
|
||||||
ptr = lbox.read().next_sibling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.transform_stack.pop();
|
context.transform_stack.pop();
|
||||||
|
|
|
@ -53,11 +53,8 @@ pub struct LayoutContext<'a, 'gc> {
|
||||||
/// The highest font size observed within the current line.
|
/// The highest font size observed within the current line.
|
||||||
max_font_size: Twips,
|
max_font_size: Twips,
|
||||||
|
|
||||||
/// The start of the current chain of layout boxes.
|
/// The growing list of layout boxes to return when layout has finished.
|
||||||
first_box: Option<GcCell<'gc, LayoutBox<'gc>>>,
|
boxes: Vec<LayoutBox<'gc>>,
|
||||||
|
|
||||||
/// The end of the current chain of layout boxes.
|
|
||||||
last_box: Option<GcCell<'gc, LayoutBox<'gc>>>,
|
|
||||||
|
|
||||||
/// The exterior bounds of all laid-out text, including left and right
|
/// The exterior bounds of all laid-out text, including left and right
|
||||||
/// margins.
|
/// margins.
|
||||||
|
@ -78,8 +75,11 @@ pub struct LayoutContext<'a, 'gc> {
|
||||||
/// the singular line if we have yet to process a newline.
|
/// the singular line if we have yet to process a newline.
|
||||||
has_line_break: bool,
|
has_line_break: bool,
|
||||||
|
|
||||||
/// All layout boxes in the current line being laid out.
|
/// The first box within the current line.
|
||||||
current_line: Option<GcCell<'gc, LayoutBox<'gc>>>,
|
///
|
||||||
|
/// If equal to the length of the array, then no layout boxes currenly
|
||||||
|
/// exist for this line.
|
||||||
|
current_line: usize,
|
||||||
|
|
||||||
/// The right margin of the first span in the current line.
|
/// The right margin of the first span in the current line.
|
||||||
current_line_span: TextSpan,
|
current_line_span: TextSpan,
|
||||||
|
@ -96,12 +96,11 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
font: None,
|
font: None,
|
||||||
text,
|
text,
|
||||||
max_font_size: Default::default(),
|
max_font_size: Default::default(),
|
||||||
first_box: None,
|
boxes: Vec::new(),
|
||||||
last_box: None,
|
|
||||||
exterior_bounds: None,
|
exterior_bounds: None,
|
||||||
is_first_line: true,
|
is_first_line: true,
|
||||||
has_line_break: false,
|
has_line_break: false,
|
||||||
current_line: None,
|
current_line: 0,
|
||||||
current_line_span: Default::default(),
|
current_line_span: Default::default(),
|
||||||
max_bounds,
|
max_bounds,
|
||||||
}
|
}
|
||||||
|
@ -139,11 +138,10 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
|
|
||||||
/// Construct an underline drawing for the current line of text and add it
|
/// Construct an underline drawing for the current line of text and add it
|
||||||
/// to the line.
|
/// to the line.
|
||||||
fn append_underlines(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
fn append_underlines(&mut self) {
|
||||||
let mut starting_pos: Option<Position<Twips>> = None;
|
let mut starting_pos: Option<Position<Twips>> = None;
|
||||||
let mut current_width: Option<Twips> = None;
|
let mut current_width: Option<Twips> = None;
|
||||||
let mut line_drawing = Drawing::new();
|
let mut line_drawing = Drawing::new();
|
||||||
let mut line = self.current_line;
|
|
||||||
let mut has_underline: bool = false;
|
let mut has_underline: bool = false;
|
||||||
|
|
||||||
line_drawing.set_line_style(Some(swf::LineStyle::new_v1(
|
line_drawing.set_line_style(Some(swf::LineStyle::new_v1(
|
||||||
|
@ -151,49 +149,51 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
swf::Color::from_rgb(0, 255),
|
swf::Color::from_rgb(0, 255),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
while let Some(linebox) = line {
|
if let Some(linelist) = self.boxes.get(self.current_line..) {
|
||||||
let read = linebox.read();
|
for linebox in linelist {
|
||||||
|
if linebox.is_text_box() {
|
||||||
|
if let Some((_t, tf, font, params, _color)) =
|
||||||
|
linebox.as_renderable_text(self.text)
|
||||||
|
{
|
||||||
|
let underline_baseline =
|
||||||
|
font.get_baseline_for_height(params.height()) + Twips::from_pixels(2.0);
|
||||||
|
let mut line_extended = false;
|
||||||
|
|
||||||
if read.is_text_box() {
|
if let Some(starting_pos) = starting_pos {
|
||||||
if let Some((_t, tf, font, params, _color)) = read.as_renderable_text(self.text) {
|
if tf.underline.unwrap_or(false)
|
||||||
let underline_baseline =
|
&& underline_baseline + linebox.bounds().origin().y()
|
||||||
font.get_baseline_for_height(params.height()) + Twips::from_pixels(2.0);
|
== starting_pos.y()
|
||||||
let mut line_extended = false;
|
{
|
||||||
|
//Underline is at the same baseline, extend it
|
||||||
|
current_width =
|
||||||
|
Some(linebox.bounds().extent_x() - starting_pos.x());
|
||||||
|
|
||||||
if let Some(starting_pos) = starting_pos {
|
line_extended = true;
|
||||||
if tf.underline.unwrap_or(false)
|
}
|
||||||
&& underline_baseline + read.bounds().origin().y() == starting_pos.y()
|
|
||||||
{
|
|
||||||
//Underline is at the same baseline, extend it
|
|
||||||
current_width = Some(read.bounds().extent_x() - starting_pos.x());
|
|
||||||
|
|
||||||
line_extended = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !line_extended {
|
|
||||||
//For whatever reason, we cannot extend the current underline.
|
|
||||||
//This can happen if we don't have an underline to extend, the
|
|
||||||
//underlines don't match, or this span doesn't call for one.
|
|
||||||
if let (Some(pos), Some(width)) = (starting_pos, current_width) {
|
|
||||||
draw_underline(&mut line_drawing, pos, width);
|
|
||||||
has_underline = true;
|
|
||||||
starting_pos = None;
|
|
||||||
current_width = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if tf.underline.unwrap_or(false) {
|
if !line_extended {
|
||||||
starting_pos = Some(
|
//For whatever reason, we cannot extend the current underline.
|
||||||
read.bounds().origin()
|
//This can happen if we don't have an underline to extend, the
|
||||||
+ Position::from((Twips::zero(), underline_baseline)),
|
//underlines don't match, or this span doesn't call for one.
|
||||||
);
|
if let (Some(pos), Some(width)) = (starting_pos, current_width) {
|
||||||
current_width = Some(read.bounds().width());
|
draw_underline(&mut line_drawing, pos, width);
|
||||||
|
has_underline = true;
|
||||||
|
starting_pos = None;
|
||||||
|
current_width = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tf.underline.unwrap_or(false) {
|
||||||
|
starting_pos = Some(
|
||||||
|
linebox.bounds().origin()
|
||||||
|
+ Position::from((Twips::zero(), underline_baseline)),
|
||||||
|
);
|
||||||
|
current_width = Some(linebox.bounds().width());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line = read.next_sibling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(starting_pos), Some(current_width)) = (starting_pos, current_width) {
|
if let (Some(starting_pos), Some(current_width)) = (starting_pos, current_width) {
|
||||||
|
@ -202,10 +202,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_underline {
|
if has_underline {
|
||||||
self.append_box(
|
self.append_box(LayoutBox::from_drawing(line_drawing));
|
||||||
context.gc_context,
|
|
||||||
LayoutBox::from_drawing(context.gc_context, line_drawing),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,22 +221,19 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
only_line: bool,
|
only_line: bool,
|
||||||
final_line_of_para: bool,
|
final_line_of_para: bool,
|
||||||
) {
|
) {
|
||||||
if self.current_line.is_none() {
|
if self.boxes.get_mut(self.current_line..).is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut line = self.current_line;
|
|
||||||
let mut line_bounds = None;
|
let mut line_bounds = None;
|
||||||
let mut box_count: i32 = 0;
|
let mut box_count: i32 = 0;
|
||||||
while let Some(linebox) = line {
|
for linebox in self.boxes.get_mut(self.current_line..).unwrap() {
|
||||||
let mut write = linebox.write(context.gc_context);
|
|
||||||
line = write.next_sibling();
|
|
||||||
let (text, _tf, font, params, _color) =
|
let (text, _tf, font, params, _color) =
|
||||||
write.as_renderable_text(self.text).expect("text");
|
linebox.as_renderable_text(self.text).expect("text");
|
||||||
|
|
||||||
//Flash ignores trailing spaces when aligning lines, so should we
|
//Flash ignores trailing spaces when aligning lines, so should we
|
||||||
if self.current_line_span.align != swf::TextAlign::Left {
|
if self.current_line_span.align != swf::TextAlign::Left {
|
||||||
write.bounds = write.bounds.with_size(Size::from(font.measure(
|
linebox.bounds = linebox.bounds.with_size(Size::from(font.measure(
|
||||||
text.trim_end(),
|
text.trim_end(),
|
||||||
params,
|
params,
|
||||||
false,
|
false,
|
||||||
|
@ -247,9 +241,9 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(line_bounds) = &mut line_bounds {
|
if let Some(line_bounds) = &mut line_bounds {
|
||||||
*line_bounds += write.bounds;
|
*line_bounds += linebox.bounds;
|
||||||
} else {
|
} else {
|
||||||
line_bounds = Some(write.bounds);
|
line_bounds = Some(linebox.bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
box_count += 1;
|
box_count += 1;
|
||||||
|
@ -290,36 +284,32 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
self.append_bullet(context, &self.current_line_span.clone());
|
self.append_bullet(context, &self.current_line_span.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
line = self.current_line;
|
|
||||||
box_count = 0;
|
box_count = 0;
|
||||||
while let Some(linebox) = line {
|
for linebox in self.boxes.get_mut(self.current_line..).unwrap() {
|
||||||
let mut write = linebox.write(context.gc_context);
|
|
||||||
|
|
||||||
// TODO: This attempts to keep text of multiple font sizes vertically
|
// TODO: This attempts to keep text of multiple font sizes vertically
|
||||||
// aligned correctly. It does not consider the baseline of the font,
|
// aligned correctly. It does not consider the baseline of the font,
|
||||||
// which is information we don't have yet.
|
// which is information we don't have yet.
|
||||||
let font_size_adjustment = self.max_font_size - write.bounds.height();
|
let font_size_adjustment = self.max_font_size - linebox.bounds.height();
|
||||||
|
|
||||||
if write.is_text_box() {
|
if linebox.is_text_box() {
|
||||||
write.bounds += Position::from((
|
linebox.bounds += Position::from((
|
||||||
left_adjustment + align_adjustment + (interim_adjustment * box_count),
|
left_adjustment + align_adjustment + (interim_adjustment * box_count),
|
||||||
font_size_adjustment,
|
font_size_adjustment,
|
||||||
));
|
));
|
||||||
} else if write.is_bullet() {
|
} else if linebox.is_bullet() {
|
||||||
write.bounds += Position::from((Default::default(), font_size_adjustment));
|
linebox.bounds += Position::from((Default::default(), font_size_adjustment));
|
||||||
}
|
}
|
||||||
|
|
||||||
line = write.next_sibling();
|
|
||||||
box_count += 1;
|
box_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.append_underlines(context);
|
self.append_underlines();
|
||||||
|
|
||||||
line_bounds +=
|
line_bounds +=
|
||||||
Position::from((left_adjustment + align_adjustment, Twips::from_pixels(0.0)));
|
Position::from((left_adjustment + align_adjustment, Twips::from_pixels(0.0)));
|
||||||
line_bounds += Size::from((Twips::from_pixels(0.0), font_leading_adjustment));
|
line_bounds += Size::from((Twips::from_pixels(0.0), font_leading_adjustment));
|
||||||
|
|
||||||
self.current_line = None;
|
self.current_line = self.boxes.len();
|
||||||
|
|
||||||
if let Some(eb) = &mut self.exterior_bounds {
|
if let Some(eb) = &mut self.exterior_bounds {
|
||||||
*eb += line_bounds;
|
*eb += line_bounds;
|
||||||
|
@ -392,7 +382,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
|
|
||||||
/// Enter a new span.
|
/// Enter a new span.
|
||||||
fn newspan(&mut self, first_span: &TextSpan) {
|
fn newspan(&mut self, first_span: &TextSpan) {
|
||||||
if self.current_line.is_none() {
|
if self.is_start_of_line() {
|
||||||
self.current_line_span = first_span.clone();
|
self.current_line_span = first_span.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,21 +416,13 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
///
|
///
|
||||||
/// The text given may or may not be separated into fragments, depending on
|
/// The text given may or may not be separated into fragments, depending on
|
||||||
/// what the layout calls for.
|
/// what the layout calls for.
|
||||||
fn append_text(
|
fn append_text(&mut self, text: &'a str, start: usize, end: usize, span: &TextSpan) {
|
||||||
&mut self,
|
|
||||||
mc: MutationContext<'gc, '_>,
|
|
||||||
text: &'a str,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
span: &TextSpan,
|
|
||||||
) {
|
|
||||||
if self.effective_alignment() == swf::TextAlign::Justify {
|
if self.effective_alignment() == swf::TextAlign::Justify {
|
||||||
for word in text.split(' ') {
|
for word in text.split(' ') {
|
||||||
let word_start = word.as_ptr() as usize - text.as_ptr() as usize;
|
let word_start = word.as_ptr() as usize - text.as_ptr() as usize;
|
||||||
let word_end = min(word_start + word.len() + 1, text.len());
|
let word_end = min(word_start + word.len() + 1, text.len());
|
||||||
|
|
||||||
self.append_text_fragment(
|
self.append_text_fragment(
|
||||||
mc,
|
|
||||||
text.get(word_start..word_end).unwrap(),
|
text.get(word_start..word_end).unwrap(),
|
||||||
start + word_start,
|
start + word_start,
|
||||||
start + word_end,
|
start + word_end,
|
||||||
|
@ -448,7 +430,7 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.append_text_fragment(mc, text, start, end, span);
|
self.append_text_fragment(text, start, end, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,24 +438,16 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
///
|
///
|
||||||
/// This function bypasses the text fragmentation necessary for justify to
|
/// This function bypasses the text fragmentation necessary for justify to
|
||||||
/// work and it should only be called internally.
|
/// work and it should only be called internally.
|
||||||
fn append_text_fragment(
|
fn append_text_fragment(&mut self, text: &'a str, start: usize, end: usize, span: &TextSpan) {
|
||||||
&mut self,
|
|
||||||
mc: MutationContext<'gc, '_>,
|
|
||||||
text: &'a str,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
span: &TextSpan,
|
|
||||||
) {
|
|
||||||
let params = EvalParameters::from_span(span);
|
let params = EvalParameters::from_span(span);
|
||||||
let text_size = Size::from(self.font.unwrap().measure(text, params, false));
|
let text_size = Size::from(self.font.unwrap().measure(text, params, false));
|
||||||
let text_bounds = BoxBounds::from_position_and_size(self.cursor, text_size);
|
let text_bounds = BoxBounds::from_position_and_size(self.cursor, text_size);
|
||||||
let new_text = LayoutBox::from_text(mc, start, end, self.font.unwrap(), span);
|
let mut new_text = LayoutBox::from_text(start, end, self.font.unwrap(), span);
|
||||||
let mut write = new_text.write(mc);
|
|
||||||
|
|
||||||
write.bounds = text_bounds;
|
new_text.bounds = text_bounds;
|
||||||
|
|
||||||
self.cursor += Position::from((text_size.width(), Twips::default()));
|
self.cursor += Position::from((text_size.width(), Twips::default()));
|
||||||
self.append_box(mc, new_text);
|
self.append_box(new_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append a bullet to the start of the current line.
|
/// Append a bullet to the start of the current line.
|
||||||
|
@ -497,12 +471,11 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
let params = EvalParameters::from_span(span);
|
let params = EvalParameters::from_span(span);
|
||||||
let text_size = Size::from(bullet_font.measure("\u{2022}", params, false));
|
let text_size = Size::from(bullet_font.measure("\u{2022}", params, false));
|
||||||
let text_bounds = BoxBounds::from_position_and_size(bullet_cursor, text_size);
|
let text_bounds = BoxBounds::from_position_and_size(bullet_cursor, text_size);
|
||||||
let new_bullet = LayoutBox::from_bullet(context.gc_context, bullet_font, span);
|
let mut new_bullet = LayoutBox::from_bullet(bullet_font, span);
|
||||||
let mut write = new_bullet.write(context.gc_context);
|
|
||||||
|
|
||||||
write.bounds = text_bounds;
|
new_bullet.bounds = text_bounds;
|
||||||
|
|
||||||
self.append_box(context.gc_context, new_bullet);
|
self.append_box(new_bullet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,24 +484,8 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
/// The box should have been positioned according to the current cursor
|
/// The box should have been positioned according to the current cursor
|
||||||
/// position. It will be adjusted some time later to properly position it
|
/// position. It will be adjusted some time later to properly position it
|
||||||
/// within the current layout box.
|
/// within the current layout box.
|
||||||
fn append_box(
|
fn append_box(&mut self, to_append: LayoutBox<'gc>) {
|
||||||
&mut self,
|
self.boxes.push(to_append);
|
||||||
gc_context: MutationContext<'gc, '_>,
|
|
||||||
to_append: GcCell<'gc, LayoutBox<'gc>>,
|
|
||||||
) {
|
|
||||||
if self.first_box.is_none() {
|
|
||||||
self.first_box = Some(to_append);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.current_line.is_none() {
|
|
||||||
self.current_line = Some(to_append);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(last) = self.last_box {
|
|
||||||
last.write(gc_context).next_sibling = Some(to_append);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.last_box = Some(to_append);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the left-align offset of a given line of text given the span
|
/// Calculate the left-align offset of a given line of text given the span
|
||||||
|
@ -567,17 +524,17 @@ impl<'a, 'gc> LayoutContext<'a, 'gc> {
|
||||||
fn end_layout(
|
fn end_layout(
|
||||||
mut self,
|
mut self,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> (Option<GcCell<'gc, LayoutBox<'gc>>>, BoxBounds<Twips>) {
|
) -> (Vec<LayoutBox<'gc>>, BoxBounds<Twips>) {
|
||||||
self.fixup_line(context, !self.has_line_break, true);
|
self.fixup_line(context, !self.has_line_break, true);
|
||||||
|
|
||||||
(
|
(
|
||||||
self.first_box,
|
self.boxes,
|
||||||
self.exterior_bounds.unwrap_or_else(Default::default),
|
self.exterior_bounds.unwrap_or_else(Default::default),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_start_of_line(&self) -> bool {
|
fn is_start_of_line(&self) -> bool {
|
||||||
self.current_line.is_none()
|
self.current_line >= self.boxes.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,9 +548,6 @@ pub struct LayoutBox<'gc> {
|
||||||
/// The rectangle corresponding to the outer boundaries of the
|
/// The rectangle corresponding to the outer boundaries of the
|
||||||
bounds: BoxBounds<Twips>,
|
bounds: BoxBounds<Twips>,
|
||||||
|
|
||||||
/// The layout box to be placed after this one.
|
|
||||||
next_sibling: Option<GcCell<'gc, LayoutBox<'gc>>>,
|
|
||||||
|
|
||||||
/// What content is contained by the content box.
|
/// What content is contained by the content box.
|
||||||
content: LayoutContent<'gc>,
|
content: LayoutContent<'gc>,
|
||||||
}
|
}
|
||||||
|
@ -662,70 +616,43 @@ pub enum LayoutContent<'gc> {
|
||||||
|
|
||||||
impl<'gc> LayoutBox<'gc> {
|
impl<'gc> LayoutBox<'gc> {
|
||||||
/// Construct a text box for a text node.
|
/// Construct a text box for a text node.
|
||||||
pub fn from_text(
|
pub fn from_text(start: usize, end: usize, font: Font<'gc>, span: &TextSpan) -> Self {
|
||||||
mc: MutationContext<'gc, '_>,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
font: Font<'gc>,
|
|
||||||
span: &TextSpan,
|
|
||||||
) -> GcCell<'gc, Self> {
|
|
||||||
let params = EvalParameters::from_span(span);
|
let params = EvalParameters::from_span(span);
|
||||||
|
|
||||||
GcCell::allocate(
|
Self {
|
||||||
mc,
|
bounds: Default::default(),
|
||||||
Self {
|
content: LayoutContent::Text {
|
||||||
bounds: Default::default(),
|
start,
|
||||||
next_sibling: None,
|
end,
|
||||||
content: LayoutContent::Text {
|
text_format: span.get_text_format(),
|
||||||
start,
|
font,
|
||||||
end,
|
params,
|
||||||
text_format: span.get_text_format(),
|
color: CollectWrapper(span.color.clone()),
|
||||||
font,
|
|
||||||
params,
|
|
||||||
color: CollectWrapper(span.color.clone()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a bullet.
|
/// Construct a bullet.
|
||||||
pub fn from_bullet(
|
pub fn from_bullet(font: Font<'gc>, span: &TextSpan) -> Self {
|
||||||
mc: MutationContext<'gc, '_>,
|
|
||||||
font: Font<'gc>,
|
|
||||||
span: &TextSpan,
|
|
||||||
) -> GcCell<'gc, Self> {
|
|
||||||
let params = EvalParameters::from_span(span);
|
let params = EvalParameters::from_span(span);
|
||||||
|
|
||||||
GcCell::allocate(
|
Self {
|
||||||
mc,
|
bounds: Default::default(),
|
||||||
Self {
|
content: LayoutContent::Bullet {
|
||||||
bounds: Default::default(),
|
text_format: span.get_text_format(),
|
||||||
next_sibling: None,
|
font,
|
||||||
content: LayoutContent::Bullet {
|
params,
|
||||||
text_format: span.get_text_format(),
|
color: CollectWrapper(span.color.clone()),
|
||||||
font,
|
|
||||||
params,
|
|
||||||
color: CollectWrapper(span.color.clone()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a drawing.
|
/// Construct a drawing.
|
||||||
pub fn from_drawing(mc: MutationContext<'gc, '_>, drawing: Drawing) -> GcCell<'gc, Self> {
|
pub fn from_drawing(drawing: Drawing) -> Self {
|
||||||
GcCell::allocate(
|
Self {
|
||||||
mc,
|
bounds: Default::default(),
|
||||||
Self {
|
content: LayoutContent::Drawing(drawing),
|
||||||
bounds: Default::default(),
|
}
|
||||||
next_sibling: None,
|
|
||||||
content: LayoutContent::Drawing(drawing),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next sibling box.
|
|
||||||
pub fn next_sibling(&self) -> Option<GcCell<'gc, LayoutBox<'gc>>> {
|
|
||||||
self.next_sibling
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new layout hierarchy from text spans.
|
/// Construct a new layout hierarchy from text spans.
|
||||||
|
@ -739,7 +666,7 @@ impl<'gc> LayoutBox<'gc> {
|
||||||
bounds: Twips,
|
bounds: Twips,
|
||||||
is_word_wrap: bool,
|
is_word_wrap: bool,
|
||||||
is_device_font: bool,
|
is_device_font: bool,
|
||||||
) -> (Option<GcCell<'gc, LayoutBox<'gc>>>, BoxBounds<Twips>) {
|
) -> (Vec<LayoutBox<'gc>>, BoxBounds<Twips>) {
|
||||||
let mut layout_context = LayoutContext::new(movie, bounds, fs.text());
|
let mut layout_context = LayoutContext::new(movie, bounds, fs.text());
|
||||||
|
|
||||||
for (span_start, _end, span_text, span) in fs.iter_spans() {
|
for (span_start, _end, span_text, span) in fs.iter_spans() {
|
||||||
|
@ -798,7 +725,6 @@ impl<'gc> LayoutBox<'gc> {
|
||||||
let next_breakpoint = min(last_breakpoint + breakpoint + 1, text.len());
|
let next_breakpoint = min(last_breakpoint + breakpoint + 1, text.len());
|
||||||
|
|
||||||
layout_context.append_text(
|
layout_context.append_text(
|
||||||
context.gc_context,
|
|
||||||
&text[last_breakpoint..next_breakpoint],
|
&text[last_breakpoint..next_breakpoint],
|
||||||
start + last_breakpoint,
|
start + last_breakpoint,
|
||||||
start + next_breakpoint,
|
start + next_breakpoint,
|
||||||
|
@ -822,7 +748,6 @@ impl<'gc> LayoutBox<'gc> {
|
||||||
|
|
||||||
if last_breakpoint < span_end {
|
if last_breakpoint < span_end {
|
||||||
layout_context.append_text(
|
layout_context.append_text(
|
||||||
context.gc_context,
|
|
||||||
&text[last_breakpoint..span_end],
|
&text[last_breakpoint..span_end],
|
||||||
start + last_breakpoint,
|
start + last_breakpoint,
|
||||||
start + span_end,
|
start + span_end,
|
||||||
|
@ -902,7 +827,6 @@ impl<'gc> LayoutBox<'gc> {
|
||||||
context,
|
context,
|
||||||
Self {
|
Self {
|
||||||
bounds: self.bounds,
|
bounds: self.bounds,
|
||||||
next_sibling: self.next_sibling.map(|ns| ns.read().duplicate(context)),
|
|
||||||
content: self.content.clone(),
|
content: self.content.clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue