Skip to content

Commit

Permalink
make inline paintables own their fragments wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kalenikaliaksandr committed Jan 13, 2024
1 parent e6861cb commit 5bebe3e
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 35 deletions.
48 changes: 30 additions & 18 deletions Userland/Libraries/LibWeb/Layout/LayoutState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,12 +293,7 @@ void LayoutState::resolve_border_radii()
}

for (auto& inline_paintable : inline_paintables) {
Vector<Painting::PaintableFragment&> fragments;
verify_cast<Painting::PaintableWithLines>(*inline_paintable.containing_block()->paintable_box()).for_each_fragment([&](auto& fragment) {
if (inline_paintable.layout_node().is_inclusive_ancestor_of(fragment.layout_node()))
fragments.append(const_cast<Painting::PaintableFragment&>(fragment));
return IterationDecision::Continue;
});
auto& fragments = inline_paintable.m_fragments;

auto const& top_left_border_radius = inline_paintable.computed_values().border_top_left_radius();
auto const& top_right_border_radius = inline_paintable.computed_values().border_top_right_radius();
Expand Down Expand Up @@ -449,6 +444,35 @@ void LayoutState::commit(Box& root)

resolve_relative_positions(paintables_with_lines);

for (auto& paintable_with_lines : paintables_with_lines) {
Vector<Painting::PaintableFragment> new_fragments;
for (auto& fragment : paintable_with_lines.fragments()) {
auto find_optional_closest_inline_paintable_of_fragment = [&](Painting::PaintableFragment& fragment) -> Painting::InlinePaintable const* {
auto const* parent = fragment.layout_node().parent();
while (parent) {
if (is<InlineNode>(*parent)) {
VERIFY(is<Painting::InlinePaintable>(*parent->paintable()));
return static_cast<Painting::InlinePaintable const*>(parent->paintable());
}
parent = parent->parent();
}
return nullptr;
};

if (fragment.layout_node().is_text_node())
text_nodes.set(static_cast<Layout::TextNode*>(const_cast<Layout::Node*>(&fragment.layout_node())));

auto* closest_inline_paintable = find_optional_closest_inline_paintable_of_fragment(fragment);
if (closest_inline_paintable) {
const_cast<Painting::InlinePaintable*>(closest_inline_paintable)->m_fragments.append(fragment);
} else {
new_fragments.append(fragment);
}
}

paintable_with_lines.set_fragments(move(new_fragments));
}

// Make a pass over all the line boxes to:
// - Measure absolute rect of each line box.
// - Collect all text nodes, so we can create paintables for them later.
Expand Down Expand Up @@ -476,18 +500,6 @@ void LayoutState::commit(Box& root)
resolve_border_radii();
resolve_box_shadow_data();
resolve_text_shadows(paintables_with_lines);

for (auto& it : used_values_per_layout_node) {
auto& used_values = *it.value;
auto& node = const_cast<NodeWithStyle&>(used_values.node());
auto* paintable = node.paintable();
if (paintable && is<Painting::InlinePaintable>(*paintable)) {
auto& inline_paintable = static_cast<Painting::InlinePaintable&>(*paintable);
// FIXME: Marking fragments contained by inline node is a hack required to skip them while painting
// PaintableWithLines content.
inline_paintable.mark_contained_fragments();
}
}
}

void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* containing_block_used_values)
Expand Down
9 changes: 9 additions & 0 deletions Userland/Libraries/LibWeb/Layout/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/Painting/InlinePaintable.h>
#include <LibWeb/Platform/FontPlugin.h>

namespace Web::Layout {
Expand Down Expand Up @@ -248,6 +249,14 @@ CSSPixelPoint Node::box_type_agnostic_position() const
if (is<Box>(*this))
return verify_cast<Box>(*this).paintable_box()->absolute_position();
VERIFY(is_inline());

if (paintable() && paintable()->is_inline_paintable()) {
auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*paintable());
if (!inline_paintable.m_fragments.is_empty())
return inline_paintable.m_fragments.first().absolute_rect().location();
VERIFY_NOT_REACHED();
}

