mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 11:47:45 +00:00
LibWeb: Move text fragment painting to PaintableWithLines
All the other painting code has moved to paintables already.
This commit is contained in:
parent
7969161f07
commit
be5f0b5ac4
6 changed files with 147 additions and 164 deletions
|
@ -5,26 +5,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Utf8View.h>
|
#include <AK/Utf8View.h>
|
||||||
#include <LibGfx/Painter.h>
|
|
||||||
#include <LibWeb/Layout/FormattingState.h>
|
#include <LibWeb/Layout/FormattingState.h>
|
||||||
#include <LibWeb/Layout/InitialContainingBlock.h>
|
#include <LibWeb/Layout/InitialContainingBlock.h>
|
||||||
#include <LibWeb/Layout/LineBoxFragment.h>
|
#include <LibWeb/Layout/LineBoxFragment.h>
|
||||||
#include <LibWeb/Layout/TextNode.h>
|
#include <LibWeb/Layout/TextNode.h>
|
||||||
#include <LibWeb/Painting/PaintContext.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
void LineBoxFragment::paint(PaintContext& context, Painting::PaintPhase phase)
|
|
||||||
{
|
|
||||||
for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) {
|
|
||||||
if (!ancestor->is_visible())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
layout_node().paint_fragment(context, *this, phase);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LineBoxFragment::ends_in_whitespace() const
|
bool LineBoxFragment::ends_in_whitespace() const
|
||||||
{
|
{
|
||||||
auto text = this->text();
|
auto text = this->text();
|
||||||
|
|
|
@ -55,8 +55,6 @@ public:
|
||||||
|
|
||||||
float absolute_x() const { return absolute_rect().x(); }
|
float absolute_x() const { return absolute_rect().x(); }
|
||||||
|
|
||||||
void paint(PaintContext&, Painting::PaintPhase);
|
|
||||||
|
|
||||||
bool ends_in_whitespace() const;
|
bool ends_in_whitespace() const;
|
||||||
bool is_justifiable_whitespace() const;
|
bool is_justifiable_whitespace() const;
|
||||||
StringView text() const;
|
StringView text() const;
|
||||||
|
|
|
@ -63,8 +63,6 @@ public:
|
||||||
|
|
||||||
bool is_inline_block() const;
|
bool is_inline_block() const;
|
||||||
|
|
||||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, Painting::PaintPhase) const { }
|
|
||||||
|
|
||||||
// These are used to optimize hot is<T> variants for some classes where dynamic_cast is too slow.
|
// These are used to optimize hot is<T> variants for some classes where dynamic_cast is too slow.
|
||||||
virtual bool is_box() const { return false; }
|
virtual bool is_box() const { return false; }
|
||||||
virtual bool is_block_container() const { return false; }
|
virtual bool is_block_container() const { return false; }
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include <AK/CharacterTypes.h>
|
#include <AK/CharacterTypes.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibGfx/Painter.h>
|
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/Layout/BlockContainer.h>
|
#include <LibWeb/Layout/BlockContainer.h>
|
||||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||||
|
@ -35,150 +34,6 @@ static bool is_all_whitespace(StringView string)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextNode::paint_text_decoration(Gfx::Painter& painter, LineBoxFragment const& fragment) const
|
|
||||||
{
|
|
||||||
Gfx::IntPoint line_start_point {};
|
|
||||||
Gfx::IntPoint line_end_point {};
|
|
||||||
|
|
||||||
auto& font = fragment.layout_node().font();
|
|
||||||
auto fragment_box = enclosing_int_rect(fragment.absolute_rect());
|
|
||||||
auto glyph_height = font.glyph_height();
|
|
||||||
auto baseline = fragment_box.height() / 2 - (glyph_height + 4) / 2 + glyph_height;
|
|
||||||
|
|
||||||
switch (computed_values().text_decoration_line()) {
|
|
||||||
case CSS::TextDecorationLine::None:
|
|
||||||
return;
|
|
||||||
case CSS::TextDecorationLine::Underline:
|
|
||||||
line_start_point = fragment_box.top_left().translated(0, baseline + 2);
|
|
||||||
line_end_point = fragment_box.top_right().translated(0, baseline + 2);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationLine::Overline:
|
|
||||||
line_start_point = fragment_box.top_left().translated(0, baseline - glyph_height);
|
|
||||||
line_end_point = fragment_box.top_right().translated(0, baseline - glyph_height);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationLine::LineThrough: {
|
|
||||||
auto x_height = font.x_height();
|
|
||||||
line_start_point = fragment_box.top_left().translated(0, baseline - x_height / 2);
|
|
||||||
line_end_point = fragment_box.top_right().translated(0, baseline - x_height / 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CSS::TextDecorationLine::Blink:
|
|
||||||
// Conforming user agents may simply not blink the text
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto line_color = computed_values().text_decoration_color();
|
|
||||||
|
|
||||||
int line_thickness = [this] {
|
|
||||||
CSS::Length computed_thickness = computed_values().text_decoration_thickness().resolved(*this, CSS::Length(1, CSS::Length::Type::Em));
|
|
||||||
if (computed_thickness.is_auto())
|
|
||||||
return CSS::InitialValues::text_decoration_thickness().to_px(*this);
|
|
||||||
|
|
||||||
return computed_thickness.to_px(*this);
|
|
||||||
}();
|
|
||||||
|
|
||||||
switch (computed_values().text_decoration_style()) {
|
|
||||||
case CSS::TextDecorationStyle::Solid:
|
|
||||||
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Solid);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationStyle::Double:
|
|
||||||
switch (computed_values().text_decoration_line()) {
|
|
||||||
case CSS::TextDecorationLine::Underline:
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationLine::Overline:
|
|
||||||
line_start_point.translate_by(0, -line_thickness - 1);
|
|
||||||
line_end_point.translate_by(0, -line_thickness - 1);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationLine::LineThrough:
|
|
||||||
line_start_point.translate_by(0, -line_thickness / 2);
|
|
||||||
line_end_point.translate_by(0, -line_thickness / 2);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness);
|
|
||||||
painter.draw_line(line_start_point.translated(0, line_thickness + 1), line_end_point.translated(0, line_thickness + 1), line_color, line_thickness);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationStyle::Dashed:
|
|
||||||
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dashed);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationStyle::Dotted:
|
|
||||||
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dotted);
|
|
||||||
break;
|
|
||||||
case CSS::TextDecorationStyle::Wavy:
|
|
||||||
painter.draw_triangle_wave(line_start_point, line_end_point, line_color, line_thickness + 1, line_thickness);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, Painting::PaintPhase phase) const
|
|
||||||
{
|
|
||||||
auto& painter = context.painter();
|
|
||||||
|
|
||||||
if (phase == Painting::PaintPhase::Foreground) {
|
|
||||||
auto fragment_absolute_rect = fragment.absolute_rect();
|
|
||||||
|
|
||||||
painter.set_font(font());
|
|
||||||
|
|
||||||
if (document().inspected_node() == &dom_node())
|
|
||||||
context.painter().draw_rect(enclosing_int_rect(fragment_absolute_rect), Color::Magenta);
|
|
||||||
|
|
||||||
// FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
|
|
||||||
auto text = m_text_for_rendering;
|
|
||||||
auto text_transform = computed_values().text_transform();
|
|
||||||
if (text_transform == CSS::TextTransform::Uppercase)
|
|
||||||
text = m_text_for_rendering.to_uppercase();
|
|
||||||
if (text_transform == CSS::TextTransform::Lowercase)
|
|
||||||
text = m_text_for_rendering.to_lowercase();
|
|
||||||
|
|
||||||
// FIXME: This is a hack to prevent text clipping when painting a bitmap font into a too-small box.
|
|
||||||
auto draw_rect = enclosing_int_rect(fragment_absolute_rect);
|
|
||||||
draw_rect.set_height(max(draw_rect.height(), font().glyph_height()));
|
|
||||||
painter.draw_text(draw_rect, text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, computed_values().color());
|
|
||||||
|
|
||||||
auto selection_rect = fragment.selection_rect(font());
|
|
||||||
if (!selection_rect.is_empty()) {
|
|
||||||
painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
|
|
||||||
Gfx::PainterStateSaver saver(painter);
|
|
||||||
painter.add_clip_rect(enclosing_int_rect(selection_rect));
|
|
||||||
painter.draw_text(enclosing_int_rect(fragment_absolute_rect), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, context.palette().selection_text());
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_text_decoration(painter, fragment);
|
|
||||||
|
|
||||||
paint_cursor_if_needed(context, fragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragment& fragment) const
|
|
||||||
{
|
|
||||||
if (!browsing_context().is_focused_context())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!browsing_context().cursor_blink_state())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (browsing_context().cursor_position().node() != &dom_node())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted.
|
|
||||||
if (browsing_context().cursor_position().offset() < (unsigned)fragment.start() || browsing_context().cursor_position().offset() > (unsigned)(fragment.start() + fragment.length()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto fragment_rect = fragment.absolute_rect();
|
|
||||||
|
|
||||||
float cursor_x = fragment_rect.x() + font().width(fragment.text().substring_view(0, browsing_context().cursor_position().offset() - fragment.start()));
|
|
||||||
float cursor_top = fragment_rect.top();
|
|
||||||
float cursor_height = fragment_rect.height();
|
|
||||||
Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height);
|
|
||||||
|
|
||||||
context.painter().draw_rect(cursor_rect, computed_values().color());
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This collapes whitespace into a single ASCII space if collapse is true. If previous_is_empty_or_ends_in_whitespace, it also strips leading whitespace.
|
// NOTE: This collapes whitespace into a single ASCII space if collapse is true. If previous_is_empty_or_ends_in_whitespace, it also strips leading whitespace.
|
||||||
void TextNode::compute_text_for_rendering(bool collapse, bool previous_is_empty_or_ends_in_whitespace)
|
void TextNode::compute_text_for_rendering(bool collapse, bool previous_is_empty_or_ends_in_whitespace)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,8 +23,6 @@ public:
|
||||||
|
|
||||||
const String& text_for_rendering() const { return m_text_for_rendering; }
|
const String& text_for_rendering() const { return m_text_for_rendering; }
|
||||||
|
|
||||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, Painting::PaintPhase) const override;
|
|
||||||
|
|
||||||
struct Chunk {
|
struct Chunk {
|
||||||
Utf8View view;
|
Utf8View view;
|
||||||
size_t start { 0 };
|
size_t start { 0 };
|
||||||
|
|
|
@ -242,6 +242,151 @@ void PaintableBox::after_children_paint(PaintContext& context, PaintPhase) const
|
||||||
context.painter().restore();
|
context.painter().restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_node, Layout::LineBoxFragment const& fragment)
|
||||||
|
{
|
||||||
|
auto const& browsing_context = text_node.browsing_context();
|
||||||
|
|
||||||
|
if (!browsing_context.is_focused_context())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!browsing_context.cursor_blink_state())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (browsing_context.cursor_position().node() != &text_node.dom_node())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted.
|
||||||
|
if (browsing_context.cursor_position().offset() < (unsigned)fragment.start() || browsing_context.cursor_position().offset() > (unsigned)(fragment.start() + fragment.length()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto fragment_rect = fragment.absolute_rect();
|
||||||
|
|
||||||
|
float cursor_x = fragment_rect.x() + text_node.font().width(fragment.text().substring_view(0, text_node.browsing_context().cursor_position().offset() - fragment.start()));
|
||||||
|
float cursor_top = fragment_rect.top();
|
||||||
|
float cursor_height = fragment_rect.height();
|
||||||
|
Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height);
|
||||||
|
|
||||||
|
context.painter().draw_rect(cursor_rect, text_node.computed_values().color());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void paint_text_decoration(Gfx::Painter& painter, Layout::Node const& text_node, Layout::LineBoxFragment const& fragment)
|
||||||
|
{
|
||||||
|
Gfx::IntPoint line_start_point {};
|
||||||
|
Gfx::IntPoint line_end_point {};
|
||||||
|
|
||||||
|
auto& font = fragment.layout_node().font();
|
||||||
|
auto fragment_box = enclosing_int_rect(fragment.absolute_rect());
|
||||||
|
auto glyph_height = font.glyph_height();
|
||||||
|
auto baseline = fragment_box.height() / 2 - (glyph_height + 4) / 2 + glyph_height;
|
||||||
|
|
||||||
|
switch (text_node.computed_values().text_decoration_line()) {
|
||||||
|
case CSS::TextDecorationLine::None:
|
||||||
|
return;
|
||||||
|
case CSS::TextDecorationLine::Underline:
|
||||||
|
line_start_point = fragment_box.top_left().translated(0, baseline + 2);
|
||||||
|
line_end_point = fragment_box.top_right().translated(0, baseline + 2);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationLine::Overline:
|
||||||
|
line_start_point = fragment_box.top_left().translated(0, baseline - glyph_height);
|
||||||
|
line_end_point = fragment_box.top_right().translated(0, baseline - glyph_height);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationLine::LineThrough: {
|
||||||
|
auto x_height = font.x_height();
|
||||||
|
line_start_point = fragment_box.top_left().translated(0, baseline - x_height / 2);
|
||||||
|
line_end_point = fragment_box.top_right().translated(0, baseline - x_height / 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CSS::TextDecorationLine::Blink:
|
||||||
|
// Conforming user agents may simply not blink the text
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto line_color = text_node.computed_values().text_decoration_color();
|
||||||
|
|
||||||
|
int line_thickness = [&] {
|
||||||
|
CSS::Length computed_thickness = text_node.computed_values().text_decoration_thickness().resolved(text_node, CSS::Length(1, CSS::Length::Type::Em));
|
||||||
|
if (computed_thickness.is_auto())
|
||||||
|
return CSS::InitialValues::text_decoration_thickness().to_px(text_node);
|
||||||
|
|
||||||
|
return computed_thickness.to_px(text_node);
|
||||||
|
}();
|
||||||
|
|
||||||
|
switch (text_node.computed_values().text_decoration_style()) {
|
||||||
|
case CSS::TextDecorationStyle::Solid:
|
||||||
|
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Solid);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationStyle::Double:
|
||||||
|
switch (text_node.computed_values().text_decoration_line()) {
|
||||||
|
case CSS::TextDecorationLine::Underline:
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationLine::Overline:
|
||||||
|
line_start_point.translate_by(0, -line_thickness - 1);
|
||||||
|
line_end_point.translate_by(0, -line_thickness - 1);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationLine::LineThrough:
|
||||||
|
line_start_point.translate_by(0, -line_thickness / 2);
|
||||||
|
line_end_point.translate_by(0, -line_thickness / 2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness);
|
||||||
|
painter.draw_line(line_start_point.translated(0, line_thickness + 1), line_end_point.translated(0, line_thickness + 1), line_color, line_thickness);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationStyle::Dashed:
|
||||||
|
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dashed);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationStyle::Dotted:
|
||||||
|
painter.draw_line(line_start_point, line_end_point, line_color, line_thickness, Gfx::Painter::LineStyle::Dotted);
|
||||||
|
break;
|
||||||
|
case CSS::TextDecorationStyle::Wavy:
|
||||||
|
painter.draw_triangle_wave(line_start_point, line_end_point, line_color, line_thickness + 1, line_thickness);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_node, Layout::LineBoxFragment const& fragment, Painting::PaintPhase phase)
|
||||||
|
{
|
||||||
|
auto& painter = context.painter();
|
||||||
|
|
||||||
|
if (phase == Painting::PaintPhase::Foreground) {
|
||||||
|
auto fragment_absolute_rect = fragment.absolute_rect();
|
||||||
|
|
||||||
|
painter.set_font(text_node.font());
|
||||||
|
|
||||||
|
if (text_node.document().inspected_node() == &text_node.dom_node())
|
||||||
|
context.painter().draw_rect(enclosing_int_rect(fragment_absolute_rect), Color::Magenta);
|
||||||
|
|
||||||
|
// FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
|
||||||
|
auto text = text_node.text_for_rendering();
|
||||||
|
auto text_transform = text_node.computed_values().text_transform();
|
||||||
|
if (text_transform == CSS::TextTransform::Uppercase)
|
||||||
|
text = text_node.text_for_rendering().to_uppercase();
|
||||||
|
if (text_transform == CSS::TextTransform::Lowercase)
|
||||||
|
text = text_node.text_for_rendering().to_lowercase();
|
||||||
|
|
||||||
|
// FIXME: This is a hack to prevent text clipping when painting a bitmap font into a too-small box.
|
||||||
|
auto draw_rect = enclosing_int_rect(fragment_absolute_rect);
|
||||||
|
draw_rect.set_height(max(draw_rect.height(), text_node.font().glyph_height()));
|
||||||
|
painter.draw_text(draw_rect, text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, text_node.computed_values().color());
|
||||||
|
|
||||||
|
auto selection_rect = fragment.selection_rect(text_node.font());
|
||||||
|
if (!selection_rect.is_empty()) {
|
||||||
|
painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
|
||||||
|
Gfx::PainterStateSaver saver(painter);
|
||||||
|
painter.add_clip_rect(enclosing_int_rect(selection_rect));
|
||||||
|
painter.draw_text(enclosing_int_rect(fragment_absolute_rect), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, context.palette().selection_text());
|
||||||
|
}
|
||||||
|
|
||||||
|
paint_text_decoration(painter, text_node, fragment);
|
||||||
|
paint_cursor_if_needed(context, text_node, fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
|
void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
|
||||||
{
|
{
|
||||||
if (!is_visible())
|
if (!is_visible())
|
||||||
|
@ -266,7 +411,8 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
|
||||||
for (auto& fragment : line_box.fragments()) {
|
for (auto& fragment : line_box.fragments()) {
|
||||||
if (context.should_show_line_box_borders())
|
if (context.should_show_line_box_borders())
|
||||||
context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green);
|
context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Green);
|
||||||
const_cast<Layout::LineBoxFragment&>(fragment).paint(context, phase);
|
if (is<Layout::TextNode>(fragment.layout_node()))
|
||||||
|
paint_text_fragment(context, static_cast<Layout::TextNode const&>(fragment.layout_node()), fragment, phase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue