1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 02:27:43 +00:00

LibWeb: Rename directory LibHTML => LibWeb

Let's rename this to LibWeb since it aims to provide more parts of the
web platform than just HTML. :^)
This commit is contained in:
Andreas Kling 2020-03-07 10:32:51 +01:00
parent 7a6c4a72d5
commit 830a57c6b2
158 changed files with 360 additions and 360 deletions

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/Layout/BoxModelMetrics.h>
namespace Web {
BoxModelMetrics::BoxModelMetrics()
{
}
BoxModelMetrics::~BoxModelMetrics()
{
}
BoxModelMetrics::PixelBox BoxModelMetrics::full_margin() const
{
return {
m_margin.top.to_px() + m_border.top.to_px() + m_padding.top.to_px(),
m_margin.right.to_px() + m_border.right.to_px() + m_padding.right.to_px(),
m_margin.bottom.to_px() + m_border.bottom.to_px() + m_padding.bottom.to_px(),
m_margin.left.to_px() + m_border.left.to_px() + m_padding.left.to_px(),
};
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibGfx/Size.h>
#include <LibWeb/CSS/LengthBox.h>
namespace Web {
class BoxModelMetrics {
public:
BoxModelMetrics();
~BoxModelMetrics();
LengthBox& margin() { return m_margin; }
LengthBox& padding() { return m_padding; }
LengthBox& border() { return m_border; }
const LengthBox& margin() const { return m_margin; }
const LengthBox& padding() const { return m_padding; }
const LengthBox& border() const { return m_border; }
struct PixelBox {
float top;
float right;
float bottom;
float left;
};
PixelBox full_margin() const;
private:
LengthBox m_margin;
LengthBox m_padding;
LengthBox m_border;
};
}

View file

@ -0,0 +1,390 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGUI/Painter.h>
#include <LibWeb/CSS/StyleResolver.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutInline.h>
#include <LibWeb/Layout/LayoutReplaced.h>
#include <LibWeb/Layout/LayoutText.h>
#include <math.h>
namespace Web {
LayoutBlock::LayoutBlock(const Node* node, NonnullRefPtr<StyleProperties> style)
: LayoutBox(node, move(style))
{
}
LayoutBlock::~LayoutBlock()
{
}
LayoutNode& LayoutBlock::inline_wrapper()
{
if (!last_child() || !last_child()->is_block() || last_child()->node() != nullptr) {
append_child(adopt(*new LayoutBlock(nullptr, style_for_anonymous_block())));
last_child()->set_children_are_inline(true);
}
return *last_child();
}
void LayoutBlock::layout()
{
compute_width();
compute_position();
if (children_are_inline())
layout_inline_children();
else
layout_block_children();
compute_height();
}
void LayoutBlock::layout_block_children()
{
ASSERT(!children_are_inline());
float content_height = 0;
for_each_child([&](auto& child) {
// FIXME: What should we do here? Something like a <table> might have a bunch of useless text children..
if (child.is_inline())
return;
auto& child_block = static_cast<LayoutBlock&>(child);
child_block.layout();
content_height = child_block.rect().bottom() + child_block.box_model().full_margin().bottom - rect().top();
});
rect().set_height(content_height);
}
void LayoutBlock::layout_inline_children()
{
ASSERT(children_are_inline());
m_line_boxes.clear();
for_each_child([&](auto& child) {
ASSERT(child.is_inline());
child.split_into_lines(*this);
});
for (auto& line_box : m_line_boxes) {
line_box.trim_trailing_whitespace();
}
float min_line_height = style().line_height();
float line_spacing = min_line_height - style().font().glyph_height();
float content_height = 0;
// FIXME: This should be done by the CSS parser!
CSS::ValueID text_align = CSS::ValueID::Left;
auto text_align_string = style().string_or_fallback(CSS::PropertyID::TextAlign, "left");
if (text_align_string == "center")
text_align = CSS::ValueID::Center;
else if (text_align_string == "left")
text_align = CSS::ValueID::Left;
else if (text_align_string == "right")
text_align = CSS::ValueID::Right;
else if (text_align_string == "justify")
text_align = CSS::ValueID::Justify;
for (auto& line_box : m_line_boxes) {
float max_height = min_line_height;
for (auto& fragment : line_box.fragments()) {
max_height = max(max_height, fragment.rect().height());
}
float x_offset = x();
float excess_horizontal_space = (float)width() - line_box.width();
switch (text_align) {
case CSS::ValueID::Center:
x_offset += excess_horizontal_space / 2;
break;
case CSS::ValueID::Right:
x_offset += excess_horizontal_space;
break;
case CSS::ValueID::Left:
case CSS::ValueID::Justify:
default:
break;
}
float excess_horizontal_space_including_whitespace = excess_horizontal_space;
int whitespace_count = 0;
if (text_align == CSS::ValueID::Justify) {
for (auto& fragment : line_box.fragments()) {
if (fragment.is_justifiable_whitespace()) {
++whitespace_count;
excess_horizontal_space_including_whitespace += fragment.rect().width();
}
}
}
float justified_space_width = whitespace_count ? (excess_horizontal_space_including_whitespace / (float)whitespace_count) : 0;
for (size_t i = 0; i < line_box.fragments().size(); ++i) {
auto& fragment = line_box.fragments()[i];
// Vertically align everyone's bottom to the line.
// FIXME: Support other kinds of vertical alignment.
fragment.rect().set_x(roundf(x_offset + fragment.rect().x()));
fragment.rect().set_y(y() + content_height + (max_height - fragment.rect().height()) - (line_spacing / 2));
if (text_align == CSS::ValueID::Justify) {
if (fragment.is_justifiable_whitespace()) {
if (fragment.rect().width() != justified_space_width) {
float diff = justified_space_width - fragment.rect().width();
fragment.rect().set_width(justified_space_width);
// Shift subsequent sibling fragments to the right to adjust for change in width.
for (size_t j = i + 1; j < line_box.fragments().size(); ++j) {
line_box.fragments()[j].rect().move_by(diff, 0);
}
}
}
}
if (is<LayoutReplaced>(fragment.layout_node()))
const_cast<LayoutReplaced&>(to<LayoutReplaced>(fragment.layout_node())).set_rect(fragment.rect());
float final_line_box_width = 0;
for (auto& fragment : line_box.fragments())
final_line_box_width += fragment.rect().width();
line_box.m_width = final_line_box_width;
}
content_height += max_height;
}
rect().set_height(content_height);
}
void LayoutBlock::compute_width()
{
auto& style = this->style();
auto auto_value = Length();
auto zero_value = Length(0, Length::Type::Absolute);
Length margin_left;
Length margin_right;
Length border_left;
Length border_right;
Length padding_left;
Length padding_right;
auto try_compute_width = [&](const auto& a_width) {
Length width = a_width;
#ifdef HTML_DEBUG
dbg() << " Left: " << margin_left << "+" << border_left << "+" << padding_left;
dbg() << "Right: " << margin_right << "+" << border_right << "+" << padding_right;
#endif
margin_left = style.length_or_fallback(CSS::PropertyID::MarginLeft, zero_value);
margin_right = style.length_or_fallback(CSS::PropertyID::MarginRight, zero_value);
border_left = style.length_or_fallback(CSS::PropertyID::BorderLeftWidth, zero_value);
border_right = style.length_or_fallback(CSS::PropertyID::BorderRightWidth, zero_value);
padding_left = style.length_or_fallback(CSS::PropertyID::PaddingLeft, zero_value);
padding_right = style.length_or_fallback(CSS::PropertyID::PaddingRight, zero_value);
float total_px = 0;
for (auto& value : { margin_left, border_left, padding_left, width, padding_right, border_right, margin_right }) {
total_px += value.to_px();
}
#ifdef HTML_DEBUG
dbg() << "Total: " << total_px;
#endif
// 10.3.3 Block-level, non-replaced elements in normal flow
// If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
if (width.is_auto() && total_px > containing_block()->width()) {
if (margin_left.is_auto())
margin_left = zero_value;
if (margin_right.is_auto())
margin_right = zero_value;
}
// 10.3.3 cont'd.
auto underflow_px = containing_block()->width() - total_px;
if (width.is_auto()) {
if (margin_left.is_auto())
margin_left = zero_value;
if (margin_right.is_auto())
margin_right = zero_value;
if (underflow_px >= 0) {
width = Length(underflow_px, Length::Type::Absolute);
} else {
width = zero_value;
margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute);
}
} else {
if (!margin_left.is_auto() && !margin_right.is_auto()) {
margin_right = Length(margin_right.to_px() + underflow_px, Length::Type::Absolute);
} else if (!margin_left.is_auto() && margin_right.is_auto()) {
margin_right = Length(underflow_px, Length::Type::Absolute);
} else if (margin_left.is_auto() && !margin_right.is_auto()) {
margin_left = Length(underflow_px, Length::Type::Absolute);
} else { // margin_left.is_auto() && margin_right.is_auto()
auto half_of_the_underflow = Length(underflow_px / 2, Length::Type::Absolute);
margin_left = half_of_the_underflow;
margin_right = half_of_the_underflow;
}
}
return width;
};
auto specified_width = style.length_or_fallback(CSS::PropertyID::Width, auto_value);
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
auto used_width = try_compute_width(specified_width);
// 2. The tentative used width is greater than 'max-width', the rules above are applied again,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
auto specified_max_width = style.length_or_fallback(CSS::PropertyID::MaxWidth, auto_value);
if (!specified_max_width.is_auto()) {
if (used_width.to_px() > specified_max_width.to_px()) {
used_width = try_compute_width(specified_max_width);
}
}
// 3. If the resulting width is smaller than 'min-width', the rules above are applied again,
// but this time using the value of 'min-width' as the computed value for 'width'.
auto specified_min_width = style.length_or_fallback(CSS::PropertyID::MinWidth, auto_value);
if (!specified_min_width.is_auto()) {
if (used_width.to_px() < specified_min_width.to_px()) {
used_width = try_compute_width(specified_min_width);
}
}
rect().set_width(used_width.to_px());
box_model().margin().left = margin_left;
box_model().margin().right = margin_right;
box_model().border().left = border_left;
box_model().border().right = border_right;
box_model().padding().left = padding_left;
box_model().padding().right = padding_right;
}
void LayoutBlock::compute_position()
{
auto& style = this->style();
auto auto_value = Length();
auto zero_value = Length(0, Length::Type::Absolute);
auto width = style.length_or_fallback(CSS::PropertyID::Width, auto_value);
box_model().margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, zero_value);
box_model().margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, zero_value);
box_model().border().top = style.length_or_fallback(CSS::PropertyID::BorderTopWidth, zero_value);
box_model().border().bottom = style.length_or_fallback(CSS::PropertyID::BorderBottomWidth, zero_value);
box_model().padding().top = style.length_or_fallback(CSS::PropertyID::PaddingTop, zero_value);
box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value);
rect().set_x(containing_block()->x() + box_model().margin().left.to_px() + box_model().border().left.to_px() + box_model().padding().left.to_px());
float top_border = -1;
if (previous_sibling() != nullptr) {
auto& previous_sibling_rect = previous_sibling()->rect();
auto& previous_sibling_style = previous_sibling()->box_model();
top_border = previous_sibling_rect.y() + previous_sibling_rect.height();
top_border += previous_sibling_style.full_margin().bottom;
} else {
top_border = containing_block()->y();
}
rect().set_y(top_border + box_model().full_margin().top);
}
void LayoutBlock::compute_height()
{
auto& style = this->style();
auto height_property = style.property(CSS::PropertyID::Height);
if (!height_property.has_value())
return;
auto height_length = height_property.value()->to_length();
if (height_length.is_absolute())
rect().set_height(height_length.to_px());
}
void LayoutBlock::render(RenderingContext& context)
{
if (!is_visible())
return;
LayoutBox::render(context);
if (children_are_inline()) {
for (auto& line_box : m_line_boxes) {
for (auto& fragment : line_box.fragments()) {
if (context.should_show_line_box_borders())
context.painter().draw_rect(enclosing_int_rect(fragment.rect()), Color::Green);
fragment.render(context);
}
}
}
}
HitTestResult LayoutBlock::hit_test(const Gfx::Point& position) const
{
if (!children_are_inline())
return LayoutBox::hit_test(position);
HitTestResult result;
for (auto& line_box : m_line_boxes) {
for (auto& fragment : line_box.fragments()) {
if (enclosing_int_rect(fragment.rect()).contains(position)) {
return { fragment.layout_node(), fragment.text_index_at(position.x()) };
}
}
}
return {};
}
NonnullRefPtr<StyleProperties> LayoutBlock::style_for_anonymous_block() const
{
auto new_style = StyleProperties::create();
style().for_each_property([&](auto property_id, auto& value) {
if (StyleResolver::is_inherited_property(property_id))
new_style->set_property(property_id, value);
});
return new_style;
}
LineBox& LayoutBlock::ensure_last_line_box()
{
if (m_line_boxes.is_empty())
m_line_boxes.append(LineBox());
return m_line_boxes.last();
}
LineBox& LayoutBlock::add_line_box()
{
m_line_boxes.append(LineBox());
return m_line_boxes.last();
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBox.h>
#include <LibWeb/Layout/LineBox.h>
namespace Web {
class Element;
class LayoutBlock : public LayoutBox {
public:
LayoutBlock(const Node*, NonnullRefPtr<StyleProperties>);
virtual ~LayoutBlock() override;
virtual const char* class_name() const override { return "LayoutBlock"; }
virtual void layout() override;
virtual void render(RenderingContext&) override;
virtual LayoutNode& inline_wrapper() override;
Vector<LineBox>& line_boxes() { return m_line_boxes; }
const Vector<LineBox>& line_boxes() const { return m_line_boxes; }
LineBox& ensure_last_line_box();
LineBox& add_line_box();
virtual HitTestResult hit_test(const Gfx::Point&) const override;
LayoutBlock* previous_sibling() { return to<LayoutBlock>(LayoutNode::previous_sibling()); }
const LayoutBlock* previous_sibling() const { return to<LayoutBlock>(LayoutNode::previous_sibling()); }
LayoutBlock* next_sibling() { return to<LayoutBlock>(LayoutNode::next_sibling()); }
const LayoutBlock* next_sibling() const { return to<LayoutBlock>(LayoutNode::next_sibling()); }
template<typename Callback>
void for_each_fragment(Callback);
template<typename Callback>
void for_each_fragment(Callback) const;
private:
virtual bool is_block() const override { return true; }
NonnullRefPtr<StyleProperties> style_for_anonymous_block() const;
void layout_inline_children();
void layout_block_children();
void compute_width();
void compute_position();
void compute_height();
Vector<LineBox> m_line_boxes;
};
template<typename Callback>
void LayoutBlock::for_each_fragment(Callback callback)
{
for (auto& line_box : line_boxes()) {
for (auto& fragment : line_box.fragments()) {
if (callback(fragment) == IterationDecision::Break)
return;
}
}
}
template<typename Callback>
void LayoutBlock::for_each_fragment(Callback callback) const
{
for (auto& line_box : line_boxes()) {
for (auto& fragment : line_box.fragments()) {
if (callback(fragment) == IterationDecision::Break)
return;
}
}
}
template<>
inline bool is<LayoutBlock>(const LayoutNode& node)
{
return node.is_block();
}
}

View file

@ -0,0 +1,244 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGUI/Painter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/HTMLBodyElement.h>
#include <LibWeb/Frame.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutBox.h>
//#define DRAW_BOXES_AROUND_LAYOUT_NODES
//#define DRAW_BOXES_AROUND_HOVERED_NODES
namespace Web {
void LayoutBox::paint_border(RenderingContext& context, Edge edge, const Gfx::FloatRect& rect, CSS::PropertyID style_property_id, CSS::PropertyID color_property_id, CSS::PropertyID width_property_id)
{
auto border_width = style().property(width_property_id);
if (!border_width.has_value())
return;
auto border_style = style().property(style_property_id);
float width = border_width.value()->to_length().to_px();
int int_width = max((int)width, 1);
Color color;
auto border_color = style().property(color_property_id);
if (border_color.has_value()) {
color = border_color.value()->to_color(document());
} else {
// FIXME: This is basically CSS "currentColor" which should be handled elsewhere
// in a much more reusable way.
auto current_color = style().property(CSS::PropertyID::Color);
if (current_color.has_value())
color = current_color.value()->to_color(document());
else
color = Color::Black;
}
auto first_point_for_edge = [](Edge edge, const Gfx::FloatRect& rect) {
switch (edge) {
case Edge::Top:
return rect.top_left();
case Edge::Right:
return rect.top_right();
case Edge::Bottom:
return rect.bottom_left();
case Edge::Left:
default:
return rect.top_left();
}
};
auto second_point_for_edge = [](Edge edge, const Gfx::FloatRect& rect) {
switch (edge) {
case Edge::Top:
return rect.top_right();
case Edge::Right:
return rect.bottom_right();
case Edge::Bottom:
return rect.bottom_right();
case Edge::Left:
default:
return rect.bottom_left();
}
};
auto p1 = first_point_for_edge(edge, rect);
auto p2 = second_point_for_edge(edge, rect);
if (border_style.has_value() && border_style.value()->to_string() == "inset") {
auto top_left_color = Color::from_rgb(0x5a5a5a);
auto bottom_right_color = Color::from_rgb(0x888888);
color = (edge == Edge::Left || edge == Edge::Top) ? top_left_color : bottom_right_color;
} else if (border_style.has_value() && border_style.value()->to_string() == "outset") {
auto top_left_color = Color::from_rgb(0x888888);
auto bottom_right_color = Color::from_rgb(0x5a5a5a);
color = (edge == Edge::Left || edge == Edge::Top) ? top_left_color : bottom_right_color;
}
bool dotted = border_style.has_value() && border_style.value()->to_string() == "dotted";
auto draw_line = [&](auto& p1, auto& p2) {
context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, dotted);
};
auto width_for = [&](CSS::PropertyID property_id) -> float {
auto width = style().property(property_id);
if (!width.has_value())
return 0;
return width.value()->to_length().to_px();
};
float p1_step = 0;
float p2_step = 0;
switch (edge) {
case Edge::Top:
p1_step = width_for(CSS::PropertyID::BorderLeftWidth) / (float)int_width;
p2_step = width_for(CSS::PropertyID::BorderRightWidth) / (float)int_width;
for (int i = 0; i < int_width; ++i) {
draw_line(p1, p2);
p1.move_by(p1_step, 1);
p2.move_by(-p2_step, 1);
}
break;
case Edge::Right:
p1_step = width_for(CSS::PropertyID::BorderTopWidth) / (float)int_width;
p2_step = width_for(CSS::PropertyID::BorderBottomWidth) / (float)int_width;
for (int i = int_width - 1; i >= 0; --i) {
draw_line(p1, p2);
p1.move_by(-1, p1_step);
p2.move_by(-1, -p2_step);
}
break;
case Edge::Bottom:
p1_step = width_for(CSS::PropertyID::BorderLeftWidth) / (float)int_width;
p2_step = width_for(CSS::PropertyID::BorderRightWidth) / (float)int_width;
for (int i = int_width - 1; i >= 0; --i) {
draw_line(p1, p2);
p1.move_by(p1_step, -1);
p2.move_by(-p2_step, -1);
}
break;
case Edge::Left:
p1_step = width_for(CSS::PropertyID::BorderTopWidth) / (float)int_width;
p2_step = width_for(CSS::PropertyID::BorderBottomWidth) / (float)int_width;
for (int i = 0; i < int_width; ++i) {
draw_line(p1, p2);
p1.move_by(1, p1_step);
p2.move_by(1, -p2_step);
}
break;
}
}
void LayoutBox::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
if (node() && document().inspected_node() == node())
context.painter().draw_rect(enclosing_int_rect(m_rect), Color::Magenta);
Gfx::FloatRect 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());
if (!is_body()) {
auto bgcolor = style().property(CSS::PropertyID::BackgroundColor);
if (bgcolor.has_value() && bgcolor.value()->is_color()) {
context.painter().fill_rect(enclosing_int_rect(padded_rect), bgcolor.value()->to_color(document()));
}
auto bgimage = style().property(CSS::PropertyID::BackgroundImage);
if (bgimage.has_value() && bgimage.value()->is_image()) {
auto& image_value = static_cast<const ImageStyleValue&>(*bgimage.value());
if (image_value.bitmap()) {
context.painter().draw_tiled_bitmap(enclosing_int_rect(padded_rect), *image_value.bitmap());
}
}
}
Gfx::FloatRect bordered_rect;
bordered_rect.set_x(padded_rect.x() - box_model().border().left.to_px());
bordered_rect.set_width(padded_rect.width() + box_model().border().left.to_px() + box_model().border().right.to_px());
bordered_rect.set_y(padded_rect.y() - box_model().border().top.to_px());
bordered_rect.set_height(padded_rect.height() + box_model().border().top.to_px() + box_model().border().bottom.to_px());
paint_border(context, Edge::Left, bordered_rect, CSS::PropertyID::BorderLeftStyle, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftWidth);
paint_border(context, Edge::Right, bordered_rect, CSS::PropertyID::BorderRightStyle, CSS::PropertyID::BorderRightColor, CSS::PropertyID::BorderRightWidth);
paint_border(context, Edge::Top, bordered_rect, CSS::PropertyID::BorderTopStyle, CSS::PropertyID::BorderTopColor, CSS::PropertyID::BorderTopWidth);
paint_border(context, Edge::Bottom, bordered_rect, CSS::PropertyID::BorderBottomStyle, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomWidth);
LayoutNodeWithStyleAndBoxModelMetrics::render(context);
}
HitTestResult LayoutBox::hit_test(const Gfx::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.x(), position.y()) ? this : nullptr };
for_each_child([&](auto& child) {
auto child_result = child.hit_test(position);
if (child_result.layout_node)
result = child_result;
});
return result;
}
void LayoutBox::set_needs_display()
{
auto* frame = document().frame();
ASSERT(frame);
if (!is_inline()) {
const_cast<Frame*>(frame)->set_needs_display(enclosing_int_rect(rect()));
return;
}
LayoutNode::set_needs_display();
}
bool LayoutBox::is_body() const
{
return node() && node() == document().body();
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibGfx/FloatRect.h>
#include <LibWeb/Layout/LayoutNode.h>
namespace Web {
class LayoutBox : public LayoutNodeWithStyleAndBoxModelMetrics {
public:
const Gfx::FloatRect& rect() const { return m_rect; }
Gfx::FloatRect& rect() { return m_rect; }
void set_rect(const Gfx::FloatRect& rect) { m_rect = rect; }
float x() const { return rect().x(); }
float y() const { return rect().y(); }
float width() const { return rect().width(); }
float height() const { return rect().height(); }
Gfx::FloatSize size() const { return rect().size(); }
Gfx::FloatPoint position() const { return rect().location(); }
virtual HitTestResult hit_test(const Gfx::Point& position) const override;
virtual void set_needs_display() override;
bool is_body() const;
protected:
LayoutBox(const Node* node, NonnullRefPtr<StyleProperties> style)
: LayoutNodeWithStyleAndBoxModelMetrics(node, move(style))
{
}
virtual void render(RenderingContext&) override;
private:
virtual bool is_box() const override { return true; }
enum class Edge {
Top,
Right,
Bottom,
Left,
};
void paint_border(RenderingContext&, Edge, const Gfx::FloatRect&, CSS::PropertyID style_property_id, CSS::PropertyID color_property_id, CSS::PropertyID width_property_id);
Gfx::FloatRect m_rect;
};
template<>
inline bool is<LayoutBox>(const LayoutNode& node)
{
return node.is_box();
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutBreak.h>
namespace Web {
LayoutBreak::LayoutBreak(const HTMLBRElement& element)
: LayoutNodeWithStyleAndBoxModelMetrics(&element, StyleProperties::create())
{
set_inline(true);
}
LayoutBreak::~LayoutBreak()
{
}
void LayoutBreak::split_into_lines(LayoutBlock& block)
{
block.add_line_box();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/DOM/HTMLBRElement.h>
#include <LibWeb/Layout/LayoutNode.h>
namespace Web {
class LayoutBreak final : public LayoutNodeWithStyleAndBoxModelMetrics {
public:
explicit LayoutBreak(const HTMLBRElement&);
virtual ~LayoutBreak() override;
const HTMLBRElement& node() const { return to<HTMLBRElement>(*LayoutNode::node()); }
private:
virtual const char* class_name() const override { return "LayoutBreak"; }
virtual void split_into_lines(LayoutBlock&) override;
};
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/Dump.h>
#include <LibWeb/Frame.h>
#include <LibWeb/Layout/LayoutDocument.h>
#include <LibWeb/Layout/LayoutImage.h>
namespace Web {
LayoutDocument::LayoutDocument(const Document& document, NonnullRefPtr<StyleProperties> style)
: LayoutBlock(&document, move(style))
{
}
LayoutDocument::~LayoutDocument()
{
}
void LayoutDocument::layout()
{
ASSERT(document().frame());
rect().set_width(document().frame()->size().width());
LayoutNode::layout();
ASSERT(!children_are_inline());
int lowest_bottom = 0;
for_each_child([&](auto& child) {
ASSERT(is<LayoutBlock>(child));
auto& child_block = to<LayoutBlock>(child);
if (child_block.rect().bottom() > lowest_bottom)
lowest_bottom = child_block.rect().bottom();
});
rect().set_bottom(lowest_bottom);
}
void LayoutDocument::did_set_viewport_rect(Badge<Frame>, const Gfx::Rect& a_viewport_rect)
{
Gfx::FloatRect viewport_rect(a_viewport_rect.x(), a_viewport_rect.y(), a_viewport_rect.width(), a_viewport_rect.height());
for_each_in_subtree_of_type<LayoutImage>([&](auto& layout_image) {
const_cast<HTMLImageElement&>(layout_image.node()).set_volatile({}, !viewport_rect.intersects(layout_image.rect()));
return IterationDecision::Continue;
});
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/LayoutBlock.h>
namespace Web {
class LayoutDocument final : public LayoutBlock {
public:
explicit LayoutDocument(const Document&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutDocument() override;
const Document& node() const { return static_cast<const Document&>(*LayoutNode::node()); }
virtual const char* class_name() const override { return "LayoutDocument"; }
virtual void layout() override;
const LayoutRange& selection() const { return m_selection; }
LayoutRange& selection() { return m_selection; }
void did_set_viewport_rect(Badge<Frame>, const Gfx::Rect&);
private:
LayoutRange m_selection;
};
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGfx/Font.h>
#include <LibGfx/StylePainter.h>
#include <LibGUI/Painter.h>
#include <LibWeb/Layout/LayoutImage.h>
namespace Web {
LayoutImage::LayoutImage(const HTMLImageElement& element, NonnullRefPtr<StyleProperties> style)
: LayoutReplaced(element, move(style))
{
}
LayoutImage::~LayoutImage()
{
}
void LayoutImage::layout()
{
if (node().preferred_width() && node().preferred_height()) {
rect().set_width(node().preferred_width());
rect().set_height(node().preferred_height());
} else if (renders_as_alt_text()) {
auto& font = Gfx::Font::default_font();
auto alt = node().alt();
if (alt.is_empty())
alt = node().src();
rect().set_width(font.width(alt) + 16);
rect().set_height(font.glyph_height() + 16);
} else {
rect().set_width(16);
rect().set_height(16);
}
LayoutReplaced::layout();
}
void LayoutImage::render(RenderingContext& context)
{
if (!is_visible())
return;
// FIXME: This should be done at a different level. Also rect() does not include padding etc!
if (!context.viewport_rect().intersects(enclosing_int_rect(rect())))
return;
if (renders_as_alt_text()) {
context.painter().set_font(Gfx::Font::default_font());
Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
auto alt = node().alt();
if (alt.is_empty())
alt = node().src();
context.painter().draw_text(enclosing_int_rect(rect()), alt, Gfx::TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), Gfx::TextElision::Right);
} else if (node().bitmap())
context.painter().draw_scaled_bitmap(enclosing_int_rect(rect()), *node().bitmap(), node().bitmap()->rect());
LayoutReplaced::render(context);
}
bool LayoutImage::renders_as_alt_text() const
{
return !node().image_decoder();
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/DOM/HTMLImageElement.h>
#include <LibWeb/Layout/LayoutReplaced.h>
namespace Web {
class HTMLImageElement;
class LayoutImage : public LayoutReplaced {
public:
LayoutImage(const HTMLImageElement&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutImage() override;
virtual void layout() override;
virtual void render(RenderingContext&) override;
const HTMLImageElement& node() const { return static_cast<const HTMLImageElement&>(LayoutReplaced::node()); }
bool renders_as_alt_text() const;
private:
virtual const char* class_name() const override { return "LayoutImage"; }
virtual bool is_image() const override { return true; }
};
template<>
inline bool is<LayoutImage>(const LayoutNode& node)
{
return node.is_image();
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutInline.h>
namespace Web {
LayoutInline::LayoutInline(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutNodeWithStyleAndBoxModelMetrics(&element, move(style))
{
set_inline(true);
}
LayoutInline::~LayoutInline()
{
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBox.h>
namespace Web {
class LayoutBlock;
class LayoutInline : public LayoutNodeWithStyleAndBoxModelMetrics {
public:
LayoutInline(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutInline() override;
virtual const char* class_name() const override { return "LayoutInline"; }
};
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/Layout/LayoutListItem.h>
#include <LibWeb/Layout/LayoutListItemMarker.h>
namespace Web {
LayoutListItem::LayoutListItem(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutBlock(&element, move(style))
{
}
LayoutListItem::~LayoutListItem()
{
}
void LayoutListItem::layout()
{
LayoutBlock::layout();
if (!m_marker) {
m_marker = adopt(*new LayoutListItemMarker);
if (first_child())
m_marker->set_inline(first_child()->is_inline());
append_child(*m_marker);
}
Gfx::FloatRect marker_rect { x() - 8, y(), 4, height() };
m_marker->set_rect(marker_rect);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutBlock.h>
namespace Web {
class LayoutListItemMarker;
class LayoutListItem final : public LayoutBlock {
public:
LayoutListItem(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutListItem() override;
virtual void layout() override;
private:
virtual const char* class_name() const override { return "LayoutListItem"; }
RefPtr<LayoutListItemMarker> m_marker;
};
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGUI/Painter.h>
#include <LibWeb/Layout/LayoutListItemMarker.h>
namespace Web {
LayoutListItemMarker::LayoutListItemMarker()
: LayoutBox(nullptr, StyleProperties::create())
{
}
LayoutListItemMarker::~LayoutListItemMarker()
{
}
void LayoutListItemMarker::render(RenderingContext& context)
{
Gfx::Rect bullet_rect { 0, 0, 4, 4 };
bullet_rect.center_within(enclosing_int_rect(rect()));
// FIXME: It would be nicer to not have to go via the parent here to get our inherited style.
auto color = parent()->style().color_or_fallback(CSS::PropertyID::Color, document(), context.palette().base_text());
context.painter().fill_rect(bullet_rect, color);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBox.h>
namespace Web {
class LayoutListItemMarker final : public LayoutBox {
public:
LayoutListItemMarker();
virtual ~LayoutListItemMarker() override;
virtual void render(RenderingContext&) override;
private:
virtual const char* class_name() const override { return "LayoutListItemMarker"; }
};
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGUI/Painter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Frame.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutNode.h>
namespace Web {
LayoutNode::LayoutNode(const Node* node)
: m_node(node)
{
if (m_node)
m_node->set_layout_node({}, this);
}
LayoutNode::~LayoutNode()
{
if (m_node && m_node->layout_node() == this)
m_node->set_layout_node({}, nullptr);
}
void LayoutNode::layout()
{
for_each_child([](auto& child) {
child.layout();
});
}
const LayoutBlock* LayoutNode::containing_block() const
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (is<LayoutBlock>(*ancestor))
return to<LayoutBlock>(ancestor);
}
return nullptr;
}
void LayoutNode::render(RenderingContext& context)
{
if (!is_visible())
return;
// TODO: render our border
for_each_child([&](auto& child) {
child.render(context);
});
}
HitTestResult LayoutNode::hit_test(const Gfx::Point& position) const
{
HitTestResult result;
for_each_child([&](auto& child) {
auto child_result = child.hit_test(position);
if (child_result.layout_node)
result = child_result;
});
return result;
}
const Document& LayoutNode::document() const
{
if (is_anonymous())
return parent()->document();
return node()->document();
}
Document& LayoutNode::document()
{
if (is_anonymous())
return parent()->document();
// FIXME: Remove this const_cast once we give up on the idea of a const link from layout tree to DOM tree.
return const_cast<Node*>(node())->document();
}
const LayoutDocument& LayoutNode::root() const
{
ASSERT(document().layout_node());
return *document().layout_node();
}
LayoutDocument& LayoutNode::root()
{
ASSERT(document().layout_node());
return *document().layout_node();
}
void LayoutNode::split_into_lines(LayoutBlock& container)
{
for_each_child([&](auto& child) {
if (child.is_inline()) {
child.split_into_lines(container);
} else {
// FIXME: Support block children of inlines.
}
});
}
void LayoutNode::set_needs_display()
{
auto* frame = document().frame();
ASSERT(frame);
if (auto* block = containing_block()) {
block->for_each_fragment([&](auto& fragment) {
if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
const_cast<Frame*>(frame)->set_needs_display(enclosing_int_rect(fragment.rect()));
}
return IterationDecision::Continue;
});
}
}
Gfx::FloatPoint LayoutNode::box_type_agnostic_position() const
{
if (is_box())
return to<LayoutBox>(*this).position();
ASSERT(is_inline());
Gfx::FloatPoint position;
if (auto* block = containing_block()) {
block->for_each_fragment([&](auto& fragment) {
if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
position = fragment.rect().location();
return IterationDecision::Break;
}
return IterationDecision::Continue;
});
}
return position;
}
}

View file

@ -0,0 +1,321 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/Vector.h>
#include <LibGfx/FloatRect.h>
#include <LibGfx/Rect.h>
#include <LibWeb/CSS/StyleProperties.h>
#include <LibWeb/Layout/BoxModelMetrics.h>
#include <LibWeb/Layout/LayoutPosition.h>
#include <LibWeb/RenderingContext.h>
#include <LibWeb/TreeNode.h>
namespace Web {
class Document;
class Element;
class LayoutBlock;
class LayoutDocument;
class LayoutNode;
class LayoutNodeWithStyle;
class LineBoxFragment;
class Node;
struct HitTestResult {
RefPtr<LayoutNode> layout_node;
int index_in_node { 0 };
};
class LayoutNode : public TreeNode<LayoutNode> {
public:
virtual ~LayoutNode();
virtual HitTestResult hit_test(const Gfx::Point&) const;
bool is_anonymous() const { return !m_node; }
const Node* node() const { return m_node; }
Document& document();
const Document& document() const;
const LayoutDocument& root() const;
LayoutDocument& root();
template<typename Callback>
inline void for_each_child(Callback callback) const
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
template<typename Callback>
inline void for_each_child(Callback callback)
{
for (auto* node = first_child(); node; node = node->next_sibling())
callback(*node);
}
virtual const char* class_name() const { return "LayoutNode"; }
virtual bool is_text() const { return false; }
virtual bool is_block() const { return false; }
virtual bool is_replaced() const { return false; }
virtual bool is_widget() const { return false; }
virtual bool is_image() const { return false; }
virtual bool is_box() const { return false; }
virtual bool is_table() const { return false; }
virtual bool is_table_row() const { return false; }
virtual bool is_table_cell() const { return false; }
bool has_style() const { return m_has_style; }
bool is_inline() const { return m_inline; }
void set_inline(bool b) { m_inline = b; }
virtual void layout();
virtual void render(RenderingContext&);
const LayoutBlock* containing_block() const;
virtual LayoutNode& inline_wrapper() { return *this; }
const StyleProperties& style() const;
LayoutNodeWithStyle* parent();
const LayoutNodeWithStyle* parent() const;
void inserted_into(LayoutNode&) {}
void removed_from(LayoutNode&) {}
virtual void split_into_lines(LayoutBlock& container);
bool is_visible() const { return m_visible; }
void set_visible(bool visible) { m_visible = visible; }
virtual void set_needs_display();
bool children_are_inline() const { return m_children_are_inline; }
void set_children_are_inline(bool value) { m_children_are_inline = value; }
template<typename U>
const U* next_sibling_of_type() const;
template<typename U>
U* next_sibling_of_type();
template<typename T>
const T* first_child_of_type() const;
template<typename T>
T* first_child_of_type();
template<typename T>
const T* first_ancestor_of_type() const;
template<typename T>
T* first_ancestor_of_type();
Gfx::FloatPoint box_type_agnostic_position() const;
protected:
explicit LayoutNode(const Node*);
private:
friend class LayoutNodeWithStyle;
const Node* m_node { nullptr };
bool m_inline { false };
bool m_has_style { false };
bool m_visible { true };
bool m_children_are_inline { false };
};
class LayoutNodeWithStyle : public LayoutNode {
public:
virtual ~LayoutNodeWithStyle() override {}
const StyleProperties& style() const { return m_style; }
void set_style(const StyleProperties& style) { m_style = style; }
protected:
explicit LayoutNodeWithStyle(const Node* node, NonnullRefPtr<StyleProperties> style)
: LayoutNode(node)
, m_style(move(style))
{
m_has_style = true;
}
private:
NonnullRefPtr<StyleProperties> m_style;
};
class LayoutNodeWithStyleAndBoxModelMetrics : public LayoutNodeWithStyle {
public:
BoxModelMetrics& box_model() { return m_box_model; }
const BoxModelMetrics& box_model() const { return m_box_model; }
protected:
LayoutNodeWithStyleAndBoxModelMetrics(const Node* node, NonnullRefPtr<StyleProperties> style)
: LayoutNodeWithStyle(node, move(style))
{
}
private:
BoxModelMetrics m_box_model;
};
inline const StyleProperties& LayoutNode::style() const
{
if (m_has_style)
return static_cast<const LayoutNodeWithStyle*>(this)->style();
return parent()->style();
}
inline const LayoutNodeWithStyle* LayoutNode::parent() const
{
return static_cast<const LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
}
inline LayoutNodeWithStyle* LayoutNode::parent()
{
return static_cast<LayoutNodeWithStyle*>(TreeNode<LayoutNode>::parent());
}
template<typename T>
inline bool is(const LayoutNode&)
{
return false;
}
template<typename T>
inline bool is(const LayoutNode* node)
{
return !node || is<T>(*node);
}
template<>
inline bool is<LayoutNode>(const LayoutNode&)
{
return true;
}
template<>
inline bool is<LayoutNodeWithStyle>(const LayoutNode& node)
{
return node.has_style();
}
template<typename T>
inline const T& to(const LayoutNode& node)
{
ASSERT(is<T>(node));
return static_cast<const T&>(node);
}
template<typename T>
inline T* to(LayoutNode* node)
{
ASSERT(is<T>(node));
return static_cast<T*>(node);
}
template<typename T>
inline const T* to(const LayoutNode* node)
{
ASSERT(is<T>(node));
return static_cast<const T*>(node);
}
template<typename T>
inline T& to(LayoutNode& node)
{
ASSERT(is<T>(node));
return static_cast<T&>(node);
}
template<typename T>
inline const T* LayoutNode::next_sibling_of_type() const
{
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
if (is<T>(*sibling))
return &to<T>(*sibling);
}
return nullptr;
}
template<typename T>
inline T* LayoutNode::next_sibling_of_type()
{
for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) {
if (is<T>(*sibling))
return &to<T>(*sibling);
}
return nullptr;
}
template<typename T>
inline const T* LayoutNode::first_child_of_type() const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (is<T>(*child))
return &to<T>(*child);
}
return nullptr;
}
template<typename T>
inline T* LayoutNode::first_child_of_type()
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (is<T>(*child))
return &to<T>(*child);
}
return nullptr;
}
template<typename T>
inline const T* LayoutNode::first_ancestor_of_type() const
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (is<T>(*ancestor))
return &to<T>(*ancestor);
}
return nullptr;
}
template<typename T>
inline T* LayoutNode::first_ancestor_of_type()
{
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
if (is<T>(*ancestor))
return &to<T>(*ancestor);
}
return nullptr;
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/RefPtr.h>
namespace Web {
class LayoutNode;
struct LayoutPosition {
bool operator>=(const LayoutPosition& other) const
{
if (layout_node == other.layout_node)
return index_in_node >= other.index_in_node;
// FIXME: Implement.
return true;
}
bool operator<=(const LayoutPosition& other) const
{
if (layout_node == other.layout_node)
return index_in_node <= other.index_in_node;
// FIXME: Implement.
return false;
}
RefPtr<LayoutNode> layout_node;
int index_in_node { 0 };
};
class LayoutRange {
public:
LayoutRange() {}
LayoutRange(const LayoutPosition& start, const LayoutPosition& end)
: m_start(start)
, m_end(end)
{
}
bool is_valid() const { return m_start.layout_node && m_end.layout_node; }
void set(const LayoutPosition& start, const LayoutPosition& end)
{
m_start = start;
m_end = end;
}
void set_start(const LayoutPosition& start) { m_start = start; }
void set_end(const LayoutPosition& end) { m_end = end; }
const LayoutPosition& start() const { return m_start; }
const LayoutPosition& end() const { return m_end; }
private:
LayoutPosition m_start;
LayoutPosition m_end;
};
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutReplaced.h>
namespace Web {
LayoutReplaced::LayoutReplaced(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutBox(&element, move(style))
{
// FIXME: Allow non-inline replaced elements.
set_inline(true);
}
LayoutReplaced::~LayoutReplaced()
{
}
void LayoutReplaced::split_into_lines(LayoutBlock& container)
{
layout();
auto* line_box = &container.ensure_last_line_box();
if (line_box->width() > 0 && line_box->width() + width() > container.width())
line_box = &container.add_line_box();
line_box->add_fragment(*this, 0, 0, width(), height());
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutBox.h>
namespace Web {
class LayoutReplaced : public LayoutBox {
public:
LayoutReplaced(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutReplaced() override;
const Element& node() const { return to<Element>(*LayoutNode::node()); }
virtual bool is_replaced() const final { return true; }
private:
virtual const char* class_name() const override { return "LayoutReplaced"; }
virtual void split_into_lines(LayoutBlock& container) override;
};
template<>
inline bool is<LayoutReplaced>(const LayoutNode& node)
{
return node.is_replaced();
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutTable.h>
#include <LibWeb/Layout/LayoutTableRow.h>
namespace Web {
LayoutTable::LayoutTable(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutBlock(&element, move(style))
{
}
LayoutTable::~LayoutTable()
{
}
void LayoutTable::layout()
{
LayoutBlock::layout();
}
LayoutTableRow* LayoutTable::first_row()
{
return first_child_of_type<LayoutTableRow>();
}
const LayoutTableRow* LayoutTable::first_row() const
{
return first_child_of_type<LayoutTableRow>();
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBlock.h>
namespace Web {
class LayoutTableRow;
class LayoutTable final : public LayoutBlock {
public:
LayoutTable(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutTable() override;
virtual void layout() override;
LayoutTableRow* first_row();
const LayoutTableRow* first_row() const;
private:
virtual bool is_table() const override { return true; }
virtual const char* class_name() const override { return "LayoutTable"; }
};
template<>
inline bool is<LayoutTable>(const LayoutNode& node)
{
return node.is_table();
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutTableCell.h>
namespace Web {
LayoutTableCell::LayoutTableCell(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutBlock(&element, move(style))
{
}
LayoutTableCell::~LayoutTableCell()
{
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBlock.h>
namespace Web {
class LayoutTableCell final : public LayoutBlock {
public:
LayoutTableCell(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutTableCell() override;
LayoutTableCell* next_cell() { return next_sibling_of_type<LayoutTableCell>(); }
const LayoutTableCell* next_cell() const { return next_sibling_of_type<LayoutTableCell>(); }
private:
virtual bool is_table_cell() const override { return true; }
virtual const char* class_name() const override { return "LayoutTableCell"; }
};
template<>
inline bool is<LayoutTableCell>(const LayoutNode& node)
{
return node.is_table_cell();
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/LayoutTableCell.h>
#include <LibWeb/Layout/LayoutTableRow.h>
namespace Web {
LayoutTableRow::LayoutTableRow(const Element& element, NonnullRefPtr<StyleProperties> style)
: LayoutBox(&element, move(style))
{
}
LayoutTableRow::~LayoutTableRow()
{
}
void LayoutTableRow::layout()
{
LayoutBox::layout();
}
LayoutTableCell* LayoutTableRow::first_cell()
{
return first_child_of_type<LayoutTableCell>();
}
const LayoutTableCell* LayoutTableRow::first_cell() const
{
return first_child_of_type<LayoutTableCell>();
}
LayoutTableRow* LayoutTableRow::next_row()
{
return next_sibling_of_type<LayoutTableRow>();
}
const LayoutTableRow* LayoutTableRow::next_row() const
{
return next_sibling_of_type<LayoutTableRow>();
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutBox.h>
namespace Web {
class LayoutTableCell;
class LayoutTableRow final : public LayoutBox {
public:
LayoutTableRow(const Element&, NonnullRefPtr<StyleProperties>);
virtual ~LayoutTableRow() override;
virtual void layout() override;
LayoutTableCell* first_cell();
const LayoutTableCell* first_cell() const;
LayoutTableRow* next_row();
const LayoutTableRow* next_row() const;
private:
virtual bool is_table_row() const override { return true; }
virtual const char* class_name() const override { return "LayoutTableRow"; }
};
template<>
inline bool is<LayoutTableRow>(const LayoutNode& node)
{
return node.is_table_row();
}
}

View file

@ -0,0 +1,237 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StringBuilder.h>
#include <AK/Utf8View.h>
#include <LibCore/DirIterator.h>
#include <LibGfx/Font.h>
#include <LibGUI/Painter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutText.h>
#include <ctype.h>
namespace Web {
LayoutText::LayoutText(const Text& text)
: LayoutNode(&text)
{
set_inline(true);
}
LayoutText::~LayoutText()
{
}
static bool is_all_whitespace(const String& string)
{
for (size_t i = 0; i < string.length(); ++i) {
if (!isspace(string[i]))
return false;
}
return true;
}
const String& LayoutText::text_for_style(const StyleProperties& style) const
{
static String one_space = " ";
if (is_all_whitespace(node().data())) {
if (style.string_or_fallback(CSS::PropertyID::WhiteSpace, "normal") == "normal")
return one_space;
}
return node().data();
}
void LayoutText::render_fragment(RenderingContext& context, const LineBoxFragment& fragment) const
{
auto& painter = context.painter();
painter.set_font(style().font());
auto background_color = style().property(CSS::PropertyID::BackgroundColor);
if (background_color.has_value() && background_color.value()->is_color())
painter.fill_rect(enclosing_int_rect(fragment.rect()), background_color.value()->to_color(document()));
auto color = style().color_or_fallback(CSS::PropertyID::Color, document(), context.palette().base_text());
auto text_decoration = style().string_or_fallback(CSS::PropertyID::TextDecoration, "none");
if (document().inspected_node() == &node())
context.painter().draw_rect(enclosing_int_rect(fragment.rect()), Color::Magenta);
bool is_underline = text_decoration == "underline";
if (is_underline)
painter.draw_line(enclosing_int_rect(fragment.rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.rect()).bottom_right().translated(0, 1), color);
painter.draw_text(enclosing_int_rect(fragment.rect()), m_text_for_rendering.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, color);
}
template<typename Callback>
void LayoutText::for_each_word(Callback callback) const
{
Utf8View view(m_text_for_rendering);
if (view.is_empty())
return;
auto start_of_word = view.begin();
auto commit_word = [&](auto it) {
int start = view.byte_offset_of(start_of_word);
int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_word);
if (length > 0) {
callback(view.substring_view(start, length), start, length);
}
start_of_word = it;
};
bool last_was_space = isspace(*view.begin());
for (auto it = view.begin(); it != view.end();) {
bool is_space = isspace(*it);
if (is_space == last_was_space) {
++it;
continue;
}
last_was_space = is_space;
commit_word(it);
++it;
}
if (start_of_word != view.end())
commit_word(view.end());
}
void LayoutText::split_preformatted_into_lines(LayoutBlock& container)
{
auto& font = style().font();
auto& line_boxes = container.line_boxes();
m_text_for_rendering = node().data();
Utf8View view(m_text_for_rendering);
if (view.is_empty())
return;
auto start_of_line = view.begin();
auto commit_line = [&](auto it) {
int start = view.byte_offset_of(start_of_line);
int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_line);
if (length > 0)
line_boxes.last().add_fragment(*this, start, length, font.width(view), font.glyph_height());
};
bool last_was_newline = false;
for (auto it = view.begin(); it != view.end();) {
bool did_commit = false;
if (*it == '\n') {
commit_line(it);
line_boxes.append(LineBox());
did_commit = true;
last_was_newline = true;
} else {
last_was_newline = false;
}
++it;
if (did_commit)
start_of_line = it;
}
if (start_of_line != view.end() || last_was_newline)
commit_line(view.end());
}
void LayoutText::split_into_lines(LayoutBlock& container)
{
auto& font = style().font();
float space_width = font.glyph_width(' ') + font.glyph_spacing();
auto& line_boxes = container.line_boxes();
if (line_boxes.is_empty())
line_boxes.append(LineBox());
float available_width = container.width() - line_boxes.last().width();
if (style().string_or_fallback(CSS::PropertyID::WhiteSpace, "normal") == "pre") {
split_preformatted_into_lines(container);
return;
}
// Collapse whitespace into single spaces
auto utf8_view = Utf8View(node().data());
StringBuilder builder(node().data().length());
for (auto it = utf8_view.begin(); it != utf8_view.end(); ++it) {
if (!isspace(*it)) {
builder.append(utf8_view.as_string().characters_without_null_termination() + utf8_view.byte_offset_of(it), it.codepoint_length_in_bytes());
} else {
builder.append(' ');
auto prev = it;
while (it != utf8_view.end() && isspace(*it)) {
prev = it;
++it;
}
it = prev;
}
}
m_text_for_rendering = builder.to_string();
struct Word {
Utf8View view;
int start;
int length;
};
Vector<Word> words;
for_each_word([&](const Utf8View& view, int start, int length) {
words.append({ Utf8View(view), start, length });
});
for (size_t i = 0; i < words.size(); ++i) {
auto& word = words[i];
float word_width;
bool is_whitespace = isspace(*word.view.begin());
if (is_whitespace)
word_width = space_width;
else
word_width = font.width(word.view) + font.glyph_spacing();
if (line_boxes.last().width() > 0 && word_width > available_width) {
line_boxes.append(LineBox());
available_width = container.width();
}
if (is_whitespace && line_boxes.last().fragments().is_empty())
continue;
line_boxes.last().add_fragment(*this, word.start, is_whitespace ? 1 : word.length, word_width, font.glyph_height());
available_width -= word_width;
if (available_width < 0) {
line_boxes.append(LineBox());
available_width = container.width();
}
}
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/DOM/Text.h>
#include <LibWeb/Layout/LayoutNode.h>
namespace Web {
class LineBoxFragment;
class LayoutText : public LayoutNode {
public:
explicit LayoutText(const Text&);
virtual ~LayoutText() override;
const Text& node() const { return static_cast<const Text&>(*LayoutNode::node()); }
const String& text_for_style(const StyleProperties&) const;
const String& text_for_rendering() const { return m_text_for_rendering; }
virtual const char* class_name() const override { return "LayoutText"; }
virtual bool is_text() const final { return true; }
void render_fragment(RenderingContext&, const LineBoxFragment&) const;
virtual void split_into_lines(LayoutBlock& container) override;
const StyleProperties& style() const { return parent()->style(); }
private:
void split_preformatted_into_lines(LayoutBlock& container);
template<typename Callback>
void for_each_word(Callback) const;
String m_text_for_rendering;
};
template<>
inline bool is<LayoutText>(const LayoutNode& node)
{
return node.is_text();
}
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ParentNode.h>
#include <LibWeb/Layout/LayoutNode.h>
#include <LibWeb/Layout/LayoutTable.h>
#include <LibWeb/Layout/LayoutText.h>
#include <LibWeb/Layout/LayoutTreeBuilder.h>
namespace Web {
LayoutTreeBuilder::LayoutTreeBuilder()
{
}
static RefPtr<LayoutNode> create_layout_tree(Node& node, const StyleProperties* parent_style)
{
auto layout_node = node.create_layout_node(parent_style);
if (!layout_node)
return nullptr;
if (!node.has_children())
return layout_node;
NonnullRefPtrVector<LayoutNode> layout_children;
bool have_inline_children = false;
bool have_block_children = false;
to<ParentNode>(node).for_each_child([&](Node& child) {
auto layout_child = create_layout_tree(child, &layout_node->style());
if (!layout_child)
return;
if (layout_child->is_inline())
have_inline_children = true;
if (layout_child->is_block())
have_block_children = true;
layout_children.append(layout_child.release_nonnull());
});
for (auto& layout_child : layout_children) {
if (have_block_children && have_inline_children && layout_child.is_inline()) {
if (is<LayoutText>(layout_child) && to<LayoutText>(layout_child).text_for_style(*parent_style) == " ")
continue;
layout_node->inline_wrapper().append_child(layout_child);
} else {
layout_node->append_child(layout_child);
}
}
if (have_inline_children && !have_block_children)
layout_node->set_children_are_inline(true);
// FIXME: This is really hackish. Some layout nodes don't care about inline children.
if (is<LayoutTable>(layout_node))
layout_node->set_children_are_inline(false);
return layout_node;
}
RefPtr<LayoutNode> LayoutTreeBuilder::build(Node& node)
{
// FIXME: Support building partial trees.
ASSERT(is<Document>(node));
return create_layout_tree(node, nullptr);
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/RefPtr.h>
namespace Web {
class Node;
class LayoutNode;
class LayoutTreeBuilder {
public:
LayoutTreeBuilder();
RefPtr<LayoutNode> build(Node&);
};
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibGfx/Font.h>
#include <LibGfx/StylePainter.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Widget.h>
#include <LibWeb/Layout/LayoutWidget.h>
namespace Web {
LayoutWidget::LayoutWidget(const Element& element, GUI::Widget& widget)
: LayoutReplaced(element, StyleProperties::create())
, m_widget(widget)
{
}
LayoutWidget::~LayoutWidget()
{
widget().remove_from_parent();
}
void LayoutWidget::layout()
{
rect().set_size(widget().width(), widget().height());
LayoutReplaced::layout();
widget().move_to(rect().x(), rect().y());
}
void LayoutWidget::render(RenderingContext& context)
{
LayoutReplaced::render(context);
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibWeb/Layout/LayoutReplaced.h>
namespace Web {
class LayoutWidget : public LayoutReplaced {
public:
LayoutWidget(const Element&, GUI::Widget&);
virtual ~LayoutWidget() override;
virtual void layout() override;
virtual void render(RenderingContext&) override;
GUI::Widget& widget() { return m_widget; }
const GUI::Widget& widget() const { return m_widget; }
virtual bool is_widget() const final { return true; }
private:
virtual const char* class_name() const override { return "LayoutWidget"; }
NonnullRefPtr<GUI::Widget> m_widget;
};
template<>
inline bool is<LayoutWidget>(const LayoutNode& node)
{
return node.is_widget();
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Utf8View.h>
#include <LibWeb/Layout/LayoutNode.h>
#include <LibWeb/Layout/LayoutText.h>
#include <LibWeb/Layout/LineBox.h>
#include <ctype.h>
namespace Web {
void LineBox::add_fragment(const LayoutNode& layout_node, int start, int length, int width, int height)
{
bool text_align_is_justify = layout_node.style().string_or_fallback(CSS::PropertyID::TextAlign, "left") == "justify";
if (!text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node) {
// The fragment we're adding is from the last LayoutNode on the line.
// Expand the last fragment instead of adding a new one with the same LayoutNode.
m_fragments.last().m_length = (start - m_fragments.last().m_start) + length;
m_fragments.last().m_rect.set_width(m_fragments.last().m_rect.width() + width);
} else {
m_fragments.empend(layout_node, start, length, Gfx::FloatRect(m_width, 0, width, height));
}
m_width += width;
}
void LineBox::trim_trailing_whitespace()
{
while (!m_fragments.is_empty() && m_fragments.last().is_justifiable_whitespace()) {
auto fragment = m_fragments.take_last();
m_width -= fragment.width();
}
if (m_fragments.is_empty())
return;
auto last_text = m_fragments.last().text();
if (last_text.is_null())
return;
auto& last_fragment = m_fragments.last();
int space_width = last_fragment.layout_node().style().font().glyph_width(' ');
while (last_fragment.length() && isspace(last_text[last_fragment.length() - 1])) {
last_fragment.m_length -= 1;
last_fragment.m_rect.set_width(last_fragment.m_rect.width() - space_width);
m_width -= space_width;
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <AK/Vector.h>
#include <LibWeb/Layout/LineBoxFragment.h>
namespace Web {
class LineBox {
public:
LineBox() {}
float width() const { return m_width; }
void add_fragment(const LayoutNode& layout_node, int start, int length, int width, int height);
const Vector<LineBoxFragment>& fragments() const { return m_fragments; }
Vector<LineBoxFragment>& fragments() { return m_fragments; }
void trim_trailing_whitespace();
private:
friend class LayoutBlock;
Vector<LineBoxFragment> m_fragments;
float m_width { 0 };
};
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Utf8View.h>
#include <LibGUI/Painter.h>
#include <LibWeb/Layout/LayoutDocument.h>
#include <LibWeb/Layout/LayoutText.h>
#include <LibWeb/Layout/LineBoxFragment.h>
#include <LibWeb/RenderingContext.h>
namespace Web {
void LineBoxFragment::render(RenderingContext& context)
{
for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) {
if (!ancestor->is_visible())
return;
}
if (is<LayoutText>(layout_node())) {
to<LayoutText>(layout_node()).render_fragment(context, *this);
}
}
bool LineBoxFragment::is_justifiable_whitespace() const
{
return text() == " ";
}
StringView LineBoxFragment::text() const
{
if (!is<LayoutText>(layout_node()))
return {};
return to<LayoutText>(layout_node()).text_for_rendering().substring_view(m_start, m_length);
}
int LineBoxFragment::text_index_at(float x) const
{
if (!layout_node().is_text())
return 0;
auto& layout_text = to<LayoutText>(layout_node());
auto& font = layout_text.style().font();
Utf8View view(text());
float relative_x = x - m_rect.location().x();
float glyph_spacing = font.glyph_spacing();
float width_so_far = 0;
for (auto it = view.begin(); it != view.end(); ++it) {
float glyph_width = font.glyph_or_emoji_width(*it);
if ((width_so_far + glyph_width + glyph_spacing) > relative_x)
return m_start + view.byte_offset_of(it);
width_so_far += glyph_width + glyph_spacing;
}
return m_start + m_length - 1;
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <LibGfx/FloatRect.h>
namespace Web {
class LayoutNode;
class RenderingContext;
class LineBoxFragment {
friend class LineBox;
public:
LineBoxFragment(const LayoutNode& layout_node, int start, int length, const Gfx::FloatRect& rect)
: m_layout_node(layout_node)
, m_start(start)
, m_length(length)
, m_rect(rect)
{
}
const LayoutNode& layout_node() const { return m_layout_node; }
int start() const { return m_start; }
int length() const { return m_length; }
const Gfx::FloatRect& rect() const { return m_rect; }
Gfx::FloatRect& rect() { return m_rect; }
float width() const { return m_rect.width(); }
void render(RenderingContext&);
bool is_justifiable_whitespace() const;
StringView text() const;
int text_index_at(float x) const;
private:
const LayoutNode& m_layout_node;
int m_start { 0 };
int m_length { 0 };
Gfx::FloatRect m_rect;
};
}