CSSPixelPoint position;
if (auto* block = containing_block(); block && block->paintable() && is<Painting::PaintableWithLines>(*block->paintable())) {
static_cast<Painting::PaintableWithLines const&>(*block->paintable_box()).for_each_fragment([&](auto& fragment) {
Expand Down
38 changes: 22 additions & 16 deletions Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,26 +165,32 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
template<typename Callback>
void InlinePaintable::for_each_fragment(Callback callback) const
{
// FIXME: This will be slow if the containing block has a lot of fragments!
Vector<PaintableFragment const&> fragments;
verify_cast<PaintableWithLines>(*containing_block()->paintable_box()).for_each_fragment([&](auto& fragment) {
if (layout_node().is_inclusive_ancestor_of(fragment.layout_node()))
fragments.append(fragment);
return IterationDecision::Continue;
});
for (size_t i = 0; i < fragments.size(); ++i) {
auto const& fragment = fragments[i];
callback(fragment, i == 0, i == fragments.size() - 1);
for (size_t i = 0; i < m_fragments.size(); ++i) {
auto const& fragment = m_fragments[i];
callback(fragment, i == 0, i == m_fragments.size() - 1);
}
}

void InlinePaintable::mark_contained_fragments()
Optional<HitTestResult> InlinePaintable::hit_test(CSSPixelPoint position, HitTestType type) const
{
verify_cast<PaintableWithLines>(*containing_block()->paintable_box()).for_each_fragment([&](auto& fragment) {
if (layout_node().is_inclusive_ancestor_of(fragment.layout_node()))
const_cast<PaintableFragment&>(fragment).set_contained_by_inline_node();
return IterationDecision::Continue;
});
for (auto& fragment : m_fragments) {
if (is<Layout::Box>(fragment.layout_node()) && static_cast<Layout::Box const&>(fragment.layout_node()).paintable_box()->stacking_context())
continue;
if (!fragment.layout_node().containing_block()) {
dbgln("FIXME: PaintableWithLines::hit_test(): Missing containing block on {}",
fragment.layout_node().debug_description());
continue;
}
auto fragment_absolute_rect = fragment.absolute_rect();
if (fragment_absolute_rect.contains(position)) {
if (is<Layout::BlockContainer>(fragment.layout_node()) && fragment.layout_node().paintable())
return fragment.layout_node().paintable()->hit_test(position, type);
return HitTestResult { const_cast<Paintable&>(const_cast<Paintable&>(*fragment.layout_node().paintable())),
fragment.text_index_at(position.x()) };
}
}

return {};
}

CSSPixelRect InlinePaintable::bounding_rect() const
Expand Down
5 changes: 4 additions & 1 deletion Userland/Libraries/LibWeb/Painting/InlinePaintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <LibWeb/Layout/InlineNode.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Painting/PaintableFragment.h>

namespace Web::Painting {

Expand All @@ -26,7 +27,9 @@ class InlinePaintable final : public Paintable {

virtual bool is_inline_paintable() const override { return true; }

void mark_contained_fragments();
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const override;

Vector<PaintableFragment> m_fragments;

private:
InlinePaintable(Layout::InlineNode const&);
Expand Down
9 changes: 9 additions & 0 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,15 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
if (!layout_box().children_are_inline() || m_fragments.is_empty())
return PaintableBox::hit_test(position, type);

for (auto* child = first_child(); child; child = child->next_sibling()) {
auto result = child->hit_test(position, type);
if (!result.has_value())
continue;
if (!result->paintable->visible_for_hit_testing())
continue;
return result;
}

Optional<HitTestResult> last_good_candidate;
for (auto const& fragment : fragments()) {
if (is<Layout::Box>(fragment.layout_node()) && static_cast<Layout::Box const&>(fragment.layout_node()).paintable_box()->stacking_context())
Expand Down
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,15 @@ class PaintableWithLines : public PaintableBox {
Layout::BlockContainer& layout_box();

Vector<PaintableFragment> const& fragments() const { return m_fragments; }
Vector<PaintableFragment>& fragments() { return m_fragments; }

void add_fragment(Layout::LineBoxFragment const& fragment)
{
m_fragments.append(PaintableFragment { fragment });
}

void set_fragments(Vector<PaintableFragment>&& fragments) { m_fragments = move(fragments); }

template<typename Callback>
void for_each_fragment(Callback callback) const
{
Expand Down

0 comments on commit 5bebe3e

Please sign in to comment.