1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:38:11 +00:00

LibHTML: Introduce LayoutBox and LayoutNodeWithStyleAndBoxModelMetrics

To streamline the layout tree and remove irrelevant data from classes
that don't need it, this patch adds two new LayoutNode subclasses.

LayoutNodeWithStyleAndBoxModelMetrics should be inherited by any layout
node that cares about box model metrics (margin, border, and padding.)
LayoutBox should be inherited by any layout node that can have a rect.

This makes LayoutText significantly smaller (from 140 to 40 bytes) and
clarifies a lot of things about the layout tree.

I'm also adding next_sibling() and previous_sibling() overloads to
LayoutBlock that return a LayoutBlock*. This is okay since blocks only
ever have block siblings.

Do also note that the semantics of is<T> slightly change in this patch:
is<T>(nullptr) now returns true, to facilitate allowing to<T>(nullptr).
This commit is contained in:
Andreas Kling 2019-10-15 16:48:38 +02:00
parent f4f5ede10a
commit 4814253589
18 changed files with 219 additions and 141 deletions

View file

@ -5,9 +5,6 @@
#include <LibHTML/Layout/LayoutBlock.h>
#include <LibHTML/Layout/LayoutNode.h>
//#define DRAW_BOXES_AROUND_LAYOUT_NODES
//#define DRAW_BOXES_AROUND_HOVERED_NODES
LayoutNode::LayoutNode(const Node* node)
: m_node(node)
{
@ -42,58 +39,6 @@ void LayoutNode::render(RenderingContext& context)
if (!is_visible())
return;
#ifdef DRAW_BOXES_AROUND_LAYOUT_NODES
context.painter().draw_rect(m_rect, Color::Blue);
#endif
#ifdef DRAW_BOXES_AROUND_HOVERED_NODES
if (!is_anonymous() && node() == document().hovered_node())
context.painter().draw_rect(m_rect, Color::Red);
#endif
Rect padded_rect;
padded_rect.set_x(x() - box_model().padding().left.to_px());
padded_rect.set_width(width() + box_model().padding().left.to_px() + box_model().padding().right.to_px());
padded_rect.set_y(y() - box_model().padding().top.to_px());
padded_rect.set_height(height() + box_model().padding().top.to_px() + box_model().padding().bottom.to_px());
auto bgcolor = style().property(CSS::PropertyID::BackgroundColor);
if (bgcolor.has_value() && bgcolor.value()->is_color()) {
context.painter().fill_rect(padded_rect, bgcolor.value()->to_color(document()));
}
// FIXME: Respect all individual border sides
auto border_width_value = style().property(CSS::PropertyID::BorderTopWidth);
auto border_color_value = style().property(CSS::PropertyID::BorderTopColor);
auto border_style_value = style().property(CSS::PropertyID::BorderTopStyle);
if (border_width_value.has_value() && border_color_value.has_value()) {
int border_width = border_width_value.value()->to_length().to_px();
Color border_color = border_color_value.value()->to_color(document());
if (border_style_value.has_value() && border_style_value.value()->to_string() == "inset") {
// border-style: inset
auto shadow_color = Color::from_rgb(0x888888);
auto highlight_color = Color::from_rgb(0x5a5a5a);
context.painter().draw_line(padded_rect.top_left(), padded_rect.top_right(), highlight_color, border_width);
context.painter().draw_line(padded_rect.top_right(), padded_rect.bottom_right(), shadow_color, border_width);
context.painter().draw_line(padded_rect.bottom_right(), padded_rect.bottom_left(), shadow_color, border_width);
context.painter().draw_line(padded_rect.bottom_left(), padded_rect.top_left(), highlight_color, border_width);
} else if (border_style_value.has_value() && border_style_value.value()->to_string() == "outset") {
// border-style: outset
auto highlight_color = Color::from_rgb(0x888888);
auto shadow_color = Color::from_rgb(0x5a5a5a);
context.painter().draw_line(padded_rect.top_left(), padded_rect.top_right(), highlight_color, border_width);
context.painter().draw_line(padded_rect.top_right(), padded_rect.bottom_right(), shadow_color, border_width);
context.painter().draw_line(padded_rect.bottom_right(), padded_rect.bottom_left(), shadow_color, border_width);
context.painter().draw_line(padded_rect.bottom_left(), padded_rect.top_left(), highlight_color, border_width);
} else {
// border-style: solid
context.painter().draw_line(padded_rect.top_left(), padded_rect.top_right(), border_color, border_width);
context.painter().draw_line(padded_rect.top_right(), padded_rect.bottom_right(), border_color, border_width);
context.painter().draw_line(padded_rect.bottom_right(), padded_rect.bottom_left(), border_color, border_width);
context.painter().draw_line(padded_rect.bottom_left(), padded_rect.top_left(), border_color, border_width);
}
}
// TODO: render our border
for_each_child([&](auto& child) {
child.render(context);
@ -102,10 +47,7 @@ void LayoutNode::render(RenderingContext& context)
HitTestResult LayoutNode::hit_test(const Point& position) const
{
// FIXME: It would be nice if we could confidently skip over hit testing
// parts of the layout tree, but currently we can't just check
// m_rect.contains() since inline text rects can't be trusted..
HitTestResult result { m_rect.contains(position) ? this : nullptr };
HitTestResult result;
for_each_child([&](auto& child) {
auto child_result = child.hit_test(position);
if (child_result.layout_node)
@ -134,18 +76,4 @@ void LayoutNode::split_into_lines(LayoutBlock& container)
void LayoutNode::set_needs_display()
{
auto* frame = document().frame();
ASSERT(frame);
if (!is_inline()) {
const_cast<Frame*>(frame)->set_needs_display(rect());
return;
}
for_each_fragment_of_this([&](auto& fragment) {
if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
const_cast<Frame*>(frame)->set_needs_display(fragment.rect());
}
return IterationDecision::Continue;
});
}