mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 09:27:35 +00:00
Libraries: Move to Userland/Libraries/
This commit is contained in:
parent
dc28c07fa5
commit
13d7c09125
1857 changed files with 266 additions and 274 deletions
133
Userland/Libraries/LibWeb/Layout/BlockBox.cpp
Normal file
133
Userland/Libraries/LibWeb/Layout/BlockBox.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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/Dump.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Layout/WidgetBox.h>
|
||||
#include <math.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Box(document, node, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
BlockBox::BlockBox(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
|
||||
: Box(document, node, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
BlockBox::~BlockBox()
|
||||
{
|
||||
}
|
||||
|
||||
void BlockBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
Box::paint(context, phase);
|
||||
|
||||
if (!children_are_inline())
|
||||
return;
|
||||
|
||||
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.absolute_rect()), Color::Green);
|
||||
fragment.paint(context, phase);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Merge this loop with the above somehow..
|
||||
if (phase == PaintPhase::FocusOutline) {
|
||||
for (auto& line_box : m_line_boxes) {
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
auto* node = fragment.layout_node().dom_node();
|
||||
if (!node)
|
||||
continue;
|
||||
auto* parent = node->parent_element();
|
||||
if (!parent)
|
||||
continue;
|
||||
if (parent->is_focused())
|
||||
context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), context.palette().focus_outline());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HitTestResult BlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const
|
||||
{
|
||||
if (!children_are_inline())
|
||||
return Box::hit_test(position, type);
|
||||
|
||||
HitTestResult last_good_candidate;
|
||||
for (auto& line_box : m_line_boxes) {
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
if (is<Box>(fragment.layout_node()) && downcast<Box>(fragment.layout_node()).stacking_context())
|
||||
continue;
|
||||
if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) {
|
||||
if (is<BlockBox>(fragment.layout_node()))
|
||||
return downcast<BlockBox>(fragment.layout_node()).hit_test(position, type);
|
||||
return { fragment.layout_node(), fragment.text_index_at(position.x()) };
|
||||
}
|
||||
if (fragment.absolute_rect().top() <= position.y())
|
||||
last_good_candidate = { fragment.layout_node(), fragment.text_index_at(position.x()) };
|
||||
}
|
||||
}
|
||||
|
||||
if (type == HitTestType::TextCursor && last_good_candidate.layout_node)
|
||||
return last_good_candidate;
|
||||
return { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
|
||||
}
|
||||
|
||||
void BlockBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
auto& containing_block = context.containing_block();
|
||||
auto* line_box = &containing_block.ensure_last_line_box();
|
||||
|
||||
context.dimension_box_on_line(*this, layout_mode);
|
||||
|
||||
float available_width = context.available_width_at_line(containing_block.line_boxes().size() - 1);
|
||||
|
||||
if (layout_mode == LayoutMode::AllPossibleLineBreaks && line_box->width() > 0) {
|
||||
line_box = &containing_block.add_line_box();
|
||||
} else if (layout_mode == LayoutMode::Default && line_box->width() > 0 && line_box->width() + border_box_width() > available_width) {
|
||||
line_box = &containing_block.add_line_box();
|
||||
}
|
||||
line_box->add_fragment(*this, 0, 0, border_box_width(), height());
|
||||
}
|
||||
|
||||
}
|
79
Userland/Libraries/LibWeb/Layout/BlockBox.h
Normal file
79
Userland/Libraries/LibWeb/Layout/BlockBox.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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/Box.h>
|
||||
#include <LibWeb/Layout/LineBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class BlockBox : public Box {
|
||||
public:
|
||||
BlockBox(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
||||
BlockBox(DOM::Document&, DOM::Node*, CSS::ComputedValues);
|
||||
virtual ~BlockBox() override;
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
|
||||
|
||||
BlockBox* previous_sibling() { return downcast<BlockBox>(Node::previous_sibling()); }
|
||||
const BlockBox* previous_sibling() const { return downcast<BlockBox>(Node::previous_sibling()); }
|
||||
BlockBox* next_sibling() { return downcast<BlockBox>(Node::next_sibling()); }
|
||||
const BlockBox* next_sibling() const { return downcast<BlockBox>(Node::next_sibling()); }
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_fragment(Callback);
|
||||
template<typename Callback>
|
||||
void for_each_fragment(Callback) const;
|
||||
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
};
|
||||
|
||||
template<typename Callback>
|
||||
void BlockBox::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 BlockBox::for_each_fragment(Callback callback) const
|
||||
{
|
||||
for (auto& line_box : line_boxes()) {
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
if (callback(fragment) == IterationDecision::Break)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
566
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
Normal file
566
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.cpp
Normal file
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* Copyright (c) 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/CSS/Length.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/BlockFormattingContext.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/ListItemBox.h>
|
||||
#include <LibWeb/Layout/WidgetBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
BlockFormattingContext::BlockFormattingContext(Box& context_box, FormattingContext* parent)
|
||||
: FormattingContext(context_box, parent)
|
||||
{
|
||||
}
|
||||
|
||||
BlockFormattingContext::~BlockFormattingContext()
|
||||
{
|
||||
}
|
||||
|
||||
bool BlockFormattingContext::is_initial() const
|
||||
{
|
||||
return is<InitialContainingBlockBox>(context_box());
|
||||
}
|
||||
|
||||
void BlockFormattingContext::run(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
if (is_initial()) {
|
||||
layout_initial_containing_block(layout_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: BFC currently computes the width+height of the target box.
|
||||
// This is necessary to be able to place absolutely positioned descendants.
|
||||
// The same work is also done by the parent BFC for each of its blocks..
|
||||
|
||||
if (layout_mode == LayoutMode::Default)
|
||||
compute_width(box);
|
||||
|
||||
if (box.children_are_inline()) {
|
||||
layout_inline_children(box, layout_mode);
|
||||
} else {
|
||||
layout_block_level_children(box, layout_mode);
|
||||
}
|
||||
|
||||
if (layout_mode == LayoutMode::Default) {
|
||||
compute_height(box);
|
||||
|
||||
box.for_each_child_of_type<Box>([&](auto& child_box) {
|
||||
if (child_box.is_absolutely_positioned()) {
|
||||
layout_absolutely_positioned_element(child_box);
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_width(Box& box)
|
||||
{
|
||||
if (box.is_absolutely_positioned()) {
|
||||
compute_width_for_absolutely_positioned_element(box);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is<ReplacedBox>(box)) {
|
||||
// FIXME: This should not be done *by* ReplacedBox
|
||||
auto& replaced = downcast<ReplacedBox>(box);
|
||||
replaced.prepare_for_replaced_layout();
|
||||
compute_width_for_block_level_replaced_element_in_normal_flow(replaced);
|
||||
return;
|
||||
}
|
||||
|
||||
if (box.is_floating()) {
|
||||
compute_width_for_floating_box(box);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& computed_values = box.computed_values();
|
||||
float width_of_containing_block = box.width_of_logical_containing_block();
|
||||
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
|
||||
auto margin_left = CSS::Length::make_auto();
|
||||
auto margin_right = CSS::Length::make_auto();
|
||||
const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block);
|
||||
const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block);
|
||||
|
||||
auto try_compute_width = [&](const auto& a_width) {
|
||||
CSS::Length width = a_width;
|
||||
margin_left = computed_values.margin().left.resolved_or_zero(box, width_of_containing_block);
|
||||
margin_right = computed_values.margin().right.resolved_or_zero(box, width_of_containing_block);
|
||||
|
||||
float total_px = computed_values.border_left().width + computed_values.border_right().width;
|
||||
for (auto& value : { margin_left, padding_left, width, padding_right, margin_right }) {
|
||||
total_px += value.to_px(box);
|
||||
}
|
||||
|
||||
if (!box.is_inline()) {
|
||||
// 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 > width_of_containing_block) {
|
||||
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 = width_of_containing_block - 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 = CSS::Length(underflow_px, CSS::Length::Type::Px);
|
||||
} else {
|
||||
width = zero_value;
|
||||
margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px);
|
||||
}
|
||||
} else {
|
||||
if (!margin_left.is_auto() && !margin_right.is_auto()) {
|
||||
margin_right = CSS::Length(margin_right.to_px(box) + underflow_px, CSS::Length::Type::Px);
|
||||
} else if (!margin_left.is_auto() && margin_right.is_auto()) {
|
||||
margin_right = CSS::Length(underflow_px, CSS::Length::Type::Px);
|
||||
} else if (margin_left.is_auto() && !margin_right.is_auto()) {
|
||||
margin_left = CSS::Length(underflow_px, CSS::Length::Type::Px);
|
||||
} else { // margin_left.is_auto() && margin_right.is_auto()
|
||||
auto half_of_the_underflow = CSS::Length(underflow_px / 2, CSS::Length::Type::Px);
|
||||
margin_left = half_of_the_underflow;
|
||||
margin_right = half_of_the_underflow;
|
||||
}
|
||||
}
|
||||
} else if (box.is_inline_block()) {
|
||||
|
||||
// 10.3.9 'Inline-block', non-replaced elements in normal flow
|
||||
|
||||
// A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
|
||||
if (margin_left.is_auto())
|
||||
margin_left = zero_value;
|
||||
if (margin_right.is_auto())
|
||||
margin_right = zero_value;
|
||||
|
||||
// If 'width' is 'auto', the used value is the shrink-to-fit width as for floating elements.
|
||||
if (width.is_auto()) {
|
||||
|
||||
// Find the available width: in this case, this is the width of the containing
|
||||
// block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
|
||||
// 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
|
||||
float available_width = width_of_containing_block
|
||||
- margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box)
|
||||
- padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box);
|
||||
|
||||
auto result = calculate_shrink_to_fit_widths(box);
|
||||
|
||||
// Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
|
||||
width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px);
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
};
|
||||
|
||||
auto specified_width = computed_values.width().resolved_or_auto(box, width_of_containing_block);
|
||||
|
||||
// 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 = computed_values.max_width().resolved_or_auto(box, width_of_containing_block);
|
||||
if (!specified_max_width.is_auto()) {
|
||||
if (used_width.to_px(box) > specified_max_width.to_px(box)) {
|
||||
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 = computed_values.min_width().resolved_or_auto(box, width_of_containing_block);
|
||||
if (!specified_min_width.is_auto()) {
|
||||
if (used_width.to_px(box) < specified_min_width.to_px(box)) {
|
||||
used_width = try_compute_width(specified_min_width);
|
||||
}
|
||||
}
|
||||
|
||||
box.set_width(used_width.to_px(box));
|
||||
box.box_model().margin.left = margin_left.to_px(box);
|
||||
box.box_model().margin.right = margin_right.to_px(box);
|
||||
box.box_model().border.left = computed_values.border_left().width;
|
||||
box.box_model().border.right = computed_values.border_right().width;
|
||||
box.box_model().padding.left = padding_left.to_px(box);
|
||||
box.box_model().padding.right = padding_right.to_px(box);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_width_for_floating_box(Box& box)
|
||||
{
|
||||
// 10.3.5 Floating, non-replaced elements
|
||||
auto& computed_values = box.computed_values();
|
||||
float width_of_containing_block = box.width_of_logical_containing_block();
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
|
||||
auto margin_left = CSS::Length::make_auto();
|
||||
auto margin_right = CSS::Length::make_auto();
|
||||
const auto padding_left = computed_values.padding().left.resolved_or_zero(box, width_of_containing_block);
|
||||
const auto padding_right = computed_values.padding().right.resolved_or_zero(box, width_of_containing_block);
|
||||
|
||||
// If 'margin-left', or 'margin-right' are computed as 'auto', their used value is '0'.
|
||||
if (margin_left.is_auto())
|
||||
margin_left = zero_value;
|
||||
if (margin_right.is_auto())
|
||||
margin_right = zero_value;
|
||||
|
||||
auto width = computed_values.width().resolved_or_auto(box, width_of_containing_block);
|
||||
|
||||
// If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.
|
||||
if (width.is_auto()) {
|
||||
|
||||
// Find the available width: in this case, this is the width of the containing
|
||||
// block minus the used values of 'margin-left', 'border-left-width', 'padding-left',
|
||||
// 'padding-right', 'border-right-width', 'margin-right', and the widths of any relevant scroll bars.
|
||||
float available_width = width_of_containing_block
|
||||
- margin_left.to_px(box) - computed_values.border_left().width - padding_left.to_px(box)
|
||||
- padding_right.to_px(box) - computed_values.border_right().width - margin_right.to_px(box);
|
||||
|
||||
auto result = calculate_shrink_to_fit_widths(box);
|
||||
|
||||
// Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).
|
||||
width = CSS::Length(min(max(result.preferred_minimum_width, available_width), result.preferred_width), CSS::Length::Type::Px);
|
||||
}
|
||||
|
||||
float final_width = width.resolved_or_zero(box, width_of_containing_block).to_px(box);
|
||||
box.set_width(final_width);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box)
|
||||
{
|
||||
box.set_width(compute_width_for_replaced_element(box));
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox& box)
|
||||
{
|
||||
box.set_height(compute_height_for_replaced_element(box));
|
||||
}
|
||||
|
||||
void BlockFormattingContext::compute_height(Box& box)
|
||||
{
|
||||
if (is<ReplacedBox>(box)) {
|
||||
compute_height_for_block_level_replaced_element_in_normal_flow(downcast<ReplacedBox>(box));
|
||||
return;
|
||||
}
|
||||
|
||||
auto& computed_values = box.computed_values();
|
||||
auto& containing_block = *box.containing_block();
|
||||
|
||||
CSS::Length specified_height;
|
||||
|
||||
if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) {
|
||||
specified_height = CSS::Length::make_auto();
|
||||
} else {
|
||||
specified_height = computed_values.height().resolved_or_auto(box, containing_block.height());
|
||||
}
|
||||
|
||||
auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height());
|
||||
|
||||
box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().border.top = computed_values.border_top().width;
|
||||
box.box_model().border.bottom = computed_values.border_bottom().width;
|
||||
box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
|
||||
if (!specified_height.is_auto()) {
|
||||
float used_height = specified_height.to_px(box);
|
||||
if (!specified_max_height.is_auto())
|
||||
used_height = min(used_height, specified_max_height.to_px(box));
|
||||
box.set_height(used_height);
|
||||
}
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_inline_children(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
InlineFormattingContext context(box, this);
|
||||
context.run(box, layout_mode);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_block_level_children(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
float content_height = 0;
|
||||
float content_width = 0;
|
||||
|
||||
box.for_each_child_of_type<Box>([&](auto& child_box) {
|
||||
if (child_box.is_absolutely_positioned())
|
||||
return IterationDecision::Continue;
|
||||
|
||||
if (child_box.is_floating()) {
|
||||
layout_floating_child(child_box, box);
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
compute_width(child_box);
|
||||
layout_inside(child_box, layout_mode);
|
||||
compute_height(child_box);
|
||||
|
||||
if (is<ReplacedBox>(child_box))
|
||||
place_block_level_replaced_element_in_normal_flow(child_box, box);
|
||||
else if (is<BlockBox>(child_box))
|
||||
place_block_level_non_replaced_element_in_normal_flow(child_box, box);
|
||||
|
||||
// FIXME: This should be factored differently. It's uncool that we mutate the tree *during* layout!
|
||||
// Instead, we should generate the marker box during the tree build.
|
||||
if (is<ListItemBox>(child_box))
|
||||
downcast<ListItemBox>(child_box).layout_marker();
|
||||
|
||||
content_height = max(content_height, child_box.effective_offset().y() + child_box.height() + child_box.box_model().margin_box().bottom);
|
||||
content_width = max(content_width, downcast<Box>(child_box).width());
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
if (layout_mode != LayoutMode::Default) {
|
||||
if (box.computed_values().width().is_undefined() || box.computed_values().width().is_auto())
|
||||
box.set_width(content_width);
|
||||
}
|
||||
|
||||
// FIXME: It's not right to always shrink-wrap the box to the content here.
|
||||
box.set_height(content_height);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::place_block_level_replaced_element_in_normal_flow(Box& child_box, Box& containing_block)
|
||||
{
|
||||
ASSERT(!containing_block.is_absolutely_positioned());
|
||||
auto& replaced_element_box_model = child_box.box_model();
|
||||
|
||||
replaced_element_box_model.margin.top = child_box.computed_values().margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
replaced_element_box_model.margin.bottom = child_box.computed_values().margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
replaced_element_box_model.border.top = child_box.computed_values().border_top().width;
|
||||
replaced_element_box_model.border.bottom = child_box.computed_values().border_bottom().width;
|
||||
replaced_element_box_model.padding.top = child_box.computed_values().padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
replaced_element_box_model.padding.bottom = child_box.computed_values().padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
|
||||
float x = replaced_element_box_model.margin.left
|
||||
+ replaced_element_box_model.border.left
|
||||
+ replaced_element_box_model.padding.left
|
||||
+ replaced_element_box_model.offset.left;
|
||||
|
||||
float y = replaced_element_box_model.margin_box().top + containing_block.box_model().offset.top;
|
||||
|
||||
child_box.set_offset(x, y);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::place_block_level_non_replaced_element_in_normal_flow(Box& child_box, Box& containing_block)
|
||||
{
|
||||
auto& box_model = child_box.box_model();
|
||||
auto& computed_values = child_box.computed_values();
|
||||
|
||||
box_model.margin.top = computed_values.margin().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
box_model.margin.bottom = computed_values.margin().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
box_model.border.top = computed_values.border_top().width;
|
||||
box_model.border.bottom = computed_values.border_bottom().width;
|
||||
box_model.padding.top = computed_values.padding().top.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
box_model.padding.bottom = computed_values.padding().bottom.resolved_or_zero(containing_block, containing_block.width()).to_px(child_box);
|
||||
|
||||
float x = box_model.margin.left
|
||||
+ box_model.border.left
|
||||
+ box_model.padding.left
|
||||
+ box_model.offset.left;
|
||||
|
||||
if (containing_block.computed_values().text_align() == CSS::TextAlign::LibwebCenter) {
|
||||
x = (containing_block.width() / 2) - child_box.width() / 2;
|
||||
}
|
||||
|
||||
float y = box_model.margin_box().top
|
||||
+ box_model.offset.top;
|
||||
|
||||
// NOTE: Empty (0-height) preceding siblings have their margins collapsed with *their* preceding sibling, etc.
|
||||
float collapsed_bottom_margin_of_preceding_siblings = 0;
|
||||
|
||||
auto* relevant_sibling = child_box.previous_sibling_of_type<Layout::BlockBox>();
|
||||
while (relevant_sibling != nullptr) {
|
||||
if (!relevant_sibling->is_absolutely_positioned() && !relevant_sibling->is_floating()) {
|
||||
collapsed_bottom_margin_of_preceding_siblings = max(collapsed_bottom_margin_of_preceding_siblings, relevant_sibling->box_model().margin.bottom);
|
||||
if (relevant_sibling->border_box_height() > 0)
|
||||
break;
|
||||
}
|
||||
relevant_sibling = relevant_sibling->previous_sibling();
|
||||
}
|
||||
|
||||
if (relevant_sibling) {
|
||||
y += relevant_sibling->effective_offset().y()
|
||||
+ relevant_sibling->height()
|
||||
+ relevant_sibling->box_model().border_box().bottom;
|
||||
|
||||
// Collapse top margin with bottom margin of preceding siblings if needed
|
||||
float my_margin_top = box_model.margin.top;
|
||||
|
||||
if (my_margin_top < 0 || collapsed_bottom_margin_of_preceding_siblings < 0) {
|
||||
// Negative margins present.
|
||||
float largest_negative_margin = -min(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
|
||||
float largest_positive_margin = (my_margin_top < 0 && collapsed_bottom_margin_of_preceding_siblings < 0) ? 0 : max(my_margin_top, collapsed_bottom_margin_of_preceding_siblings);
|
||||
float final_margin = largest_positive_margin - largest_negative_margin;
|
||||
y += final_margin - my_margin_top;
|
||||
} else if (collapsed_bottom_margin_of_preceding_siblings > my_margin_top) {
|
||||
// Sibling's margin is larger than mine, adjust so we use sibling's.
|
||||
y += collapsed_bottom_margin_of_preceding_siblings - my_margin_top;
|
||||
}
|
||||
}
|
||||
|
||||
if (child_box.computed_values().clear() == CSS::Clear::Left || child_box.computed_values().clear() == CSS::Clear::Both) {
|
||||
if (!m_left_floating_boxes.is_empty()) {
|
||||
float clearance_y = 0;
|
||||
for (auto* floating_box : m_left_floating_boxes) {
|
||||
clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom);
|
||||
}
|
||||
y = max(y, clearance_y);
|
||||
m_left_floating_boxes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (child_box.computed_values().clear() == CSS::Clear::Right || child_box.computed_values().clear() == CSS::Clear::Both) {
|
||||
if (!m_right_floating_boxes.is_empty()) {
|
||||
float clearance_y = 0;
|
||||
for (auto* floating_box : m_right_floating_boxes) {
|
||||
clearance_y = max(clearance_y, floating_box->effective_offset().y() + floating_box->box_model().margin_box().bottom);
|
||||
}
|
||||
y = max(y, clearance_y);
|
||||
m_right_floating_boxes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
child_box.set_offset(x, y);
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_initial_containing_block(LayoutMode layout_mode)
|
||||
{
|
||||
auto viewport_rect = context_box().frame().viewport_rect();
|
||||
|
||||
auto& icb = downcast<Layout::InitialContainingBlockBox>(context_box());
|
||||
icb.build_stacking_context_tree();
|
||||
|
||||
icb.set_width(viewport_rect.width());
|
||||
|
||||
layout_block_level_children(context_box(), layout_mode);
|
||||
|
||||
ASSERT(!icb.children_are_inline());
|
||||
|
||||
// FIXME: The ICB should have the height of the viewport.
|
||||
// Instead of auto-sizing the ICB, we should spill into overflow.
|
||||
float lowest_bottom = 0;
|
||||
icb.for_each_child_of_type<Box>([&](auto& child) {
|
||||
lowest_bottom = max(lowest_bottom, child.absolute_rect().bottom());
|
||||
});
|
||||
|
||||
// FIXME: This is a hack and should be managed by an overflow mechanism.
|
||||
icb.set_height(max(static_cast<float>(viewport_rect.height()), lowest_bottom));
|
||||
|
||||
// FIXME: This is a total hack. Make sure any GUI::Widgets are moved into place after layout.
|
||||
// We should stop embedding GUI::Widgets entirely, since that won't work out-of-process.
|
||||
icb.for_each_in_subtree_of_type<Layout::WidgetBox>([&](auto& widget) {
|
||||
widget.update_widget();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
static Gfx::FloatRect rect_in_coordinate_space(const Box& box, const Box& context_box)
|
||||
{
|
||||
Gfx::FloatRect rect { box.effective_offset(), box.size() };
|
||||
for (auto* ancestor = box.parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (is<Box>(*ancestor)) {
|
||||
auto offset = downcast<Box>(*ancestor).effective_offset();
|
||||
rect.move_by(offset);
|
||||
}
|
||||
if (ancestor == &context_box)
|
||||
break;
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
void BlockFormattingContext::layout_floating_child(Box& box, Box& containing_block)
|
||||
{
|
||||
ASSERT(box.is_floating());
|
||||
|
||||
compute_width(box);
|
||||
layout_inside(box, LayoutMode::Default);
|
||||
compute_height(box);
|
||||
|
||||
// First we place the box normally (to get the right y coordinate.)
|
||||
place_block_level_non_replaced_element_in_normal_flow(box, containing_block);
|
||||
|
||||
// Then we float it to the left or right.
|
||||
float x = box.effective_offset().x();
|
||||
|
||||
auto box_in_context_rect = rect_in_coordinate_space(box, context_box());
|
||||
float y_in_context_box = box_in_context_rect.y();
|
||||
|
||||
// Next, float to the left and/or right
|
||||
if (box.computed_values().float_() == CSS::Float::Left) {
|
||||
if (!m_left_floating_boxes.is_empty()) {
|
||||
auto& previous_floating_box = *m_left_floating_boxes.last();
|
||||
auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box());
|
||||
if (previous_rect.contains_vertically(y_in_context_box)) {
|
||||
// This box touches another already floating box. Stack to the right.
|
||||
x = previous_floating_box.effective_offset().x() + previous_floating_box.width();
|
||||
} else {
|
||||
// This box does not touch another floating box, go all the way to the left.
|
||||
x = 0;
|
||||
// Also, forget all previous left-floating boxes while we're here since they're no longer relevant.
|
||||
m_left_floating_boxes.clear();
|
||||
}
|
||||
} else {
|
||||
// This is the first left-floating box. Go all the way to the left.
|
||||
x = 0;
|
||||
}
|
||||
m_left_floating_boxes.append(&box);
|
||||
} else if (box.computed_values().float_() == CSS::Float::Right) {
|
||||
if (!m_right_floating_boxes.is_empty()) {
|
||||
auto& previous_floating_box = *m_right_floating_boxes.last();
|
||||
auto previous_rect = rect_in_coordinate_space(previous_floating_box, context_box());
|
||||
if (previous_rect.contains_vertically(y_in_context_box)) {
|
||||
// This box touches another already floating box. Stack to the left.
|
||||
x = previous_floating_box.effective_offset().x() - box.width();
|
||||
} else {
|
||||
// This box does not touch another floating box, go all the way to the right.
|
||||
x = containing_block.width() - box.width();
|
||||
// Also, forget all previous right-floating boxes while we're here since they're no longer relevant.
|
||||
m_right_floating_boxes.clear();
|
||||
}
|
||||
} else {
|
||||
// This is the first right-floating box. Go all the way to the right.
|
||||
x = containing_block.width() - box.width();
|
||||
}
|
||||
m_right_floating_boxes.append(&box);
|
||||
}
|
||||
|
||||
box.set_offset(x, box.effective_offset().y());
|
||||
}
|
||||
|
||||
}
|
73
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
Normal file
73
Userland/Libraries/LibWeb/Layout/BlockFormattingContext.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 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/Forward.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class BlockFormattingContext : public FormattingContext {
|
||||
public:
|
||||
explicit BlockFormattingContext(Box&, FormattingContext* parent);
|
||||
~BlockFormattingContext();
|
||||
|
||||
virtual void run(Box&, LayoutMode) override;
|
||||
|
||||
bool is_initial() const;
|
||||
|
||||
const Vector<Box*>& left_floating_boxes() const { return m_left_floating_boxes; }
|
||||
const Vector<Box*>& right_floating_boxes() const { return m_right_floating_boxes; }
|
||||
|
||||
protected:
|
||||
void compute_width(Box&);
|
||||
void compute_height(Box&);
|
||||
|
||||
private:
|
||||
virtual bool is_block_formatting_context() const final { return true; }
|
||||
|
||||
void compute_width_for_floating_box(Box&);
|
||||
|
||||
void compute_width_for_block_level_replaced_element_in_normal_flow(ReplacedBox&);
|
||||
void compute_height_for_block_level_replaced_element_in_normal_flow(ReplacedBox&);
|
||||
|
||||
void layout_initial_containing_block(LayoutMode);
|
||||
|
||||
void layout_block_level_children(Box&, LayoutMode);
|
||||
void layout_inline_children(Box&, LayoutMode);
|
||||
|
||||
void place_block_level_replaced_element_in_normal_flow(Box& child, Box& container);
|
||||
void place_block_level_non_replaced_element_in_normal_flow(Box& child, Box& container);
|
||||
|
||||
void layout_floating_child(Box&, Box& containing_block);
|
||||
|
||||
Vector<Box*> m_left_floating_boxes;
|
||||
Vector<Box*> m_right_floating_boxes;
|
||||
};
|
||||
|
||||
}
|
212
Userland/Libraries/LibWeb/Layout/Box.cpp
Normal file
212
Userland/Libraries/LibWeb/Layout/Box.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* 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/HTML/HTMLBodyElement.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
#include <LibWeb/Painting/BorderPainting.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
void Box::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
Gfx::PainterStateSaver saver(context.painter());
|
||||
if (is_fixed_position())
|
||||
context.painter().translate(context.scroll_offset());
|
||||
|
||||
Gfx::FloatRect padded_rect;
|
||||
padded_rect.set_x(absolute_x() - box_model().padding.left);
|
||||
padded_rect.set_width(width() + box_model().padding.left + box_model().padding.right);
|
||||
padded_rect.set_y(absolute_y() - box_model().padding.top);
|
||||
padded_rect.set_height(height() + box_model().padding.top + box_model().padding.bottom);
|
||||
|
||||
if (phase == PaintPhase::Background && !is_body()) {
|
||||
context.painter().fill_rect(enclosing_int_rect(padded_rect), computed_values().background_color());
|
||||
|
||||
if (background_image() && background_image()->bitmap())
|
||||
context.painter().draw_tiled_bitmap(enclosing_int_rect(padded_rect), *background_image()->bitmap());
|
||||
}
|
||||
|
||||
if (phase == PaintPhase::Border) {
|
||||
Gfx::FloatRect bordered_rect;
|
||||
bordered_rect.set_x(padded_rect.x() - box_model().border.left);
|
||||
bordered_rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right);
|
||||
bordered_rect.set_y(padded_rect.y() - box_model().border.top);
|
||||
bordered_rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom);
|
||||
|
||||
Painting::paint_border(context, Painting::BorderEdge::Left, bordered_rect, computed_values());
|
||||
Painting::paint_border(context, Painting::BorderEdge::Right, bordered_rect, computed_values());
|
||||
Painting::paint_border(context, Painting::BorderEdge::Top, bordered_rect, computed_values());
|
||||
Painting::paint_border(context, Painting::BorderEdge::Bottom, bordered_rect, computed_values());
|
||||
}
|
||||
|
||||
Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Overlay && dom_node() && document().inspected_node() == dom_node()) {
|
||||
auto content_rect = absolute_rect();
|
||||
|
||||
auto margin_box = box_model().margin_box();
|
||||
Gfx::FloatRect margin_rect;
|
||||
margin_rect.set_x(absolute_x() - margin_box.left);
|
||||
margin_rect.set_width(width() + margin_box.left + margin_box.right);
|
||||
margin_rect.set_y(absolute_y() - margin_box.top);
|
||||
margin_rect.set_height(height() + margin_box.top + margin_box.bottom);
|
||||
|
||||
context.painter().draw_rect(enclosing_int_rect(margin_rect), Color::Yellow);
|
||||
context.painter().draw_rect(enclosing_int_rect(padded_rect), Color::Cyan);
|
||||
context.painter().draw_rect(enclosing_int_rect(content_rect), Color::Magenta);
|
||||
}
|
||||
|
||||
if (phase == PaintPhase::FocusOutline && dom_node() && dom_node()->is_element() && downcast<DOM::Element>(*dom_node()).is_focused()) {
|
||||
context.painter().draw_rect(enclosing_int_rect(absolute_rect()), context.palette().focus_outline());
|
||||
}
|
||||
}
|
||||
|
||||
HitTestResult Box::hit_test(const Gfx::IntPoint& position, HitTestType type) 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 { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
|
||||
for_each_child_in_paint_order([&](auto& child) {
|
||||
auto child_result = child.hit_test(position, type);
|
||||
if (child_result.layout_node)
|
||||
result = child_result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void Box::set_needs_display()
|
||||
{
|
||||
if (!is_inline()) {
|
||||
frame().set_needs_display(enclosing_int_rect(absolute_rect()));
|
||||
return;
|
||||
}
|
||||
|
||||
Node::set_needs_display();
|
||||
}
|
||||
|
||||
bool Box::is_body() const
|
||||
{
|
||||
return dom_node() && dom_node() == document().body();
|
||||
}
|
||||
|
||||
void Box::set_offset(const Gfx::FloatPoint& offset)
|
||||
{
|
||||
if (m_offset == offset)
|
||||
return;
|
||||
m_offset = offset;
|
||||
did_set_rect();
|
||||
}
|
||||
|
||||
void Box::set_size(const Gfx::FloatSize& size)
|
||||
{
|
||||
if (m_size == size)
|
||||
return;
|
||||
m_size = size;
|
||||
did_set_rect();
|
||||
}
|
||||
|
||||
Gfx::FloatPoint Box::effective_offset() const
|
||||
{
|
||||
if (m_containing_line_box_fragment)
|
||||
return m_containing_line_box_fragment->offset();
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
const Gfx::FloatRect Box::absolute_rect() const
|
||||
{
|
||||
Gfx::FloatRect rect { effective_offset(), size() };
|
||||
for (auto* block = containing_block(); block; block = block->containing_block()) {
|
||||
rect.move_by(block->effective_offset());
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
void Box::set_containing_line_box_fragment(LineBoxFragment& fragment)
|
||||
{
|
||||
m_containing_line_box_fragment = fragment.make_weak_ptr();
|
||||
}
|
||||
|
||||
StackingContext* Box::enclosing_stacking_context()
|
||||
{
|
||||
for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (!is<Box>(ancestor))
|
||||
continue;
|
||||
auto& ancestor_box = downcast<Box>(*ancestor);
|
||||
if (!ancestor_box.establishes_stacking_context())
|
||||
continue;
|
||||
ASSERT(ancestor_box.stacking_context());
|
||||
return ancestor_box.stacking_context();
|
||||
}
|
||||
// We should always reach the Layout::InitialContainingBlockBox stacking context.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool Box::establishes_stacking_context() const
|
||||
{
|
||||
if (!has_style())
|
||||
return false;
|
||||
if (dom_node() == document().root())
|
||||
return true;
|
||||
auto position = computed_values().position();
|
||||
auto z_index = computed_values().z_index();
|
||||
if (position == CSS::Position::Absolute || position == CSS::Position::Relative) {
|
||||
if (z_index.has_value())
|
||||
return true;
|
||||
}
|
||||
if (position == CSS::Position::Fixed || position == CSS::Position::Sticky)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
LineBox& Box::ensure_last_line_box()
|
||||
{
|
||||
if (m_line_boxes.is_empty())
|
||||
return add_line_box();
|
||||
return m_line_boxes.last();
|
||||
}
|
||||
|
||||
LineBox& Box::add_line_box()
|
||||
{
|
||||
m_line_boxes.append(LineBox());
|
||||
return m_line_boxes.last();
|
||||
}
|
||||
|
||||
float Box::width_of_logical_containing_block() const
|
||||
{
|
||||
auto* containing_block = this->containing_block();
|
||||
ASSERT(containing_block);
|
||||
return containing_block->width();
|
||||
}
|
||||
|
||||
}
|
146
Userland/Libraries/LibWeb/Layout/Box.h
Normal file
146
Userland/Libraries/LibWeb/Layout/Box.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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/OwnPtr.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibWeb/Layout/LineBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Painting/StackingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class Box : public NodeWithStyleAndBoxModelMetrics {
|
||||
public:
|
||||
const Gfx::FloatRect absolute_rect() const;
|
||||
|
||||
Gfx::FloatPoint effective_offset() const;
|
||||
|
||||
void set_offset(const Gfx::FloatPoint& offset);
|
||||
void set_offset(float x, float y) { set_offset({ x, y }); }
|
||||
|
||||
const Gfx::FloatSize& size() const { return m_size; }
|
||||
void set_size(const Gfx::FloatSize&);
|
||||
void set_size(float width, float height) { set_size({ width, height }); }
|
||||
|
||||
void set_width(float width) { set_size(width, height()); }
|
||||
void set_height(float height) { set_size(width(), height); }
|
||||
float width() const { return m_size.width(); }
|
||||
float height() const { return m_size.height(); }
|
||||
|
||||
float border_box_width() const
|
||||
{
|
||||
auto border_box = box_model().border_box();
|
||||
return width() + border_box.left + border_box.right;
|
||||
}
|
||||
|
||||
float border_box_height() const
|
||||
{
|
||||
auto border_box = box_model().border_box();
|
||||
return height() + border_box.top + border_box.bottom;
|
||||
}
|
||||
|
||||
Gfx::FloatRect content_box_as_relative_rect() const
|
||||
{
|
||||
return { m_offset, m_size };
|
||||
}
|
||||
|
||||
Gfx::FloatRect margin_box_as_relative_rect() const
|
||||
{
|
||||
auto rect = content_box_as_relative_rect();
|
||||
auto margin_box = box_model().margin_box();
|
||||
rect.set_x(rect.x() - margin_box.left);
|
||||
rect.set_width(rect.width() + margin_box.left + margin_box.right);
|
||||
rect.set_y(rect.y() - margin_box.top);
|
||||
rect.set_height(rect.height() + margin_box.top + margin_box.bottom);
|
||||
return rect;
|
||||
}
|
||||
|
||||
float absolute_x() const { return absolute_rect().x(); }
|
||||
float absolute_y() const { return absolute_rect().y(); }
|
||||
Gfx::FloatPoint absolute_position() const { return absolute_rect().location(); }
|
||||
|
||||
virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
|
||||
virtual void set_needs_display() override;
|
||||
|
||||
bool is_body() const;
|
||||
|
||||
void set_containing_line_box_fragment(LineBoxFragment&);
|
||||
|
||||
bool establishes_stacking_context() const;
|
||||
StackingContext* stacking_context() { return m_stacking_context; }
|
||||
const StackingContext* stacking_context() const { return m_stacking_context; }
|
||||
void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); }
|
||||
StackingContext* enclosing_stacking_context();
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) 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 float width_of_logical_containing_block() const;
|
||||
|
||||
protected:
|
||||
Box(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: NodeWithStyleAndBoxModelMetrics(document, node, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
Box(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
|
||||
: NodeWithStyleAndBoxModelMetrics(document, node, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void did_set_rect() { }
|
||||
|
||||
Vector<LineBox> m_line_boxes;
|
||||
|
||||
private:
|
||||
virtual bool is_box() const final { return true; }
|
||||
|
||||
Gfx::FloatPoint m_offset;
|
||||
Gfx::FloatSize m_size;
|
||||
|
||||
// Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc)
|
||||
WeakPtr<LineBoxFragment> m_containing_line_box_fragment;
|
||||
|
||||
OwnPtr<StackingContext> m_stacking_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
template<>
|
||||
inline bool is<Web::Layout::Box>(const Web::Layout::Node& input)
|
||||
{
|
||||
return input.is_box();
|
||||
}
|
||||
|
||||
}
|
61
Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
Normal file
61
Userland/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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::Layout {
|
||||
|
||||
PixelBox BoxModelMetrics::margin_box() const
|
||||
{
|
||||
return {
|
||||
margin.top + border.top + padding.top,
|
||||
margin.right + border.right + padding.right,
|
||||
margin.bottom + border.bottom + padding.bottom,
|
||||
margin.left + border.left + padding.left,
|
||||
};
|
||||
}
|
||||
|
||||
PixelBox BoxModelMetrics::padding_box() const
|
||||
{
|
||||
return {
|
||||
padding.top,
|
||||
padding.right,
|
||||
padding.bottom,
|
||||
padding.left,
|
||||
};
|
||||
}
|
||||
|
||||
PixelBox BoxModelMetrics::border_box() const
|
||||
{
|
||||
return {
|
||||
border.top + padding.top,
|
||||
border.right + padding.right,
|
||||
border.bottom + padding.bottom,
|
||||
border.left + padding.left,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
52
Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h
Normal file
52
Userland/Libraries/LibWeb/Layout/BoxModelMetrics.h
Normal 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 <LibGfx/Size.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
struct PixelBox {
|
||||
float top { 0 };
|
||||
float right { 0 };
|
||||
float bottom { 0 };
|
||||
float left { 0 };
|
||||
};
|
||||
|
||||
struct BoxModelMetrics {
|
||||
public:
|
||||
PixelBox margin;
|
||||
PixelBox padding;
|
||||
PixelBox border;
|
||||
PixelBox offset;
|
||||
|
||||
PixelBox margin_box() const;
|
||||
PixelBox padding_box() const;
|
||||
PixelBox border_box() const;
|
||||
};
|
||||
|
||||
}
|
48
Userland/Libraries/LibWeb/Layout/BreakNode.cpp
Normal file
48
Userland/Libraries/LibWeb/Layout/BreakNode.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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/BlockBox.h>
|
||||
#include <LibWeb/Layout/BreakNode.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
BreakNode::BreakNode(DOM::Document& document, HTML::HTMLBRElement& element)
|
||||
: Layout::NodeWithStyleAndBoxModelMetrics(document, &element, CSS::StyleProperties::create())
|
||||
{
|
||||
set_inline(true);
|
||||
}
|
||||
|
||||
BreakNode::~BreakNode()
|
||||
{
|
||||
}
|
||||
|
||||
void BreakNode::split_into_lines(InlineFormattingContext& context, LayoutMode)
|
||||
{
|
||||
context.containing_block().add_line_box();
|
||||
}
|
||||
|
||||
}
|
45
Userland/Libraries/LibWeb/Layout/BreakNode.h
Normal file
45
Userland/Libraries/LibWeb/Layout/BreakNode.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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/HTML/HTMLBRElement.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class BreakNode final : public NodeWithStyleAndBoxModelMetrics {
|
||||
public:
|
||||
BreakNode(DOM::Document&, HTML::HTMLBRElement&);
|
||||
virtual ~BreakNode() override;
|
||||
|
||||
const HTML::HTMLBRElement& dom_node() const { return downcast<HTML::HTMLBRElement>(*Node::dom_node()); }
|
||||
|
||||
private:
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
};
|
||||
|
||||
}
|
117
Userland/Libraries/LibWeb/Layout/ButtonBox.cpp
Normal file
117
Userland/Libraries/LibWeb/Layout/ButtonBox.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 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/Event.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Layout/ButtonBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
ButtonBox::ButtonBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
ButtonBox::~ButtonBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ButtonBox::prepare_for_replaced_layout()
|
||||
{
|
||||
set_intrinsic_width(font().width(dom_node().value()) + 20);
|
||||
set_has_intrinsic_width(true);
|
||||
|
||||
set_intrinsic_height(20);
|
||||
set_has_intrinsic_height(true);
|
||||
}
|
||||
|
||||
void ButtonBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
ReplacedBox::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
bool hovered = document().hovered_node() == &dom_node();
|
||||
Gfx::StylePainter::paint_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::ButtonStyle::Normal, m_being_pressed, hovered, dom_node().checked(), dom_node().enabled());
|
||||
|
||||
auto text_rect = enclosing_int_rect(absolute_rect());
|
||||
if (m_being_pressed)
|
||||
text_rect.move_by(1, 1);
|
||||
context.painter().draw_text(text_rect, dom_node().value(), font(), Gfx::TextAlignment::Center, context.palette().button_text());
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned)
|
||||
{
|
||||
if (button != GUI::MouseButton::Left || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
m_being_pressed = true;
|
||||
set_needs_display();
|
||||
|
||||
m_tracking_mouse = true;
|
||||
frame().event_handler().set_mouse_event_tracking_layout_node(this);
|
||||
}
|
||||
|
||||
void ButtonBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
|
||||
{
|
||||
if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
// NOTE: Handling the click may run arbitrary JS, which could disappear this node.
|
||||
NonnullRefPtr protected_this = *this;
|
||||
NonnullRefPtr protected_frame = frame();
|
||||
|
||||
bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
|
||||
if (is_inside)
|
||||
dom_node().did_click_button({});
|
||||
|
||||
m_being_pressed = false;
|
||||
m_tracking_mouse = false;
|
||||
|
||||
protected_frame->event_handler().set_mouse_event_tracking_layout_node(nullptr);
|
||||
}
|
||||
|
||||
void ButtonBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned)
|
||||
{
|
||||
if (!m_tracking_mouse || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
|
||||
if (m_being_pressed == is_inside)
|
||||
return;
|
||||
|
||||
m_being_pressed = is_inside;
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
}
|
55
Userland/Libraries/LibWeb/Layout/ButtonBox.h
Normal file
55
Userland/Libraries/LibWeb/Layout/ButtonBox.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 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/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class ButtonBox : public ReplacedBox {
|
||||
public:
|
||||
ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~ButtonBox() override;
|
||||
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
|
||||
HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
|
||||
|
||||
private:
|
||||
virtual bool wants_mouse_events() const override { return true; }
|
||||
virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
|
||||
virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
|
||||
virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override;
|
||||
|
||||
bool m_being_pressed { false };
|
||||
bool m_tracking_mouse { false };
|
||||
};
|
||||
|
||||
}
|
68
Userland/Libraries/LibWeb/Layout/CanvasBox.cpp
Normal file
68
Userland/Libraries/LibWeb/Layout/CanvasBox.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 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 <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/Layout/CanvasBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
CanvasBox::CanvasBox(DOM::Document& document, HTML::HTMLCanvasElement& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
CanvasBox::~CanvasBox()
|
||||
{
|
||||
}
|
||||
|
||||
void CanvasBox::prepare_for_replaced_layout()
|
||||
{
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(dom_node().width());
|
||||
set_intrinsic_height(dom_node().height());
|
||||
}
|
||||
|
||||
void CanvasBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
ReplacedBox::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
// FIXME: This should be done at a different level. Also rect() does not include padding etc!
|
||||
if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect())))
|
||||
return;
|
||||
|
||||
if (dom_node().bitmap())
|
||||
context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *dom_node().bitmap(), dom_node().bitmap()->rect());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
Userland/Libraries/LibWeb/Layout/CanvasBox.h
Normal file
45
Userland/Libraries/LibWeb/Layout/CanvasBox.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 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/HTML/HTMLCanvasElement.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class CanvasBox : public ReplacedBox {
|
||||
public:
|
||||
CanvasBox(DOM::Document&, HTML::HTMLCanvasElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~CanvasBox() override;
|
||||
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
const HTML::HTMLCanvasElement& dom_node() const { return static_cast<const HTML::HTMLCanvasElement&>(ReplacedBox::dom_node()); }
|
||||
};
|
||||
|
||||
}
|
103
Userland/Libraries/LibWeb/Layout/CheckBox.cpp
Normal file
103
Userland/Libraries/LibWeb/Layout/CheckBox.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 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/Event.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/Layout/CheckBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
CheckBox::CheckBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
{
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(13);
|
||||
set_intrinsic_height(13);
|
||||
}
|
||||
|
||||
CheckBox::~CheckBox()
|
||||
{
|
||||
}
|
||||
|
||||
void CheckBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
ReplacedBox::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), dom_node().enabled(), dom_node().checked(), m_being_pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned)
|
||||
{
|
||||
if (button != GUI::MouseButton::Left || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
m_being_pressed = true;
|
||||
set_needs_display();
|
||||
|
||||
m_tracking_mouse = true;
|
||||
frame().event_handler().set_mouse_event_tracking_layout_node(this);
|
||||
}
|
||||
|
||||
void CheckBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
|
||||
{
|
||||
if (!m_tracking_mouse || button != GUI::MouseButton::Left || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
// NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node.
|
||||
NonnullRefPtr protect = *this;
|
||||
|
||||
bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
|
||||
if (is_inside)
|
||||
dom_node().set_checked(!dom_node().checked());
|
||||
|
||||
m_being_pressed = false;
|
||||
m_tracking_mouse = false;
|
||||
frame().event_handler().set_mouse_event_tracking_layout_node(nullptr);
|
||||
}
|
||||
|
||||
void CheckBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned)
|
||||
{
|
||||
if (!m_tracking_mouse || !dom_node().enabled())
|
||||
return;
|
||||
|
||||
bool is_inside = enclosing_int_rect(absolute_rect()).contains(position);
|
||||
if (m_being_pressed == is_inside)
|
||||
return;
|
||||
|
||||
m_being_pressed = is_inside;
|
||||
set_needs_display();
|
||||
}
|
||||
|
||||
}
|
54
Userland/Libraries/LibWeb/Layout/CheckBox.h
Normal file
54
Userland/Libraries/LibWeb/Layout/CheckBox.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 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/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class CheckBox : public ReplacedBox {
|
||||
public:
|
||||
CheckBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~CheckBox() override;
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
|
||||
HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(ReplacedBox::dom_node()); }
|
||||
|
||||
private:
|
||||
virtual bool wants_mouse_events() const override { return true; }
|
||||
virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
|
||||
virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
|
||||
virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override;
|
||||
|
||||
bool m_being_pressed { false };
|
||||
bool m_tracking_mouse { false };
|
||||
};
|
||||
|
||||
}
|
548
Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
Normal file
548
Userland/Libraries/LibWeb/Layout/FormattingContext.cpp
Normal file
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* Copyright (c) 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/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/BlockFormattingContext.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
#include <LibWeb/Layout/TableBox.h>
|
||||
#include <LibWeb/Layout/TableCellBox.h>
|
||||
#include <LibWeb/Layout/TableFormattingContext.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
FormattingContext::FormattingContext(Box& context_box, FormattingContext* parent)
|
||||
: m_parent(parent)
|
||||
, m_context_box(&context_box)
|
||||
{
|
||||
}
|
||||
|
||||
FormattingContext::~FormattingContext()
|
||||
{
|
||||
}
|
||||
|
||||
bool FormattingContext::creates_block_formatting_context(const Box& box)
|
||||
{
|
||||
if (box.is_root_element())
|
||||
return true;
|
||||
if (box.is_floating())
|
||||
return true;
|
||||
if (box.is_absolutely_positioned())
|
||||
return true;
|
||||
if (box.is_inline_block())
|
||||
return true;
|
||||
if (is<TableCellBox>(box))
|
||||
return true;
|
||||
// FIXME: table-caption
|
||||
// FIXME: anonymous table cells
|
||||
// FIXME: Block elements where overflow has a value other than visible and clip.
|
||||
// FIXME: display: flow-root
|
||||
// FIXME: Elements with contain: layout, content, or paint.
|
||||
// FIXME: flex
|
||||
// FIXME: grid
|
||||
// FIXME: multicol
|
||||
// FIXME: column-span: all
|
||||
return false;
|
||||
}
|
||||
|
||||
void FormattingContext::layout_inside(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
if (creates_block_formatting_context(box)) {
|
||||
BlockFormattingContext context(box, this);
|
||||
context.run(box, layout_mode);
|
||||
return;
|
||||
}
|
||||
if (is<TableBox>(box)) {
|
||||
TableFormattingContext context(box, this);
|
||||
context.run(box, layout_mode);
|
||||
} else if (box.children_are_inline()) {
|
||||
InlineFormattingContext context(box, this);
|
||||
context.run(box, layout_mode);
|
||||
} else {
|
||||
// FIXME: This needs refactoring!
|
||||
ASSERT(is_block_formatting_context());
|
||||
run(box, layout_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static float greatest_child_width(const Box& box)
|
||||
{
|
||||
float max_width = 0;
|
||||
if (box.children_are_inline()) {
|
||||
for (auto& child : box.line_boxes()) {
|
||||
max_width = max(max_width, child.width());
|
||||
}
|
||||
} else {
|
||||
box.for_each_child_of_type<Box>([&](auto& child) {
|
||||
max_width = max(max_width, child.border_box_width());
|
||||
});
|
||||
}
|
||||
return max_width;
|
||||
}
|
||||
|
||||
FormattingContext::ShrinkToFitResult FormattingContext::calculate_shrink_to_fit_widths(Box& box)
|
||||
{
|
||||
// Calculate the preferred width by formatting the content without breaking lines
|
||||
// other than where explicit line breaks occur.
|
||||
layout_inside(box, LayoutMode::OnlyRequiredLineBreaks);
|
||||
float preferred_width = greatest_child_width(box);
|
||||
|
||||
// Also calculate the preferred minimum width, e.g., by trying all possible line breaks.
|
||||
// CSS 2.2 does not define the exact algorithm.
|
||||
|
||||
layout_inside(box, LayoutMode::AllPossibleLineBreaks);
|
||||
float preferred_minimum_width = greatest_child_width(box);
|
||||
|
||||
return { preferred_width, preferred_minimum_width };
|
||||
}
|
||||
|
||||
static Gfx::FloatSize solve_replaced_size_constraint(float w, float h, const ReplacedBox& box)
|
||||
{
|
||||
// 10.4 Minimum and maximum widths: 'min-width' and 'max-width'
|
||||
|
||||
auto& containing_block = *box.containing_block();
|
||||
auto specified_min_width = box.computed_values().min_width().resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
auto specified_max_width = box.computed_values().max_width().resolved(CSS::Length::make_px(w), box, containing_block.width()).to_px(box);
|
||||
auto specified_min_height = box.computed_values().min_height().resolved_or_auto(box, containing_block.height()).to_px(box);
|
||||
auto specified_max_height = box.computed_values().max_height().resolved(CSS::Length::make_px(h), box, containing_block.height()).to_px(box);
|
||||
|
||||
auto min_width = min(specified_min_width, specified_max_width);
|
||||
auto max_width = max(specified_min_width, specified_max_width);
|
||||
auto min_height = min(specified_min_height, specified_max_height);
|
||||
auto max_height = max(specified_min_height, specified_max_height);
|
||||
|
||||
if (w > max_width)
|
||||
return { w, max(max_width * h / w, min_height) };
|
||||
if (w < min_width)
|
||||
return { max_width, min(min_width * h / w, max_height) };
|
||||
if (h > max_height)
|
||||
return { max(max_height * w / h, min_width), max_height };
|
||||
if (h < min_height)
|
||||
return { min(min_height * w / h, max_width), min_height };
|
||||
if ((w > max_width && h > max_height) && (max_width / w < max_height / h))
|
||||
return { max_width, max(min_height, max_width * h / w) };
|
||||
if ((w > max_width && h > max_height) && (max_width / w > max_height / h))
|
||||
return { max(min_width, max_height * w / h), max_height };
|
||||
if ((w < min_width && h < min_height) && (min_width / w < min_height / h))
|
||||
return { min(max_width, min_height * w / h), min_height };
|
||||
if ((w < min_width && h < min_height) && (min_width / w > min_height / h))
|
||||
return { min_width, min(max_height, min_width * h / w) };
|
||||
if (w < min_width && h > max_height)
|
||||
return { min_width, max_height };
|
||||
if (w > max_width && h < min_height)
|
||||
return { max_width, min_height };
|
||||
return { w, h };
|
||||
}
|
||||
|
||||
float FormattingContext::tentative_width_for_replaced_element(const ReplacedBox& box, const CSS::Length& width)
|
||||
{
|
||||
auto& containing_block = *box.containing_block();
|
||||
auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height());
|
||||
|
||||
float used_width = width.to_px(box);
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element also has an intrinsic width,
|
||||
// then that intrinsic width is the used value of 'width'.
|
||||
if (specified_height.is_auto() && width.is_auto() && box.has_intrinsic_width()) {
|
||||
used_width = box.intrinsic_width();
|
||||
}
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element has no intrinsic width,
|
||||
// but does have an intrinsic height and intrinsic ratio;
|
||||
// or if 'width' has a computed value of 'auto',
|
||||
// 'height' has some other computed value, and the element does have an intrinsic ratio; then the used value of 'width' is:
|
||||
//
|
||||
// (used height) * (intrinsic ratio)
|
||||
else if ((specified_height.is_auto() && width.is_auto() && !box.has_intrinsic_width() && box.has_intrinsic_height() && box.has_intrinsic_ratio()) || (width.is_auto() && box.has_intrinsic_ratio())) {
|
||||
used_width = compute_height_for_replaced_element(box) * box.intrinsic_ratio();
|
||||
}
|
||||
|
||||
else if (width.is_auto() && box.has_intrinsic_width()) {
|
||||
used_width = box.intrinsic_width();
|
||||
}
|
||||
|
||||
else if (width.is_auto()) {
|
||||
used_width = 300;
|
||||
}
|
||||
|
||||
return used_width;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_element(Box& box)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_width_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
|
||||
else
|
||||
compute_width_for_absolutely_positioned_non_replaced_element(box);
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_element(Box& box)
|
||||
{
|
||||
if (is<ReplacedBox>(box))
|
||||
compute_height_for_absolutely_positioned_replaced_element(downcast<ReplacedBox>(box));
|
||||
else
|
||||
compute_height_for_absolutely_positioned_non_replaced_element(box);
|
||||
}
|
||||
|
||||
float FormattingContext::compute_width_for_replaced_element(const ReplacedBox& box)
|
||||
{
|
||||
// 10.3.4 Block-level, replaced elements in normal flow...
|
||||
// 10.3.2 Inline, replaced elements
|
||||
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
auto& containing_block = *box.containing_block();
|
||||
|
||||
auto margin_left = box.computed_values().margin().left.resolved_or_zero(box, containing_block.width());
|
||||
auto margin_right = box.computed_values().margin().right.resolved_or_zero(box, containing_block.width());
|
||||
|
||||
// A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.
|
||||
if (margin_left.is_auto())
|
||||
margin_left = zero_value;
|
||||
if (margin_right.is_auto())
|
||||
margin_right = zero_value;
|
||||
|
||||
auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
|
||||
|
||||
// 1. The tentative used width is calculated (without 'min-width' and 'max-width')
|
||||
auto used_width = tentative_width_for_replaced_element(box, 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 = box.computed_values().max_width().resolved_or_auto(box, containing_block.width());
|
||||
if (!specified_max_width.is_auto()) {
|
||||
if (used_width > specified_max_width.to_px(box)) {
|
||||
used_width = tentative_width_for_replaced_element(box, 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 = box.computed_values().min_width().resolved_or_auto(box, containing_block.width());
|
||||
if (!specified_min_width.is_auto()) {
|
||||
if (used_width < specified_min_width.to_px(box)) {
|
||||
used_width = tentative_width_for_replaced_element(box, specified_min_width);
|
||||
}
|
||||
}
|
||||
|
||||
return used_width;
|
||||
}
|
||||
|
||||
float FormattingContext::tentative_height_for_replaced_element(const ReplacedBox& box, const CSS::Length& height)
|
||||
{
|
||||
auto& containing_block = *box.containing_block();
|
||||
auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
|
||||
|
||||
float used_height = height.to_px(box);
|
||||
|
||||
// If 'height' and 'width' both have computed values of 'auto' and the element also has
|
||||
// an intrinsic height, then that intrinsic height is the used value of 'height'.
|
||||
if (specified_width.is_auto() && height.is_auto() && box.has_intrinsic_height())
|
||||
used_height = box.intrinsic_height();
|
||||
else if (height.is_auto() && box.has_intrinsic_ratio())
|
||||
used_height = compute_width_for_replaced_element(box) / box.intrinsic_ratio();
|
||||
else if (height.is_auto() && box.has_intrinsic_height())
|
||||
used_height = box.intrinsic_height();
|
||||
else if (height.is_auto())
|
||||
used_height = 150;
|
||||
|
||||
return used_height;
|
||||
}
|
||||
|
||||
float FormattingContext::compute_height_for_replaced_element(const ReplacedBox& box)
|
||||
{
|
||||
// 10.6.2 Inline replaced elements, block-level replaced elements in normal flow,
|
||||
// 'inline-block' replaced elements in normal flow and floating replaced elements
|
||||
|
||||
auto& containing_block = *box.containing_block();
|
||||
auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
|
||||
auto specified_height = box.computed_values().height().resolved_or_auto(box, containing_block.height());
|
||||
|
||||
float used_height = tentative_height_for_replaced_element(box, specified_height);
|
||||
|
||||
if (specified_width.is_auto() && specified_height.is_auto() && box.has_intrinsic_ratio()) {
|
||||
float w = tentative_width_for_replaced_element(box, specified_width);
|
||||
float h = used_height;
|
||||
used_height = solve_replaced_size_constraint(w, h, box).height();
|
||||
}
|
||||
|
||||
return used_height;
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_non_replaced_element(Box& box)
|
||||
{
|
||||
auto& containing_block = *box.containing_block();
|
||||
auto& computed_values = box.computed_values();
|
||||
auto zero_value = CSS::Length::make_px(0);
|
||||
|
||||
auto margin_left = CSS::Length::make_auto();
|
||||
auto margin_right = CSS::Length::make_auto();
|
||||
const auto border_left = computed_values.border_left().width;
|
||||
const auto border_right = computed_values.border_right().width;
|
||||
const auto padding_left = computed_values.padding().left.resolved_or_zero(box, containing_block.width());
|
||||
const auto padding_right = computed_values.padding().right.resolved_or_zero(box, containing_block.width());
|
||||
|
||||
auto try_compute_width = [&](const auto& a_width) {
|
||||
margin_left = computed_values.margin().left.resolved_or_zero(box, containing_block.width());
|
||||
margin_right = computed_values.margin().right.resolved_or_zero(box, containing_block.width());
|
||||
|
||||
auto left = computed_values.offset().left.resolved_or_auto(box, containing_block.width());
|
||||
auto right = computed_values.offset().right.resolved_or_auto(box, containing_block.width());
|
||||
auto width = a_width;
|
||||
|
||||
auto solve_for_left = [&] {
|
||||
return CSS::Length(containing_block.width() - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
|
||||
};
|
||||
|
||||
auto solve_for_width = [&] {
|
||||
return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box) - right.to_px(box), CSS::Length::Type::Px);
|
||||
};
|
||||
|
||||
auto solve_for_right = [&] {
|
||||
return CSS::Length(containing_block.width() - left.to_px(box) - margin_left.to_px(box) - border_left - padding_left.to_px(box) - width.to_px(box) - padding_right.to_px(box) - border_right - margin_right.to_px(box), CSS::Length::Type::Px);
|
||||
};
|
||||
|
||||
// If all three of 'left', 'width', and 'right' are 'auto':
|
||||
if (left.is_auto() && width.is_auto() && right.is_auto()) {
|
||||
// First set any 'auto' values for 'margin-left' and 'margin-right' to 0.
|
||||
if (margin_left.is_auto())
|
||||
margin_left = CSS::Length::make_px(0);
|
||||
if (margin_right.is_auto())
|
||||
margin_right = CSS::Length::make_px(0);
|
||||
// Then, if the 'direction' property of the element establishing the static-position containing block
|
||||
// is 'ltr' set 'left' to the static position and apply rule number three below;
|
||||
// otherwise, set 'right' to the static position and apply rule number one below.
|
||||
// FIXME: This is very hackish.
|
||||
left = CSS::Length::make_px(0);
|
||||
goto Rule3;
|
||||
}
|
||||
|
||||
if (!left.is_auto() && !width.is_auto() && !right.is_auto()) {
|
||||
// FIXME: This should be solved in a more complicated way.
|
||||
return width;
|
||||
}
|
||||
|
||||
if (margin_left.is_auto())
|
||||
margin_left = CSS::Length::make_px(0);
|
||||
if (margin_right.is_auto())
|
||||
margin_right = CSS::Length::make_px(0);
|
||||
|
||||
// 1. 'left' and 'width' are 'auto' and 'right' is not 'auto',
|
||||
// then the width is shrink-to-fit. Then solve for 'left'
|
||||
if (left.is_auto() && width.is_auto() && !right.is_auto()) {
|
||||
auto result = calculate_shrink_to_fit_widths(box);
|
||||
solve_for_left();
|
||||
auto available_width = solve_for_width();
|
||||
width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
|
||||
}
|
||||
|
||||
// 2. 'left' and 'right' are 'auto' and 'width' is not 'auto',
|
||||
// then if the 'direction' property of the element establishing
|
||||
// the static-position containing block is 'ltr' set 'left'
|
||||
// to the static position, otherwise set 'right' to the static position.
|
||||
// Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
|
||||
else if (left.is_auto() && right.is_auto() && !width.is_auto()) {
|
||||
// FIXME: Check direction
|
||||
// FIXME: Use the static-position containing block
|
||||
left = zero_value;
|
||||
right = solve_for_right();
|
||||
}
|
||||
|
||||
// 3. 'width' and 'right' are 'auto' and 'left' is not 'auto',
|
||||
// then the width is shrink-to-fit. Then solve for 'right'
|
||||
else if (width.is_auto() && right.is_auto() && !left.is_auto()) {
|
||||
Rule3:
|
||||
auto result = calculate_shrink_to_fit_widths(box);
|
||||
auto available_width = solve_for_width();
|
||||
width = CSS::Length(min(max(result.preferred_minimum_width, available_width.to_px(box)), result.preferred_width), CSS::Length::Type::Px);
|
||||
right = solve_for_right();
|
||||
}
|
||||
|
||||
// 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve for 'left'
|
||||
else if (left.is_auto() && !width.is_auto() && !right.is_auto()) {
|
||||
left = solve_for_left();
|
||||
}
|
||||
|
||||
// 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve for 'width'
|
||||
else if (width.is_auto() && !left.is_auto() && !right.is_auto()) {
|
||||
width = solve_for_width();
|
||||
}
|
||||
|
||||
// 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve for 'right'
|
||||
else if (right.is_auto() && !left.is_auto() && !width.is_auto()) {
|
||||
right = solve_for_right();
|
||||
}
|
||||
|
||||
return width;
|
||||
};
|
||||
|
||||
auto specified_width = computed_values.width().resolved_or_auto(box, containing_block.width());
|
||||
|
||||
// 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 = computed_values.max_width().resolved_or_auto(box, containing_block.width());
|
||||
if (!specified_max_width.is_auto()) {
|
||||
if (used_width.to_px(box) > specified_max_width.to_px(box)) {
|
||||
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 = computed_values.min_width().resolved_or_auto(box, containing_block.width());
|
||||
if (!specified_min_width.is_auto()) {
|
||||
if (used_width.to_px(box) < specified_min_width.to_px(box)) {
|
||||
used_width = try_compute_width(specified_min_width);
|
||||
}
|
||||
}
|
||||
|
||||
box.set_width(used_width.to_px(box));
|
||||
|
||||
box.box_model().margin.left = margin_left.to_px(box);
|
||||
box.box_model().margin.right = margin_right.to_px(box);
|
||||
box.box_model().border.left = border_left;
|
||||
box.box_model().border.right = border_right;
|
||||
box.box_model().padding.left = padding_left.to_px(box);
|
||||
box.box_model().padding.right = padding_right.to_px(box);
|
||||
}
|
||||
|
||||
void FormattingContext::compute_width_for_absolutely_positioned_replaced_element(ReplacedBox& box)
|
||||
{
|
||||
// 10.3.8 Absolutely positioned, replaced elements
|
||||
// The used value of 'width' is determined as for inline replaced elements.
|
||||
box.prepare_for_replaced_layout();
|
||||
box.set_width(compute_width_for_replaced_element(box));
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_non_replaced_element(Box& box)
|
||||
{
|
||||
auto& computed_values = box.computed_values();
|
||||
auto& containing_block = *box.containing_block();
|
||||
|
||||
CSS::Length specified_height;
|
||||
|
||||
if (computed_values.height().is_percentage() && !containing_block.computed_values().height().is_absolute()) {
|
||||
specified_height = CSS::Length::make_auto();
|
||||
} else {
|
||||
specified_height = computed_values.height().resolved_or_auto(box, containing_block.height());
|
||||
}
|
||||
|
||||
auto specified_max_height = computed_values.max_height().resolved_or_auto(box, containing_block.height());
|
||||
|
||||
box.box_model().margin.top = computed_values.margin().top.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().margin.bottom = computed_values.margin().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().border.top = computed_values.border_top().width;
|
||||
box.box_model().border.bottom = computed_values.border_bottom().width;
|
||||
box.box_model().padding.top = computed_values.padding().top.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
box.box_model().padding.bottom = computed_values.padding().bottom.resolved_or_zero(box, containing_block.width()).to_px(box);
|
||||
|
||||
if (!specified_height.is_auto()) {
|
||||
float used_height = specified_height.to_px(box);
|
||||
if (!specified_max_height.is_auto())
|
||||
used_height = min(used_height, specified_max_height.to_px(box));
|
||||
box.set_height(used_height);
|
||||
}
|
||||
}
|
||||
|
||||
void FormattingContext::layout_absolutely_positioned_element(Box& box)
|
||||
{
|
||||
auto& containing_block = context_box();
|
||||
auto& box_model = box.box_model();
|
||||
|
||||
auto specified_width = box.computed_values().width().resolved_or_auto(box, containing_block.width());
|
||||
|
||||
compute_width_for_absolutely_positioned_element(box);
|
||||
layout_inside(box, LayoutMode::Default);
|
||||
compute_height_for_absolutely_positioned_element(box);
|
||||
|
||||
box_model.margin.left = box.computed_values().margin().left.resolved_or_auto(box, containing_block.width()).to_px(box);
|
||||
box_model.margin.top = box.computed_values().margin().top.resolved_or_auto(box, containing_block.height()).to_px(box);
|
||||
box_model.margin.right = box.computed_values().margin().right.resolved_or_auto(box, containing_block.width()).to_px(box);
|
||||
box_model.margin.bottom = box.computed_values().margin().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
|
||||
|
||||
box_model.border.left = box.computed_values().border_left().width;
|
||||
box_model.border.right = box.computed_values().border_right().width;
|
||||
box_model.border.top = box.computed_values().border_top().width;
|
||||
box_model.border.bottom = box.computed_values().border_bottom().width;
|
||||
|
||||
box_model.offset.left = box.computed_values().offset().left.resolved_or_auto(box, containing_block.width()).to_px(box);
|
||||
box_model.offset.top = box.computed_values().offset().top.resolved_or_auto(box, containing_block.height()).to_px(box);
|
||||
box_model.offset.right = box.computed_values().offset().right.resolved_or_auto(box, containing_block.width()).to_px(box);
|
||||
box_model.offset.bottom = box.computed_values().offset().bottom.resolved_or_auto(box, containing_block.height()).to_px(box);
|
||||
|
||||
if (box.computed_values().offset().left.is_auto() && specified_width.is_auto() && box.computed_values().offset().right.is_auto()) {
|
||||
if (box.computed_values().margin().left.is_auto())
|
||||
box_model.margin.left = 0;
|
||||
if (box.computed_values().margin().right.is_auto())
|
||||
box_model.margin.right = 0;
|
||||
}
|
||||
|
||||
Gfx::FloatPoint used_offset;
|
||||
|
||||
if (!box.computed_values().offset().left.is_auto()) {
|
||||
float x_offset = box_model.offset.left
|
||||
+ box_model.border_box().left;
|
||||
used_offset.set_x(x_offset + box_model.margin.left);
|
||||
} else if (!box.computed_values().offset().right.is_auto()) {
|
||||
float x_offset = 0
|
||||
- box_model.offset.right
|
||||
- box_model.border_box().right;
|
||||
used_offset.set_x(containing_block.width() + x_offset - box.width() - box_model.margin.right);
|
||||
} else {
|
||||
float x_offset = box_model.margin_box().left;
|
||||
used_offset.set_x(x_offset);
|
||||
}
|
||||
|
||||
if (!box.computed_values().offset().top.is_auto()) {
|
||||
float y_offset = box_model.offset.top
|
||||
+ box_model.border_box().top;
|
||||
used_offset.set_y(y_offset + box_model.margin.top);
|
||||
} else if (!box.computed_values().offset().bottom.is_auto()) {
|
||||
float y_offset = 0
|
||||
- box_model.offset.bottom
|
||||
- box_model.border_box().bottom;
|
||||
used_offset.set_y(containing_block.height() + y_offset - box.height() - box_model.margin.bottom);
|
||||
} else {
|
||||
float y_offset = box_model.margin_box().top;
|
||||
used_offset.set_y(y_offset);
|
||||
}
|
||||
|
||||
box.set_offset(used_offset);
|
||||
}
|
||||
|
||||
void FormattingContext::compute_height_for_absolutely_positioned_replaced_element(ReplacedBox& box)
|
||||
{
|
||||
// FIXME: Implement this.
|
||||
return compute_height_for_absolutely_positioned_non_replaced_element(box);
|
||||
}
|
||||
|
||||
}
|
78
Userland/Libraries/LibWeb/Layout/FormattingContext.h
Normal file
78
Userland/Libraries/LibWeb/Layout/FormattingContext.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 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/Forward.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class FormattingContext {
|
||||
public:
|
||||
virtual void run(Box&, LayoutMode) = 0;
|
||||
|
||||
Box& context_box() { return *m_context_box; }
|
||||
const Box& context_box() const { return *m_context_box; }
|
||||
|
||||
FormattingContext* parent() { return m_parent; }
|
||||
const FormattingContext* parent() const { return m_parent; }
|
||||
|
||||
virtual bool is_block_formatting_context() const { return false; }
|
||||
|
||||
static bool creates_block_formatting_context(const Box&);
|
||||
|
||||
static float compute_width_for_replaced_element(const ReplacedBox&);
|
||||
static float compute_height_for_replaced_element(const ReplacedBox&);
|
||||
|
||||
protected:
|
||||
FormattingContext(Box&, FormattingContext* parent = nullptr);
|
||||
virtual ~FormattingContext();
|
||||
|
||||
void layout_inside(Box&, LayoutMode);
|
||||
|
||||
struct ShrinkToFitResult {
|
||||
float preferred_width { 0 };
|
||||
float preferred_minimum_width { 0 };
|
||||
};
|
||||
|
||||
static float tentative_width_for_replaced_element(const ReplacedBox&, const CSS::Length& width);
|
||||
static float tentative_height_for_replaced_element(const ReplacedBox&, const CSS::Length& width);
|
||||
|
||||
ShrinkToFitResult calculate_shrink_to_fit_widths(Box&);
|
||||
|
||||
void layout_absolutely_positioned_element(Box&);
|
||||
void compute_width_for_absolutely_positioned_element(Box&);
|
||||
void compute_width_for_absolutely_positioned_non_replaced_element(Box&);
|
||||
void compute_width_for_absolutely_positioned_replaced_element(ReplacedBox&);
|
||||
void compute_height_for_absolutely_positioned_element(Box&);
|
||||
void compute_height_for_absolutely_positioned_non_replaced_element(Box&);
|
||||
void compute_height_for_absolutely_positioned_replaced_element(ReplacedBox&);
|
||||
|
||||
FormattingContext* m_parent { nullptr };
|
||||
Box* m_context_box { nullptr };
|
||||
};
|
||||
|
||||
}
|
102
Userland/Libraries/LibWeb/Layout/FrameBox.cpp
Normal file
102
Userland/Libraries/LibWeb/Layout/FrameBox.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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 <LibGUI/ScrollBar.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/InProcessWebView.h>
|
||||
#include <LibWeb/Layout/FrameBox.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
//#define DEBUG_HIGHLIGHT_FOCUSED_FRAME
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
FrameBox::FrameBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
FrameBox::~FrameBox()
|
||||
{
|
||||
}
|
||||
|
||||
void FrameBox::prepare_for_replaced_layout()
|
||||
{
|
||||
ASSERT(dom_node().content_frame());
|
||||
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
// FIXME: Do proper error checking, etc.
|
||||
set_intrinsic_width(dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(300));
|
||||
set_intrinsic_height(dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(150));
|
||||
}
|
||||
|
||||
void FrameBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
ReplacedBox::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
auto* hosted_document = dom_node().content_document();
|
||||
if (!hosted_document)
|
||||
return;
|
||||
auto* hosted_layout_tree = hosted_document->layout_node();
|
||||
if (!hosted_layout_tree)
|
||||
return;
|
||||
|
||||
context.painter().save();
|
||||
auto old_viewport_rect = context.viewport_rect();
|
||||
|
||||
context.painter().add_clip_rect(enclosing_int_rect(absolute_rect()));
|
||||
context.painter().translate(absolute_x(), absolute_y());
|
||||
|
||||
context.set_viewport_rect({ {}, dom_node().content_frame()->size() });
|
||||
const_cast<Layout::InitialContainingBlockBox*>(hosted_layout_tree)->paint_all_phases(context);
|
||||
|
||||
context.set_viewport_rect(old_viewport_rect);
|
||||
context.painter().restore();
|
||||
|
||||
#ifdef DEBUG_HIGHLIGHT_FOCUSED_FRAME
|
||||
if (dom_node().content_frame()->is_focused_frame()) {
|
||||
context.painter().draw_rect(absolute_rect().to<int>(), Color::Cyan);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FrameBox::did_set_rect()
|
||||
{
|
||||
ReplacedBox::did_set_rect();
|
||||
|
||||
ASSERT(dom_node().content_frame());
|
||||
dom_node().content_frame()->set_size(size().to_type<int>());
|
||||
}
|
||||
|
||||
}
|
49
Userland/Libraries/LibWeb/Layout/FrameBox.h
Normal file
49
Userland/Libraries/LibWeb/Layout/FrameBox.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 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/HTML/HTMLIFrameElement.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class FrameBox final : public ReplacedBox {
|
||||
public:
|
||||
FrameBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~FrameBox() override;
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
|
||||
const HTML::HTMLIFrameElement& dom_node() const { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); }
|
||||
HTML::HTMLIFrameElement& dom_node() { return downcast<HTML::HTMLIFrameElement>(ReplacedBox::dom_node()); }
|
||||
|
||||
private:
|
||||
virtual void did_set_rect() override;
|
||||
};
|
||||
|
||||
}
|
135
Userland/Libraries/LibWeb/Layout/ImageBox.cpp
Normal file
135
Userland/Libraries/LibWeb/Layout/ImageBox.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 <LibGfx/Font.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
#include <LibGfx/ImageDecoder.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/Layout/ImageBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
ImageBox::ImageBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style, const ImageLoader& image_loader)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
, m_image_loader(image_loader)
|
||||
{
|
||||
}
|
||||
|
||||
ImageBox::~ImageBox()
|
||||
{
|
||||
}
|
||||
|
||||
int ImageBox::preferred_width() const
|
||||
{
|
||||
return dom_node().attribute(HTML::AttributeNames::width).to_int().value_or(m_image_loader.width());
|
||||
}
|
||||
|
||||
int ImageBox::preferred_height() const
|
||||
{
|
||||
return dom_node().attribute(HTML::AttributeNames::height).to_int().value_or(m_image_loader.height());
|
||||
}
|
||||
|
||||
void ImageBox::prepare_for_replaced_layout()
|
||||
{
|
||||
if (!m_image_loader.has_loaded_or_failed()) {
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(0);
|
||||
set_intrinsic_height(0);
|
||||
} else {
|
||||
if (m_image_loader.width()) {
|
||||
set_has_intrinsic_width(true);
|
||||
set_intrinsic_width(m_image_loader.width());
|
||||
}
|
||||
if (m_image_loader.height()) {
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_height(m_image_loader.height());
|
||||
}
|
||||
|
||||
if (m_image_loader.width() && m_image_loader.height()) {
|
||||
set_has_intrinsic_ratio(true);
|
||||
set_intrinsic_ratio((float)m_image_loader.width() / (float)m_image_loader.height());
|
||||
} else {
|
||||
set_has_intrinsic_ratio(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (renders_as_alt_text()) {
|
||||
auto& image_element = downcast<HTML::HTMLImageElement>(dom_node());
|
||||
auto& font = Gfx::FontDatabase::default_font();
|
||||
auto alt = image_element.alt();
|
||||
if (alt.is_empty())
|
||||
alt = image_element.src();
|
||||
set_width(font.width(alt) + 16);
|
||||
set_height(font.glyph_height() + 16);
|
||||
}
|
||||
|
||||
if (!has_intrinsic_width() && !has_intrinsic_height()) {
|
||||
set_width(16);
|
||||
set_height(16);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
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(absolute_rect())))
|
||||
return;
|
||||
|
||||
ReplacedBox::paint(context, phase);
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
if (renders_as_alt_text()) {
|
||||
auto& image_element = downcast<HTML::HTMLImageElement>(dom_node());
|
||||
context.painter().set_font(Gfx::FontDatabase::default_font());
|
||||
Gfx::StylePainter::paint_frame(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
|
||||
auto alt = image_element.alt();
|
||||
if (alt.is_empty())
|
||||
alt = image_element.src();
|
||||
context.painter().draw_text(enclosing_int_rect(absolute_rect()), alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right);
|
||||
} else if (auto bitmap = m_image_loader.bitmap()) {
|
||||
context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *bitmap, bitmap->rect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageBox::renders_as_alt_text() const
|
||||
{
|
||||
if (is<HTML::HTMLImageElement>(dom_node()))
|
||||
return !m_image_loader.has_image();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImageBox::set_visible_in_viewport(Badge<Layout::InitialContainingBlockBox>, bool visible_in_viewport)
|
||||
{
|
||||
m_image_loader.set_visible_in_viewport(visible_in_viewport);
|
||||
}
|
||||
|
||||
}
|
55
Userland/Libraries/LibWeb/Layout/ImageBox.h
Normal file
55
Userland/Libraries/LibWeb/Layout/ImageBox.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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/HTML/HTMLImageElement.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class ImageBox : public ReplacedBox {
|
||||
public:
|
||||
ImageBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>, const ImageLoader&);
|
||||
virtual ~ImageBox() override;
|
||||
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
const DOM::Element& dom_node() const { return static_cast<const DOM::Element&>(ReplacedBox::dom_node()); }
|
||||
|
||||
bool renders_as_alt_text() const;
|
||||
|
||||
void set_visible_in_viewport(Badge<InitialContainingBlockBox>, bool);
|
||||
|
||||
private:
|
||||
int preferred_width() const;
|
||||
int preferred_height() const;
|
||||
|
||||
const ImageLoader& m_image_loader;
|
||||
};
|
||||
|
||||
}
|
133
Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp
Normal file
133
Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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/Layout/ImageBox.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/WidgetBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
#include <LibWeb/Painting/StackingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
InitialContainingBlockBox::InitialContainingBlockBox(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: BlockBox(document, &document, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
InitialContainingBlockBox::~InitialContainingBlockBox()
|
||||
{
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::build_stacking_context_tree()
|
||||
{
|
||||
if (stacking_context())
|
||||
return;
|
||||
|
||||
set_stacking_context(make<StackingContext>(*this, nullptr));
|
||||
|
||||
for_each_in_subtree_of_type<Box>([&](Box& box) {
|
||||
if (&box == this)
|
||||
return IterationDecision::Continue;
|
||||
if (!box.establishes_stacking_context()) {
|
||||
ASSERT(!box.stacking_context());
|
||||
return IterationDecision::Continue;
|
||||
}
|
||||
auto* parent_context = box.enclosing_stacking_context();
|
||||
ASSERT(parent_context);
|
||||
box.set_stacking_context(make<StackingContext>(box, parent_context));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect& 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<ImageBox>([&](auto& layout_image) {
|
||||
const_cast<ImageBox&>(layout_image).set_visible_in_viewport({}, viewport_rect.intersects(layout_image.absolute_rect()));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::paint_all_phases(PaintContext& context)
|
||||
{
|
||||
paint(context, PaintPhase::Background);
|
||||
paint(context, PaintPhase::Border);
|
||||
paint(context, PaintPhase::Foreground);
|
||||
if (context.has_focus())
|
||||
paint(context, PaintPhase::FocusOutline);
|
||||
paint(context, PaintPhase::Overlay);
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
stacking_context()->paint(context, phase);
|
||||
}
|
||||
|
||||
HitTestResult InitialContainingBlockBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const
|
||||
{
|
||||
return stacking_context()->hit_test(position, type);
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::recompute_selection_states()
|
||||
{
|
||||
SelectionState state = SelectionState::None;
|
||||
|
||||
auto selection = this->selection().normalized();
|
||||
|
||||
for_each_in_subtree([&](auto& layout_node) {
|
||||
if (!selection.is_valid()) {
|
||||
// Everything gets SelectionState::None.
|
||||
} else if (&layout_node == selection.start().layout_node && &layout_node == selection.end().layout_node) {
|
||||
state = SelectionState::StartAndEnd;
|
||||
} else if (&layout_node == selection.start().layout_node) {
|
||||
state = SelectionState::Start;
|
||||
} else if (&layout_node == selection.end().layout_node) {
|
||||
state = SelectionState::End;
|
||||
} else {
|
||||
if (state == SelectionState::Start)
|
||||
state = SelectionState::Full;
|
||||
else if (state == SelectionState::End || state == SelectionState::StartAndEnd)
|
||||
state = SelectionState::None;
|
||||
}
|
||||
layout_node.set_selection_state(state);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::set_selection(const LayoutRange& selection)
|
||||
{
|
||||
m_selection = selection;
|
||||
recompute_selection_states();
|
||||
}
|
||||
|
||||
void InitialContainingBlockBox::set_selection_end(const LayoutPosition& position)
|
||||
{
|
||||
m_selection.set_end(position);
|
||||
recompute_selection_states();
|
||||
}
|
||||
|
||||
}
|
60
Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h
Normal file
60
Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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/BlockBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class InitialContainingBlockBox final : public BlockBox {
|
||||
public:
|
||||
explicit InitialContainingBlockBox(DOM::Document&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~InitialContainingBlockBox() override;
|
||||
|
||||
const DOM::Document& dom_node() const { return static_cast<const DOM::Document&>(*Node::dom_node()); }
|
||||
|
||||
void paint_all_phases(PaintContext&);
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
|
||||
virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
|
||||
|
||||
const LayoutRange& selection() const { return m_selection; }
|
||||
void set_selection(const LayoutRange&);
|
||||
void set_selection_end(const LayoutPosition&);
|
||||
|
||||
void did_set_viewport_rect(Badge<Frame>, const Gfx::IntRect&);
|
||||
|
||||
void build_stacking_context_tree();
|
||||
|
||||
void recompute_selection_states();
|
||||
|
||||
private:
|
||||
LayoutRange m_selection;
|
||||
};
|
||||
|
||||
}
|
254
Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
Normal file
254
Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (c) 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/CSS/Length.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/BlockFormattingContext.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
InlineFormattingContext::InlineFormattingContext(Box& containing_block, FormattingContext* parent)
|
||||
: FormattingContext(containing_block, parent)
|
||||
{
|
||||
}
|
||||
|
||||
InlineFormattingContext::~InlineFormattingContext()
|
||||
{
|
||||
}
|
||||
|
||||
struct AvailableSpaceForLineInfo {
|
||||
float left { 0 };
|
||||
float right { 0 };
|
||||
};
|
||||
|
||||
static AvailableSpaceForLineInfo available_space_for_line(const InlineFormattingContext& context, size_t line_index)
|
||||
{
|
||||
AvailableSpaceForLineInfo info;
|
||||
|
||||
// FIXME: This is a total hack guess since we don't actually know the final y position of lines here!
|
||||
float line_height = context.containing_block().line_height();
|
||||
float y = (line_index * line_height);
|
||||
|
||||
auto& bfc = static_cast<const BlockFormattingContext&>(*context.parent());
|
||||
|
||||
for (ssize_t i = bfc.left_floating_boxes().size() - 1; i >= 0; --i) {
|
||||
auto& floating_box = *bfc.left_floating_boxes().at(i);
|
||||
auto rect = floating_box.margin_box_as_relative_rect();
|
||||
if (rect.contains_vertically(y)) {
|
||||
info.left = rect.right() + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.right = context.containing_block().width();
|
||||
|
||||
for (ssize_t i = bfc.right_floating_boxes().size() - 1; i >= 0; --i) {
|
||||
auto& floating_box = *bfc.right_floating_boxes().at(i);
|
||||
auto rect = floating_box.margin_box_as_relative_rect();
|
||||
if (rect.contains_vertically(y)) {
|
||||
info.right = rect.left() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
float InlineFormattingContext::available_width_at_line(size_t line_index) const
|
||||
{
|
||||
auto info = available_space_for_line(*this, line_index);
|
||||
return info.right - info.left;
|
||||
}
|
||||
|
||||
void InlineFormattingContext::run(Box&, LayoutMode layout_mode)
|
||||
{
|
||||
ASSERT(containing_block().children_are_inline());
|
||||
containing_block().line_boxes().clear();
|
||||
containing_block().for_each_child([&](auto& child) {
|
||||
ASSERT(child.is_inline());
|
||||
if (is<Box>(child) && child.is_absolutely_positioned()) {
|
||||
layout_absolutely_positioned_element(downcast<Box>(child));
|
||||
return;
|
||||
}
|
||||
|
||||
child.split_into_lines(*this, layout_mode);
|
||||
});
|
||||
|
||||
for (auto& line_box : containing_block().line_boxes()) {
|
||||
line_box.trim_trailing_whitespace();
|
||||
}
|
||||
|
||||
// If there's an empty line box at the bottom, just remove it instead of giving it height.
|
||||
if (!containing_block().line_boxes().is_empty() && containing_block().line_boxes().last().fragments().is_empty())
|
||||
containing_block().line_boxes().take_last();
|
||||
|
||||
auto text_align = containing_block().computed_values().text_align();
|
||||
float min_line_height = containing_block().line_height();
|
||||
float content_height = 0;
|
||||
float max_linebox_width = 0;
|
||||
|
||||
for (size_t line_index = 0; line_index < containing_block().line_boxes().size(); ++line_index) {
|
||||
auto& line_box = containing_block().line_boxes()[line_index];
|
||||
float max_height = min_line_height;
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
max_height = max(max_height, fragment.height());
|
||||
}
|
||||
|
||||
float x_offset = available_space_for_line(*this, line_index).left;
|
||||
|
||||
float excess_horizontal_space = (float)containing_block().width() - line_box.width();
|
||||
|
||||
switch (text_align) {
|
||||
case CSS::TextAlign::Center:
|
||||
case CSS::TextAlign::LibwebCenter:
|
||||
x_offset += excess_horizontal_space / 2;
|
||||
break;
|
||||
case CSS::TextAlign::Right:
|
||||
x_offset += excess_horizontal_space;
|
||||
break;
|
||||
case CSS::TextAlign::Left:
|
||||
case CSS::TextAlign::Justify:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
float excess_horizontal_space_including_whitespace = excess_horizontal_space;
|
||||
int whitespace_count = 0;
|
||||
if (text_align == CSS::TextAlign::Justify) {
|
||||
for (auto& fragment : line_box.fragments()) {
|
||||
if (fragment.is_justifiable_whitespace()) {
|
||||
++whitespace_count;
|
||||
excess_horizontal_space_including_whitespace += fragment.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];
|
||||
|
||||
if (fragment.type() == LineBoxFragment::Type::Leading || fragment.type() == LineBoxFragment::Type::Trailing) {
|
||||
fragment.set_height(max_height);
|
||||
} else {
|
||||
fragment.set_height(max(min_line_height, fragment.height()));
|
||||
}
|
||||
|
||||
// Vertically align everyone's bottom to the line.
|
||||
// FIXME: Support other kinds of vertical alignment.
|
||||
fragment.set_offset({ roundf(x_offset + fragment.offset().x()), content_height + (max_height - fragment.height()) });
|
||||
|
||||
if (text_align == CSS::TextAlign::Justify
|
||||
&& fragment.is_justifiable_whitespace()
|
||||
&& fragment.width() != justified_space_width) {
|
||||
float diff = justified_space_width - fragment.width();
|
||||
fragment.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) {
|
||||
auto offset = line_box.fragments()[j].offset();
|
||||
offset.move_by(diff, 0);
|
||||
line_box.fragments()[j].set_offset(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!line_box.fragments().is_empty()) {
|
||||
float left_edge = line_box.fragments().first().offset().x();
|
||||
float right_edge = line_box.fragments().last().offset().x() + line_box.fragments().last().width();
|
||||
float final_line_box_width = right_edge - left_edge;
|
||||
line_box.m_width = final_line_box_width;
|
||||
max_linebox_width = max(max_linebox_width, final_line_box_width);
|
||||
}
|
||||
|
||||
content_height += max_height;
|
||||
}
|
||||
|
||||
if (layout_mode != LayoutMode::Default) {
|
||||
containing_block().set_width(max_linebox_width);
|
||||
}
|
||||
|
||||
containing_block().set_height(content_height);
|
||||
}
|
||||
|
||||
void InlineFormattingContext::dimension_box_on_line(Box& box, LayoutMode layout_mode)
|
||||
{
|
||||
if (is<ReplacedBox>(box)) {
|
||||
auto& replaced = downcast<ReplacedBox>(box);
|
||||
replaced.set_width(compute_width_for_replaced_element(replaced));
|
||||
replaced.set_height(compute_height_for_replaced_element(replaced));
|
||||
return;
|
||||
}
|
||||
|
||||
if (box.is_inline_block()) {
|
||||
auto& inline_block = const_cast<BlockBox&>(downcast<BlockBox>(box));
|
||||
|
||||
if (inline_block.computed_values().width().is_undefined_or_auto()) {
|
||||
auto result = calculate_shrink_to_fit_widths(inline_block);
|
||||
|
||||
auto margin_left = inline_block.computed_values().margin().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
|
||||
auto border_left_width = inline_block.computed_values().border_left().width;
|
||||
auto padding_left = inline_block.computed_values().padding().left.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
|
||||
|
||||
auto margin_right = inline_block.computed_values().margin().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
|
||||
auto border_right_width = inline_block.computed_values().border_right().width;
|
||||
auto padding_right = inline_block.computed_values().padding().right.resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block);
|
||||
|
||||
auto available_width = containing_block().width()
|
||||
- margin_left
|
||||
- border_left_width
|
||||
- padding_left
|
||||
- padding_right
|
||||
- border_right_width
|
||||
- margin_right;
|
||||
|
||||
auto width = min(max(result.preferred_minimum_width, available_width), result.preferred_width);
|
||||
inline_block.set_width(width);
|
||||
} else {
|
||||
inline_block.set_width(inline_block.computed_values().width().resolved_or_zero(inline_block, containing_block().width()).to_px(inline_block));
|
||||
}
|
||||
layout_inside(inline_block, layout_mode);
|
||||
|
||||
if (inline_block.computed_values().height().is_undefined_or_auto()) {
|
||||
// FIXME: (10.6.6) If 'height' is 'auto', the height depends on the element's descendants per 10.6.7.
|
||||
} else {
|
||||
inline_block.set_height(inline_block.computed_values().height().resolved_or_zero(inline_block, containing_block().height()).to_px(inline_block));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-replaced, non-inline-block, box on a line!?
|
||||
// I don't think we should be here. Dump the box tree so we can take a look at it.
|
||||
dbgln("FIXME: I've been asked to dimension a non-replaced, non-inline-block box on a line:");
|
||||
dump_tree(box);
|
||||
}
|
||||
|
||||
}
|
50
Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
Normal file
50
Userland/Libraries/LibWeb/Layout/InlineFormattingContext.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 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/Types.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class InlineFormattingContext final : public FormattingContext {
|
||||
public:
|
||||
InlineFormattingContext(Box& containing_block, FormattingContext* parent);
|
||||
~InlineFormattingContext();
|
||||
|
||||
Box& containing_block() { return context_box(); }
|
||||
const Box& containing_block() const { return context_box(); }
|
||||
|
||||
virtual void run(Box&, LayoutMode) override;
|
||||
|
||||
float available_width_at_line(size_t line_index) const;
|
||||
|
||||
void dimension_box_on_line(Box&, LayoutMode);
|
||||
};
|
||||
|
||||
}
|
71
Userland/Libraries/LibWeb/Layout/InlineNode.cpp
Normal file
71
Userland/Libraries/LibWeb/Layout/InlineNode.cpp
Normal 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 <LibGfx/Painter.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
InlineNode::InlineNode(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Layout::NodeWithStyleAndBoxModelMetrics(document, &element, move(style))
|
||||
{
|
||||
set_inline(true);
|
||||
}
|
||||
|
||||
InlineNode::~InlineNode()
|
||||
{
|
||||
}
|
||||
|
||||
void InlineNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
auto& containing_block = context.context_box();
|
||||
|
||||
if (!computed_values().padding().left.is_undefined_or_auto()) {
|
||||
float padding_left = computed_values().padding().left.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
|
||||
containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_left, 0, LineBoxFragment::Type::Leading);
|
||||
}
|
||||
|
||||
NodeWithStyleAndBoxModelMetrics::split_into_lines(context, layout_mode);
|
||||
|
||||
if (!computed_values().padding().right.is_undefined_or_auto()) {
|
||||
float padding_right = computed_values().padding().right.resolved(CSS::Length::make_px(0), *this, containing_block.width()).to_px(*this);
|
||||
containing_block.ensure_last_line_box().add_fragment(*this, 0, 0, padding_right, 0, LineBoxFragment::Type::Trailing);
|
||||
}
|
||||
}
|
||||
|
||||
void InlineNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const
|
||||
{
|
||||
auto& painter = context.painter();
|
||||
|
||||
if (phase == PaintPhase::Background) {
|
||||
painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
43
Userland/Libraries/LibWeb/Layout/InlineNode.h
Normal file
43
Userland/Libraries/LibWeb/Layout/InlineNode.h
Normal 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 <LibWeb/Layout/Box.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class InlineNode : public NodeWithStyleAndBoxModelMetrics {
|
||||
public:
|
||||
InlineNode(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~InlineNode() override;
|
||||
|
||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
|
||||
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
};
|
||||
|
||||
}
|
67
Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp
Normal file
67
Userland/Libraries/LibWeb/Layout/LayoutPosition.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 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/Position.h>
|
||||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/Layout/LayoutPosition.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
DOM::Position LayoutPosition::to_dom_position() const
|
||||
{
|
||||
if (!layout_node)
|
||||
return {};
|
||||
|
||||
// FIXME: Verify that there are no shenanigans going on.
|
||||
return { const_cast<DOM::Node&>(*layout_node->dom_node()), (unsigned)index_in_node };
|
||||
}
|
||||
|
||||
LayoutRange LayoutRange::normalized() const
|
||||
{
|
||||
if (!is_valid())
|
||||
return {};
|
||||
if (m_start.layout_node == m_end.layout_node) {
|
||||
if (m_start.index_in_node < m_end.index_in_node)
|
||||
return *this;
|
||||
return { m_end, m_start };
|
||||
}
|
||||
if (m_start.layout_node->is_before(*m_end.layout_node))
|
||||
return *this;
|
||||
return { m_end, m_start };
|
||||
}
|
||||
|
||||
NonnullRefPtr<DOM::Range> LayoutRange::to_dom_range() const
|
||||
{
|
||||
ASSERT(is_valid());
|
||||
|
||||
auto start = m_start.to_dom_position();
|
||||
auto end = m_end.to_dom_position();
|
||||
|
||||
return DOM::Range::create(*start.node(), start.offset(), *end.node(), end.offset());
|
||||
}
|
||||
|
||||
}
|
78
Userland/Libraries/LibWeb/Layout/LayoutPosition.h
Normal file
78
Userland/Libraries/LibWeb/Layout/LayoutPosition.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class Node;
|
||||
|
||||
struct LayoutPosition {
|
||||
RefPtr<Node> layout_node;
|
||||
int index_in_node { 0 };
|
||||
|
||||
DOM::Position to_dom_position() const;
|
||||
};
|
||||
|
||||
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; }
|
||||
LayoutPosition& start() { return m_start; }
|
||||
const LayoutPosition& end() const { return m_end; }
|
||||
LayoutPosition& end() { return m_end; }
|
||||
|
||||
LayoutRange normalized() const;
|
||||
|
||||
NonnullRefPtr<DOM::Range> to_dom_range() const;
|
||||
|
||||
private:
|
||||
LayoutPosition m_start;
|
||||
LayoutPosition m_end;
|
||||
};
|
||||
|
||||
}
|
83
Userland/Libraries/LibWeb/Layout/LineBox.cpp
Normal file
83
Userland/Libraries/LibWeb/Layout/LineBox.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* 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/Box.h>
|
||||
#include <LibWeb/Layout/LineBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
void LineBox::add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type fragment_type)
|
||||
{
|
||||
bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::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 Layout::Node on the line.
|
||||
// Expand the last fragment instead of adding a new one with the same Layout::Node.
|
||||
m_fragments.last().m_length = (start - m_fragments.last().m_start) + length;
|
||||
m_fragments.last().set_width(m_fragments.last().width() + width);
|
||||
} else {
|
||||
m_fragments.append(make<LineBoxFragment>(layout_node, start, length, Gfx::FloatPoint(m_width, 0.0f), Gfx::FloatSize(width, height), fragment_type));
|
||||
}
|
||||
m_width += width;
|
||||
|
||||
if (is<Box>(layout_node))
|
||||
downcast<Box>(layout_node).set_containing_line_box_fragment(m_fragments.last());
|
||||
}
|
||||
|
||||
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().font().glyph_width(' ');
|
||||
while (last_fragment.length() && isspace(last_text[last_fragment.length() - 1])) {
|
||||
last_fragment.m_length -= 1;
|
||||
last_fragment.set_width(last_fragment.width() - space_width);
|
||||
m_width -= space_width;
|
||||
}
|
||||
}
|
||||
|
||||
bool LineBox::is_empty_or_ends_in_whitespace() const
|
||||
{
|
||||
if (m_fragments.is_empty())
|
||||
return true;
|
||||
return m_fragments.last().ends_in_whitespace();
|
||||
}
|
||||
|
||||
}
|
57
Userland/Libraries/LibWeb/Layout/LineBox.h
Normal file
57
Userland/Libraries/LibWeb/Layout/LineBox.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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/NonnullOwnPtrVector.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibWeb/Layout/LineBoxFragment.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class LineBox {
|
||||
public:
|
||||
LineBox() { }
|
||||
|
||||
float width() const { return m_width; }
|
||||
|
||||
void add_fragment(Node& layout_node, int start, int length, float width, float height, LineBoxFragment::Type = LineBoxFragment::Type::Normal);
|
||||
|
||||
const NonnullOwnPtrVector<LineBoxFragment>& fragments() const { return m_fragments; }
|
||||
NonnullOwnPtrVector<LineBoxFragment>& fragments() { return m_fragments; }
|
||||
|
||||
void trim_trailing_whitespace();
|
||||
|
||||
bool is_empty_or_ends_in_whitespace() const;
|
||||
|
||||
private:
|
||||
friend class BlockBox;
|
||||
friend class InlineFormattingContext;
|
||||
NonnullOwnPtrVector<LineBoxFragment> m_fragments;
|
||||
float m_width { 0 };
|
||||
};
|
||||
|
||||
}
|
174
Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp
Normal file
174
Userland/Libraries/LibWeb/Layout/LineBoxFragment.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/LineBoxFragment.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Painting/BorderPainting.h>
|
||||
#include <LibWeb/Painting/PaintContext.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
void LineBoxFragment::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
for (auto* ancestor = layout_node().parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (!ancestor->is_visible())
|
||||
return;
|
||||
}
|
||||
|
||||
layout_node().paint_fragment(context, *this, phase);
|
||||
}
|
||||
|
||||
bool LineBoxFragment::ends_in_whitespace() const
|
||||
{
|
||||
auto text = this->text();
|
||||
if (text.is_empty())
|
||||
return false;
|
||||
return isspace(text[text.length() - 1]);
|
||||
}
|
||||
|
||||
bool LineBoxFragment::is_justifiable_whitespace() const
|
||||
{
|
||||
return text() == " ";
|
||||
}
|
||||
|
||||
StringView LineBoxFragment::text() const
|
||||
{
|
||||
if (!is<TextNode>(layout_node()))
|
||||
return {};
|
||||
return downcast<TextNode>(layout_node()).text_for_rendering().substring_view(m_start, m_length);
|
||||
}
|
||||
|
||||
const Gfx::FloatRect LineBoxFragment::absolute_rect() const
|
||||
{
|
||||
Gfx::FloatRect rect { {}, size() };
|
||||
rect.set_location(m_layout_node.containing_block()->absolute_position());
|
||||
rect.move_by(offset());
|
||||
return rect;
|
||||
}
|
||||
|
||||
int LineBoxFragment::text_index_at(float x) const
|
||||
{
|
||||
if (!is<TextNode>(layout_node()))
|
||||
return 0;
|
||||
auto& layout_text = downcast<TextNode>(layout_node());
|
||||
auto& font = layout_text.font();
|
||||
Utf8View view(text());
|
||||
|
||||
float relative_x = x - absolute_x();
|
||||
float glyph_spacing = font.glyph_spacing();
|
||||
|
||||
if (relative_x < 0)
|
||||
return 0;
|
||||
|
||||
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) / 2) > relative_x)
|
||||
return m_start + view.byte_offset_of(it);
|
||||
width_so_far += glyph_width + glyph_spacing;
|
||||
}
|
||||
return m_start + m_length;
|
||||
}
|
||||
|
||||
Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
||||
{
|
||||
if (layout_node().selection_state() == Node::SelectionState::None)
|
||||
return {};
|
||||
|
||||
if (layout_node().selection_state() == Node::SelectionState::Full)
|
||||
return absolute_rect();
|
||||
|
||||
auto selection = layout_node().root().selection().normalized();
|
||||
if (!selection.is_valid())
|
||||
return {};
|
||||
if (!is<TextNode>(layout_node()))
|
||||
return {};
|
||||
|
||||
const auto start_index = m_start;
|
||||
const auto end_index = m_start + m_length;
|
||||
auto text = this->text();
|
||||
|
||||
if (layout_node().selection_state() == Node::SelectionState::StartAndEnd) {
|
||||
// we are in the start/end node (both the same)
|
||||
if (start_index > selection.end().index_in_node)
|
||||
return {};
|
||||
if (end_index < selection.start().index_in_node)
|
||||
return {};
|
||||
|
||||
if (selection.start().index_in_node == selection.end().index_in_node)
|
||||
return {};
|
||||
|
||||
auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start);
|
||||
auto selection_end_in_this_fragment = min(m_length, selection.end().index_in_node - m_start);
|
||||
auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
|
||||
auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
|
||||
|
||||
auto rect = absolute_rect();
|
||||
rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
|
||||
rect.set_width(pixel_width_of_selection);
|
||||
|
||||
return rect;
|
||||
}
|
||||
if (layout_node().selection_state() == Node::SelectionState::Start) {
|
||||
// we are in the start node
|
||||
if (end_index < selection.start().index_in_node)
|
||||
return {};
|
||||
|
||||
auto selection_start_in_this_fragment = max(0, selection.start().index_in_node - m_start);
|
||||
auto selection_end_in_this_fragment = m_length;
|
||||
auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
|
||||
auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
|
||||
|
||||
auto rect = absolute_rect();
|
||||
rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
|
||||
rect.set_width(pixel_width_of_selection);
|
||||
|
||||
return rect;
|
||||
}
|
||||
if (layout_node().selection_state() == Node::SelectionState::End) {
|
||||
// we are in the end node
|
||||
if (start_index > selection.end().index_in_node)
|
||||
return {};
|
||||
|
||||
auto selection_start_in_this_fragment = 0;
|
||||
auto selection_end_in_this_fragment = min(selection.end().index_in_node, m_length);
|
||||
auto pixel_distance_to_first_selected_character = font.width(text.substring_view(0, selection_start_in_this_fragment));
|
||||
auto pixel_width_of_selection = font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment)) + 1;
|
||||
|
||||
auto rect = absolute_rect();
|
||||
rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
|
||||
rect.set_width(pixel_width_of_selection);
|
||||
|
||||
return rect;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
92
Userland/Libraries/LibWeb/Layout/LineBoxFragment.h
Normal file
92
Userland/Libraries/LibWeb/Layout/LineBoxFragment.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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/Weakable.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class LineBoxFragment : public Weakable<LineBoxFragment> {
|
||||
friend class LineBox;
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
Normal,
|
||||
Leading,
|
||||
Trailing,
|
||||
};
|
||||
|
||||
LineBoxFragment(Node& layout_node, int start, int length, const Gfx::FloatPoint& offset, const Gfx::FloatSize& size, Type type)
|
||||
: m_layout_node(layout_node)
|
||||
, m_start(start)
|
||||
, m_length(length)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
Node& layout_node() const { return m_layout_node; }
|
||||
int start() const { return m_start; }
|
||||
int length() const { return m_length; }
|
||||
const Gfx::FloatRect absolute_rect() const;
|
||||
Type type() const { return m_type; }
|
||||
|
||||
const Gfx::FloatPoint& offset() const { return m_offset; }
|
||||
void set_offset(const Gfx::FloatPoint& offset) { m_offset = offset; }
|
||||
|
||||
const Gfx::FloatSize& size() const { return m_size; }
|
||||
void set_width(float width) { m_size.set_width(width); }
|
||||
void set_height(float height) { m_size.set_height(height); }
|
||||
float width() const { return m_size.width(); }
|
||||
float height() const { return m_size.height(); }
|
||||
|
||||
float absolute_x() const { return absolute_rect().x(); }
|
||||
|
||||
void paint(PaintContext&, PaintPhase);
|
||||
|
||||
bool ends_in_whitespace() const;
|
||||
bool is_justifiable_whitespace() const;
|
||||
StringView text() const;
|
||||
|
||||
int text_index_at(float x) const;
|
||||
|
||||
Gfx::FloatRect selection_rect(const Gfx::Font&) const;
|
||||
|
||||
private:
|
||||
Node& m_layout_node;
|
||||
int m_start { 0 };
|
||||
int m_length { 0 };
|
||||
Gfx::FloatPoint m_offset;
|
||||
Gfx::FloatSize m_size;
|
||||
Type m_type { Type::Normal };
|
||||
};
|
||||
|
||||
}
|
62
Userland/Libraries/LibWeb/Layout/ListItemBox.cpp
Normal file
62
Userland/Libraries/LibWeb/Layout/ListItemBox.cpp
Normal 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.
|
||||
*/
|
||||
|
||||
#include <LibWeb/Layout/ListItemBox.h>
|
||||
#include <LibWeb/Layout/ListItemMarkerBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
ListItemBox::ListItemBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Layout::BlockBox(document, &element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
ListItemBox::~ListItemBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ListItemBox::layout_marker()
|
||||
{
|
||||
if (m_marker) {
|
||||
remove_child(*m_marker);
|
||||
m_marker = nullptr;
|
||||
}
|
||||
|
||||
if (computed_values().list_style_type() == CSS::ListStyleType::None)
|
||||
return;
|
||||
|
||||
if (!m_marker) {
|
||||
m_marker = adopt(*new ListItemMarkerBox(document()));
|
||||
if (first_child())
|
||||
m_marker->set_inline(first_child()->is_inline());
|
||||
append_child(*m_marker);
|
||||
}
|
||||
|
||||
m_marker->set_offset(-8, 0);
|
||||
m_marker->set_size(4, height());
|
||||
}
|
||||
|
||||
}
|
47
Userland/Libraries/LibWeb/Layout/ListItemBox.h
Normal file
47
Userland/Libraries/LibWeb/Layout/ListItemBox.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class ListItemMarkerBox;
|
||||
|
||||
class ListItemBox final : public BlockBox {
|
||||
public:
|
||||
ListItemBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~ListItemBox() override;
|
||||
|
||||
void layout_marker();
|
||||
|
||||
private:
|
||||
RefPtr<ListItemMarkerBox> m_marker;
|
||||
};
|
||||
|
||||
}
|
52
Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp
Normal file
52
Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp
Normal 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.
|
||||
*/
|
||||
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibWeb/Layout/ListItemMarkerBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document)
|
||||
: Box(document, nullptr, CSS::StyleProperties::create())
|
||||
{
|
||||
}
|
||||
|
||||
ListItemMarkerBox::~ListItemMarkerBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ListItemMarkerBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
Gfx::IntRect bullet_rect { 0, 0, 4, 4 };
|
||||
bullet_rect.center_within(enclosing_int_rect(absolute_rect()));
|
||||
// FIXME: It would be nicer to not have to go via the parent here to get our inherited style.
|
||||
auto color = parent()->computed_values().color();
|
||||
context.painter().fill_rect(bullet_rect, color);
|
||||
}
|
||||
|
||||
}
|
41
Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h
Normal file
41
Userland/Libraries/LibWeb/Layout/ListItemMarkerBox.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class ListItemMarkerBox final : public Box {
|
||||
public:
|
||||
explicit ListItemMarkerBox(DOM::Document&);
|
||||
virtual ~ListItemMarkerBox() override;
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) override;
|
||||
};
|
||||
|
||||
}
|
339
Userland/Libraries/LibWeb/Layout/Node.cpp
Normal file
339
Userland/Libraries/LibWeb/Layout/Node.cpp
Normal file
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* 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/Demangle.h>
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/FormattingContext.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
#include <typeinfo>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
Node::Node(DOM::Document& document, DOM::Node* node)
|
||||
: m_document(document)
|
||||
, m_dom_node(node)
|
||||
{
|
||||
if (m_dom_node)
|
||||
m_dom_node->set_layout_node({}, this);
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
if (m_dom_node && m_dom_node->layout_node() == this)
|
||||
m_dom_node->set_layout_node({}, nullptr);
|
||||
}
|
||||
|
||||
bool Node::can_contain_boxes_with_position_absolute() const
|
||||
{
|
||||
return computed_values().position() != CSS::Position::Static || is<InitialContainingBlockBox>(*this);
|
||||
}
|
||||
|
||||
const BlockBox* Node::containing_block() const
|
||||
{
|
||||
auto nearest_block_ancestor = [this] {
|
||||
auto* ancestor = parent();
|
||||
while (ancestor && !is<BlockBox>(*ancestor))
|
||||
ancestor = ancestor->parent();
|
||||
return downcast<BlockBox>(ancestor);
|
||||
};
|
||||
|
||||
if (is<TextNode>(*this))
|
||||
return nearest_block_ancestor();
|
||||
|
||||
auto position = computed_values().position();
|
||||
|
||||
if (position == CSS::Position::Absolute) {
|
||||
auto* ancestor = parent();
|
||||
while (ancestor && !ancestor->can_contain_boxes_with_position_absolute())
|
||||
ancestor = ancestor->parent();
|
||||
while (ancestor && (!is<BlockBox>(ancestor) || ancestor->is_anonymous()))
|
||||
ancestor = ancestor->containing_block();
|
||||
return downcast<BlockBox>(ancestor);
|
||||
}
|
||||
|
||||
if (position == CSS::Position::Fixed)
|
||||
return &root();
|
||||
|
||||
return nearest_block_ancestor();
|
||||
}
|
||||
|
||||
void Node::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
before_children_paint(context, phase);
|
||||
|
||||
for_each_child_in_paint_order([&](auto& child) {
|
||||
child.paint(context, phase);
|
||||
});
|
||||
|
||||
after_children_paint(context, phase);
|
||||
}
|
||||
|
||||
HitTestResult Node::hit_test(const Gfx::IntPoint& position, HitTestType type) const
|
||||
{
|
||||
HitTestResult result;
|
||||
for_each_child_in_paint_order([&](auto& child) {
|
||||
auto child_result = child.hit_test(position, type);
|
||||
if (child_result.layout_node)
|
||||
result = child_result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const Frame& Node::frame() const
|
||||
{
|
||||
ASSERT(document().frame());
|
||||
return *document().frame();
|
||||
}
|
||||
|
||||
Frame& Node::frame()
|
||||
{
|
||||
ASSERT(document().frame());
|
||||
return *document().frame();
|
||||
}
|
||||
|
||||
const InitialContainingBlockBox& Node::root() const
|
||||
{
|
||||
ASSERT(document().layout_node());
|
||||
return *document().layout_node();
|
||||
}
|
||||
|
||||
InitialContainingBlockBox& Node::root()
|
||||
{
|
||||
ASSERT(document().layout_node());
|
||||
return *document().layout_node();
|
||||
}
|
||||
|
||||
void Node::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
for_each_child([&](auto& child) {
|
||||
child.split_into_lines(context, layout_mode);
|
||||
});
|
||||
}
|
||||
|
||||
void Node::set_needs_display()
|
||||
{
|
||||
if (auto* block = containing_block()) {
|
||||
block->for_each_fragment([&](auto& fragment) {
|
||||
if (&fragment.layout_node() == this || is_ancestor_of(fragment.layout_node())) {
|
||||
frame().set_needs_display(enclosing_int_rect(fragment.absolute_rect()));
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Gfx::FloatPoint Node::box_type_agnostic_position() const
|
||||
{
|
||||
if (is<Box>(*this))
|
||||
return downcast<Box>(*this).absolute_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.absolute_rect().location();
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
bool Node::is_floating() const
|
||||
{
|
||||
if (!has_style())
|
||||
return false;
|
||||
return computed_values().float_() != CSS::Float::None;
|
||||
}
|
||||
|
||||
bool Node::is_positioned() const
|
||||
{
|
||||
return has_style() && computed_values().position() != CSS::Position::Static;
|
||||
}
|
||||
|
||||
bool Node::is_absolutely_positioned() const
|
||||
{
|
||||
if (!has_style())
|
||||
return false;
|
||||
auto position = computed_values().position();
|
||||
return position == CSS::Position::Absolute || position == CSS::Position::Fixed;
|
||||
}
|
||||
|
||||
bool Node::is_fixed_position() const
|
||||
{
|
||||
if (!has_style())
|
||||
return false;
|
||||
auto position = computed_values().position();
|
||||
return position == CSS::Position::Fixed;
|
||||
}
|
||||
|
||||
NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> specified_style)
|
||||
: Node(document, node)
|
||||
{
|
||||
m_has_style = true;
|
||||
apply_style(*specified_style);
|
||||
}
|
||||
|
||||
NodeWithStyle::NodeWithStyle(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
|
||||
: Node(document, node)
|
||||
, m_computed_values(move(computed_values))
|
||||
{
|
||||
m_has_style = true;
|
||||
m_font = Gfx::FontDatabase::default_font();
|
||||
}
|
||||
|
||||
void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style)
|
||||
{
|
||||
auto& computed_values = static_cast<CSS::MutableComputedValues&>(m_computed_values);
|
||||
|
||||
m_font = specified_style.font();
|
||||
m_line_height = specified_style.line_height(*this);
|
||||
|
||||
{
|
||||
// FIXME: This doesn't work right for relative font-sizes
|
||||
auto length = specified_style.length_or_fallback(CSS::PropertyID::FontSize, CSS::Length(10, CSS::Length::Type::Px));
|
||||
m_font_size = length.raw_value();
|
||||
}
|
||||
|
||||
auto bgimage = specified_style.property(CSS::PropertyID::BackgroundImage);
|
||||
if (bgimage.has_value() && bgimage.value()->is_image()) {
|
||||
m_background_image = static_ptr_cast<CSS::ImageStyleValue>(bgimage.value());
|
||||
}
|
||||
|
||||
computed_values.set_display(specified_style.display());
|
||||
|
||||
auto position = specified_style.position();
|
||||
if (position.has_value())
|
||||
computed_values.set_position(position.value());
|
||||
|
||||
auto text_align = specified_style.text_align();
|
||||
if (text_align.has_value())
|
||||
computed_values.set_text_align(text_align.value());
|
||||
|
||||
auto white_space = specified_style.white_space();
|
||||
if (white_space.has_value())
|
||||
computed_values.set_white_space(white_space.value());
|
||||
|
||||
auto float_ = specified_style.float_();
|
||||
if (float_.has_value())
|
||||
computed_values.set_float(float_.value());
|
||||
|
||||
auto clear = specified_style.clear();
|
||||
if (clear.has_value())
|
||||
computed_values.set_clear(clear.value());
|
||||
|
||||
auto text_decoration_line = specified_style.text_decoration_line();
|
||||
if (text_decoration_line.has_value())
|
||||
computed_values.set_text_decoration_line(text_decoration_line.value());
|
||||
|
||||
auto text_transform = specified_style.text_transform();
|
||||
if (text_transform.has_value())
|
||||
computed_values.set_text_transform(text_transform.value());
|
||||
|
||||
if (auto list_style_type = specified_style.list_style_type(); list_style_type.has_value())
|
||||
computed_values.set_list_style_type(list_style_type.value());
|
||||
|
||||
computed_values.set_color(specified_style.color_or_fallback(CSS::PropertyID::Color, document(), Color::Black));
|
||||
computed_values.set_background_color(specified_style.color_or_fallback(CSS::PropertyID::BackgroundColor, document(), Color::Transparent));
|
||||
|
||||
computed_values.set_z_index(specified_style.z_index());
|
||||
computed_values.set_width(specified_style.length_or_fallback(CSS::PropertyID::Width, {}));
|
||||
computed_values.set_min_width(specified_style.length_or_fallback(CSS::PropertyID::MinWidth, {}));
|
||||
computed_values.set_max_width(specified_style.length_or_fallback(CSS::PropertyID::MaxWidth, {}));
|
||||
computed_values.set_height(specified_style.length_or_fallback(CSS::PropertyID::Height, {}));
|
||||
computed_values.set_min_height(specified_style.length_or_fallback(CSS::PropertyID::MinHeight, {}));
|
||||
computed_values.set_max_height(specified_style.length_or_fallback(CSS::PropertyID::MaxHeight, {}));
|
||||
|
||||
computed_values.set_offset(specified_style.length_box(CSS::PropertyID::Left, CSS::PropertyID::Top, CSS::PropertyID::Right, CSS::PropertyID::Bottom, CSS::Length::make_auto()));
|
||||
computed_values.set_margin(specified_style.length_box(CSS::PropertyID::MarginLeft, CSS::PropertyID::MarginTop, CSS::PropertyID::MarginRight, CSS::PropertyID::MarginBottom, CSS::Length::make_px(0)));
|
||||
computed_values.set_padding(specified_style.length_box(CSS::PropertyID::PaddingLeft, CSS::PropertyID::PaddingTop, CSS::PropertyID::PaddingRight, CSS::PropertyID::PaddingBottom, CSS::Length::make_px(0)));
|
||||
|
||||
auto do_border_style = [&](CSS::BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) {
|
||||
border.width = specified_style.length_or_fallback(width_property, {}).resolved_or_zero(*this, 0).to_px(*this);
|
||||
border.color = specified_style.color_or_fallback(color_property, document(), Color::Transparent);
|
||||
border.line_style = specified_style.line_style(style_property).value_or(CSS::LineStyle::None);
|
||||
};
|
||||
|
||||
do_border_style(computed_values.border_left(), CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftStyle);
|
||||
do_border_style(computed_values.border_top(), CSS::PropertyID::BorderTopWidth, CSS::PropertyID::BorderTopColor, CSS::PropertyID::BorderTopStyle);
|
||||
do_border_style(computed_values.border_right(), CSS::PropertyID::BorderRightWidth, CSS::PropertyID::BorderRightColor, CSS::PropertyID::BorderRightStyle);
|
||||
do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle);
|
||||
}
|
||||
|
||||
void Node::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
|
||||
{
|
||||
}
|
||||
|
||||
void Node::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
|
||||
{
|
||||
}
|
||||
|
||||
void Node::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned)
|
||||
{
|
||||
}
|
||||
|
||||
bool Node::is_root_element() const
|
||||
{
|
||||
if (is_anonymous())
|
||||
return false;
|
||||
return is<HTML::HTMLHtmlElement>(*dom_node());
|
||||
}
|
||||
|
||||
String Node::class_name() const
|
||||
{
|
||||
return demangle(typeid(*this).name());
|
||||
}
|
||||
|
||||
bool Node::is_inline_block() const
|
||||
{
|
||||
return is_inline() && is<BlockBox>(*this);
|
||||
}
|
||||
|
||||
NonnullRefPtr<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
|
||||
{
|
||||
auto wrapper = adopt(*new BlockBox(const_cast<DOM::Document&>(document()), nullptr, m_computed_values.clone_inherited_values()));
|
||||
wrapper->m_font = m_font;
|
||||
wrapper->m_font_size = m_font_size;
|
||||
wrapper->m_line_height = m_line_height;
|
||||
wrapper->m_background_image = m_background_image;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
}
|
278
Userland/Libraries/LibWeb/Layout/Node.h
Normal file
278
Userland/Libraries/LibWeb/Layout/Node.h
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, 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/TypeCasts.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibWeb/CSS/ComputedValues.h>
|
||||
#include <LibWeb/CSS/StyleProperties.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Layout/BoxModelMetrics.h>
|
||||
#include <LibWeb/Layout/LayoutPosition.h>
|
||||
#include <LibWeb/Painting/PaintContext.h>
|
||||
#include <LibWeb/TreeNode.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
enum class LayoutMode {
|
||||
Default,
|
||||
AllPossibleLineBreaks,
|
||||
OnlyRequiredLineBreaks,
|
||||
};
|
||||
|
||||
enum class PaintPhase {
|
||||
Background,
|
||||
Border,
|
||||
Foreground,
|
||||
FocusOutline,
|
||||
Overlay,
|
||||
};
|
||||
|
||||
struct HitTestResult {
|
||||
RefPtr<Node> layout_node;
|
||||
int index_in_node { 0 };
|
||||
|
||||
enum InternalPosition {
|
||||
None,
|
||||
Before,
|
||||
Inside,
|
||||
After,
|
||||
};
|
||||
InternalPosition internal_position { None };
|
||||
};
|
||||
|
||||
enum class HitTestType {
|
||||
Exact, // Exact matches only
|
||||
TextCursor, // Clicking past the right/bottom edge of text will still hit the text
|
||||
};
|
||||
|
||||
class Node : public TreeNode<Node> {
|
||||
public:
|
||||
virtual ~Node();
|
||||
|
||||
virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const;
|
||||
|
||||
bool is_anonymous() const { return !m_dom_node; }
|
||||
const DOM::Node* dom_node() const { return m_dom_node; }
|
||||
DOM::Node* dom_node() { return m_dom_node; }
|
||||
|
||||
DOM::Document& document() { return m_document; }
|
||||
const DOM::Document& document() const { return m_document; }
|
||||
|
||||
const Frame& frame() const;
|
||||
Frame& frame();
|
||||
|
||||
const InitialContainingBlockBox& root() const;
|
||||
InitialContainingBlockBox& root();
|
||||
|
||||
bool is_root_element() const;
|
||||
|
||||
String class_name() const;
|
||||
|
||||
bool has_style() const { return m_has_style; }
|
||||
|
||||
virtual bool can_have_children() const { return true; }
|
||||
|
||||
bool is_inline() const { return m_inline; }
|
||||
void set_inline(bool b) { m_inline = b; }
|
||||
|
||||
bool is_inline_block() const;
|
||||
|
||||
virtual bool wants_mouse_events() const { return false; }
|
||||
|
||||
virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
||||
virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
|
||||
virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
|
||||
|
||||
virtual void before_children_paint(PaintContext&, PaintPhase) {};
|
||||
virtual void paint(PaintContext&, PaintPhase);
|
||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const { }
|
||||
virtual void after_children_paint(PaintContext&, PaintPhase) {};
|
||||
|
||||
virtual bool is_box() const { return false; }
|
||||
|
||||
bool is_floating() const;
|
||||
bool is_positioned() const;
|
||||
bool is_absolutely_positioned() const;
|
||||
bool is_fixed_position() const;
|
||||
|
||||
const BlockBox* containing_block() const;
|
||||
BlockBox* containing_block() { return const_cast<BlockBox*>(const_cast<const Node*>(this)->containing_block()); }
|
||||
|
||||
bool can_contain_boxes_with_position_absolute() const;
|
||||
|
||||
const Gfx::Font& font() const;
|
||||
const CSS::ImmutableComputedValues& computed_values() const;
|
||||
|
||||
NodeWithStyle* parent();
|
||||
const NodeWithStyle* parent() const;
|
||||
|
||||
void inserted_into(Node&) { }
|
||||
void removed_from(Node&) { }
|
||||
void children_changed() { }
|
||||
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode);
|
||||
|
||||
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; }
|
||||
|
||||
Gfx::FloatPoint box_type_agnostic_position() const;
|
||||
|
||||
float font_size() const;
|
||||
|
||||
enum class SelectionState {
|
||||
None, // No selection
|
||||
Start, // Selection starts in this Node
|
||||
End, // Selection ends in this Node
|
||||
StartAndEnd, // Selection starts and ends in this Node
|
||||
Full, // Selection starts before and ends after this Node
|
||||
};
|
||||
|
||||
SelectionState selection_state() const { return m_selection_state; }
|
||||
void set_selection_state(SelectionState state) { m_selection_state = state; }
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_child_in_paint_order(Callback callback) const
|
||||
{
|
||||
for_each_child([&](auto& child) {
|
||||
if (is<Box>(child) && downcast<Box>(child).stacking_context())
|
||||
return;
|
||||
if (!child.is_positioned())
|
||||
callback(child);
|
||||
});
|
||||
for_each_child([&](auto& child) {
|
||||
if (is<Box>(child) && downcast<Box>(child).stacking_context())
|
||||
return;
|
||||
if (child.is_positioned())
|
||||
callback(child);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
Node(DOM::Document&, DOM::Node*);
|
||||
|
||||
private:
|
||||
friend class NodeWithStyle;
|
||||
|
||||
NonnullRefPtr<DOM::Document> m_document;
|
||||
RefPtr<DOM::Node> m_dom_node;
|
||||
|
||||
bool m_inline { false };
|
||||
bool m_has_style { false };
|
||||
bool m_visible { true };
|
||||
bool m_children_are_inline { false };
|
||||
SelectionState m_selection_state { SelectionState::None };
|
||||
};
|
||||
|
||||
class NodeWithStyle : public Node {
|
||||
public:
|
||||
virtual ~NodeWithStyle() override { }
|
||||
|
||||
const CSS::ImmutableComputedValues& computed_values() const { return static_cast<const CSS::ImmutableComputedValues&>(m_computed_values); }
|
||||
|
||||
void apply_style(const CSS::StyleProperties&);
|
||||
|
||||
const Gfx::Font& font() const { return *m_font; }
|
||||
float line_height() const { return m_line_height; }
|
||||
float font_size() const { return m_font_size; }
|
||||
const CSS::ImageStyleValue* background_image() const { return m_background_image; }
|
||||
|
||||
NonnullRefPtr<NodeWithStyle> create_anonymous_wrapper() const;
|
||||
|
||||
protected:
|
||||
NodeWithStyle(DOM::Document&, DOM::Node*, NonnullRefPtr<CSS::StyleProperties>);
|
||||
NodeWithStyle(DOM::Document&, DOM::Node*, CSS::ComputedValues);
|
||||
|
||||
private:
|
||||
CSS::ComputedValues m_computed_values;
|
||||
RefPtr<Gfx::Font> m_font;
|
||||
float m_line_height { 0 };
|
||||
float m_font_size { 0 };
|
||||
RefPtr<CSS::ImageStyleValue> m_background_image;
|
||||
|
||||
CSS::Position m_position;
|
||||
};
|
||||
|
||||
class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
|
||||
public:
|
||||
BoxModelMetrics& box_model() { return m_box_model; }
|
||||
const BoxModelMetrics& box_model() const { return m_box_model; }
|
||||
|
||||
protected:
|
||||
NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: NodeWithStyle(document, node, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
NodeWithStyleAndBoxModelMetrics(DOM::Document& document, DOM::Node* node, CSS::ComputedValues computed_values)
|
||||
: NodeWithStyle(document, node, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
BoxModelMetrics m_box_model;
|
||||
};
|
||||
|
||||
inline const Gfx::Font& Node::font() const
|
||||
{
|
||||
if (m_has_style)
|
||||
return static_cast<const NodeWithStyle*>(this)->font();
|
||||
return parent()->font();
|
||||
}
|
||||
|
||||
inline float Node::font_size() const
|
||||
{
|
||||
if (m_has_style)
|
||||
return static_cast<const NodeWithStyle*>(this)->font_size();
|
||||
return parent()->font_size();
|
||||
}
|
||||
|
||||
inline const CSS::ImmutableComputedValues& Node::computed_values() const
|
||||
{
|
||||
if (m_has_style)
|
||||
return static_cast<const NodeWithStyle*>(this)->computed_values();
|
||||
return parent()->computed_values();
|
||||
}
|
||||
|
||||
inline const NodeWithStyle* Node::parent() const
|
||||
{
|
||||
return static_cast<const NodeWithStyle*>(TreeNode<Node>::parent());
|
||||
}
|
||||
|
||||
inline NodeWithStyle* Node::parent()
|
||||
{
|
||||
return static_cast<NodeWithStyle*>(TreeNode<Node>::parent());
|
||||
}
|
||||
|
||||
}
|
58
Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp
Normal file
58
Userland/Libraries/LibWeb/Layout/ReplacedBox.cpp
Normal 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/BlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
ReplacedBox::ReplacedBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Box(document, &element, move(style))
|
||||
{
|
||||
// FIXME: Allow non-inline replaced elements.
|
||||
set_inline(true);
|
||||
}
|
||||
|
||||
ReplacedBox::~ReplacedBox()
|
||||
{
|
||||
}
|
||||
|
||||
void ReplacedBox::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
auto& containing_block = context.containing_block();
|
||||
|
||||
prepare_for_replaced_layout();
|
||||
context.dimension_box_on_line(*this, layout_mode);
|
||||
|
||||
auto* line_box = &containing_block.ensure_last_line_box();
|
||||
if (line_box->width() > 0 && line_box->width() + width() > context.available_width_at_line(containing_block.line_boxes().size() - 1))
|
||||
line_box = &containing_block.add_line_box();
|
||||
line_box->add_fragment(*this, 0, 0, width(), height());
|
||||
}
|
||||
|
||||
}
|
74
Userland/Libraries/LibWeb/Layout/ReplacedBox.h
Normal file
74
Userland/Libraries/LibWeb/Layout/ReplacedBox.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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/Box.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class ReplacedBox : public Box {
|
||||
public:
|
||||
ReplacedBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~ReplacedBox() override;
|
||||
|
||||
const DOM::Element& dom_node() const { return downcast<DOM::Element>(*Node::dom_node()); }
|
||||
DOM::Element& dom_node() { return downcast<DOM::Element>(*Node::dom_node()); }
|
||||
|
||||
bool has_intrinsic_width() const { return m_has_intrinsic_width; }
|
||||
bool has_intrinsic_height() const { return m_has_intrinsic_height; }
|
||||
bool has_intrinsic_ratio() const { return m_has_intrinsic_ratio; }
|
||||
|
||||
float intrinsic_width() const { return m_intrinsic_width; }
|
||||
float intrinsic_height() const { return m_intrinsic_height; }
|
||||
float intrinsic_ratio() const { return m_intrinsic_ratio; }
|
||||
|
||||
void set_has_intrinsic_width(bool has) { m_has_intrinsic_width = has; }
|
||||
void set_has_intrinsic_height(bool has) { m_has_intrinsic_height = has; }
|
||||
void set_has_intrinsic_ratio(bool has) { m_has_intrinsic_ratio = has; }
|
||||
|
||||
void set_intrinsic_width(float width) { m_intrinsic_width = width; }
|
||||
void set_intrinsic_height(float height) { m_intrinsic_height = height; }
|
||||
void set_intrinsic_ratio(float ratio) { m_intrinsic_ratio = ratio; }
|
||||
|
||||
virtual void prepare_for_replaced_layout() { }
|
||||
|
||||
virtual bool can_have_children() const override { return false; }
|
||||
|
||||
protected:
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
bool m_has_intrinsic_width { false };
|
||||
bool m_has_intrinsic_height { false };
|
||||
bool m_has_intrinsic_ratio { false };
|
||||
float m_intrinsic_width { 0 };
|
||||
float m_intrinsic_height { 0 };
|
||||
float m_intrinsic_ratio { 0 };
|
||||
};
|
||||
|
||||
}
|
55
Userland/Libraries/LibWeb/Layout/SVGBox.cpp
Normal file
55
Userland/Libraries/LibWeb/Layout/SVGBox.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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 <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/Layout/SVGBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
SVGBox::SVGBox(DOM::Document& document, SVG::SVGElement& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: ReplacedBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
void SVGBox::before_children_paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
Node::before_children_paint(context, phase);
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
context.svg_context().save();
|
||||
}
|
||||
|
||||
void SVGBox::after_children_paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
Node::after_children_paint(context, phase);
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
context.svg_context().restore();
|
||||
}
|
||||
|
||||
}
|
44
Userland/Libraries/LibWeb/Layout/SVGBox.h
Normal file
44
Userland/Libraries/LibWeb/Layout/SVGBox.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/ReplacedBox.h>
|
||||
#include <LibWeb/SVG/SVGElement.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class SVGBox : public ReplacedBox {
|
||||
public:
|
||||
SVGBox(DOM::Document&, SVG::SVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~SVGBox() override = default;
|
||||
|
||||
virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
|
||||
virtual void after_children_paint(PaintContext& context, PaintPhase phase) override;
|
||||
};
|
||||
|
||||
}
|
52
Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp
Normal file
52
Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/SVGGraphicsBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
SVGGraphicsBox::SVGGraphicsBox(DOM::Document& document, SVG::SVGGraphicsElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
|
||||
: SVGBox(document, element, properties)
|
||||
{
|
||||
}
|
||||
|
||||
void SVGGraphicsBox::before_children_paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
SVGBox::before_children_paint(context, phase);
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
|
||||
auto& graphics_element = downcast<SVG::SVGGraphicsElement>(dom_node());
|
||||
|
||||
if (graphics_element.fill_color().has_value())
|
||||
context.svg_context().set_fill_color(graphics_element.fill_color().value());
|
||||
if (graphics_element.stroke_color().has_value())
|
||||
context.svg_context().set_stroke_color(graphics_element.stroke_color().value());
|
||||
if (graphics_element.stroke_width().has_value())
|
||||
context.svg_context().set_stroke_width(graphics_element.stroke_width().value());
|
||||
}
|
||||
|
||||
}
|
43
Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h
Normal file
43
Userland/Libraries/LibWeb/Layout/SVGGraphicsBox.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/SVGBox.h>
|
||||
#include <LibWeb/SVG/SVGElement.h>
|
||||
#include <LibWeb/SVG/SVGGraphicsElement.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class SVGGraphicsBox : public SVGBox {
|
||||
public:
|
||||
SVGGraphicsBox(DOM::Document&, SVG::SVGGraphicsElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~SVGGraphicsBox() override = default;
|
||||
|
||||
virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
|
||||
};
|
||||
|
||||
}
|
89
Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp
Normal file
89
Userland/Libraries/LibWeb/Layout/SVGPathBox.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/Painter.h>
|
||||
#include <LibWeb/Layout/SVGPathBox.h>
|
||||
#include <LibWeb/SVG/SVGPathElement.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
SVGPathBox::SVGPathBox(DOM::Document& document, SVG::SVGPathElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
|
||||
: SVGGraphicsBox(document, element, properties)
|
||||
{
|
||||
}
|
||||
|
||||
void SVGPathBox::prepare_for_replaced_layout()
|
||||
{
|
||||
auto& bounding_box = dom_node().get_path().bounding_box();
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(bounding_box.width());
|
||||
set_intrinsic_height(bounding_box.height());
|
||||
|
||||
// FIXME: This does not belong here! Someone at a higher level should place this box.
|
||||
set_offset(bounding_box.top_left());
|
||||
}
|
||||
|
||||
void SVGPathBox::paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
||||
SVGGraphicsBox::paint(context, phase);
|
||||
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
|
||||
auto& path_element = dom_node();
|
||||
auto& path = path_element.get_path();
|
||||
|
||||
// We need to fill the path before applying the stroke, however the filled
|
||||
// path must be closed, whereas the stroke path may not necessary be closed.
|
||||
// Copy the path and close it for filling, but use the previous path for stroke
|
||||
auto closed_path = path;
|
||||
closed_path.close();
|
||||
|
||||
// Fills are computed as though all paths are closed (https://svgwg.org/svg2-draft/painting.html#FillProperties)
|
||||
auto& painter = context.painter();
|
||||
auto& svg_context = context.svg_context();
|
||||
|
||||
auto offset = (absolute_position() - effective_offset()).to_type<int>();
|
||||
|
||||
painter.translate(offset);
|
||||
|
||||
painter.fill_path(
|
||||
closed_path,
|
||||
path_element.fill_color().value_or(svg_context.fill_color()),
|
||||
Gfx::Painter::WindingRule::EvenOdd);
|
||||
painter.stroke_path(
|
||||
path,
|
||||
path_element.stroke_color().value_or(svg_context.stroke_color()),
|
||||
path_element.stroke_width().value_or(svg_context.stroke_width()));
|
||||
|
||||
painter.translate(-offset);
|
||||
}
|
||||
|
||||
}
|
44
Userland/Libraries/LibWeb/Layout/SVGPathBox.h
Normal file
44
Userland/Libraries/LibWeb/Layout/SVGPathBox.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/SVGGraphicsBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class SVGPathBox final : public SVGGraphicsBox {
|
||||
public:
|
||||
SVGPathBox(DOM::Document&, SVG::SVGPathElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~SVGPathBox() override = default;
|
||||
|
||||
SVG::SVGPathElement& dom_node() { return downcast<SVG::SVGPathElement>(SVGGraphicsBox::dom_node()); }
|
||||
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
virtual void paint(PaintContext& context, PaintPhase phase) override;
|
||||
};
|
||||
|
||||
}
|
62
Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp
Normal file
62
Userland/Libraries/LibWeb/Layout/SVGSVGBox.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/SVGSVGBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
SVGSVGBox::SVGSVGBox(DOM::Document& document, SVG::SVGSVGElement& element, NonnullRefPtr<CSS::StyleProperties> properties)
|
||||
: SVGGraphicsBox(document, element, properties)
|
||||
{
|
||||
}
|
||||
|
||||
void SVGSVGBox::prepare_for_replaced_layout()
|
||||
{
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(dom_node().width());
|
||||
set_intrinsic_height(dom_node().height());
|
||||
}
|
||||
|
||||
void SVGSVGBox::before_children_paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
|
||||
if (!context.has_svg_context())
|
||||
context.set_svg_context(SVGContext());
|
||||
|
||||
SVGGraphicsBox::before_children_paint(context, phase);
|
||||
}
|
||||
|
||||
void SVGSVGBox::after_children_paint(PaintContext& context, PaintPhase phase)
|
||||
{
|
||||
SVGGraphicsBox::after_children_paint(context, phase);
|
||||
if (phase != PaintPhase::Foreground)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
49
Userland/Libraries/LibWeb/Layout/SVGSVGBox.h
Normal file
49
Userland/Libraries/LibWeb/Layout/SVGSVGBox.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* 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/SVGGraphicsBox.h>
|
||||
#include <LibWeb/SVG/SVGSVGElement.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class SVGSVGBox final : public SVGGraphicsBox {
|
||||
public:
|
||||
SVGSVGBox(DOM::Document&, SVG::SVGSVGElement&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~SVGSVGBox() override = default;
|
||||
|
||||
SVG::SVGSVGElement& dom_node() { return downcast<SVG::SVGSVGElement>(SVGGraphicsBox::dom_node()); }
|
||||
|
||||
virtual void prepare_for_replaced_layout() override;
|
||||
|
||||
virtual void before_children_paint(PaintContext& context, PaintPhase phase) override;
|
||||
virtual void after_children_paint(PaintContext& context, PaintPhase phase) override;
|
||||
|
||||
virtual bool can_have_children() const override { return true; }
|
||||
};
|
||||
|
||||
}
|
46
Userland/Libraries/LibWeb/Layout/TableBox.cpp
Normal file
46
Userland/Libraries/LibWeb/Layout/TableBox.cpp
Normal 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.
|
||||
*/
|
||||
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/TableBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TableBox::TableBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Layout::BlockBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
TableBox::TableBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
|
||||
: Layout::BlockBox(document, element, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
TableBox::~TableBox()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
42
Userland/Libraries/LibWeb/Layout/TableBox.h
Normal file
42
Userland/Libraries/LibWeb/Layout/TableBox.h
Normal 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/BlockBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TableBox final : public Layout::BlockBox {
|
||||
public:
|
||||
TableBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||
TableBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||
virtual ~TableBox() override;
|
||||
|
||||
static CSS::Display static_display() { return CSS::Display::Table; }
|
||||
};
|
||||
|
||||
}
|
61
Userland/Libraries/LibWeb/Layout/TableCellBox.cpp
Normal file
61
Userland/Libraries/LibWeb/Layout/TableCellBox.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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/TableCellBox.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Layout::BlockBox(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
TableCellBox::TableCellBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
|
||||
: Layout::BlockBox(document, element, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
TableCellBox::~TableCellBox()
|
||||
{
|
||||
}
|
||||
|
||||
size_t TableCellBox::colspan() const
|
||||
{
|
||||
if (!dom_node())
|
||||
return 0;
|
||||
return downcast<DOM::Element>(*dom_node()).attribute(HTML::AttributeNames::colspan).to_uint().value_or(1);
|
||||
}
|
||||
|
||||
float TableCellBox::width_of_logical_containing_block() const
|
||||
{
|
||||
if (auto* row = first_ancestor_of_type<TableRowBox>())
|
||||
return row->width();
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
50
Userland/Libraries/LibWeb/Layout/TableCellBox.h
Normal file
50
Userland/Libraries/LibWeb/Layout/TableCellBox.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TableCellBox final : public BlockBox {
|
||||
public:
|
||||
TableCellBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||
TableCellBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||
virtual ~TableCellBox() override;
|
||||
|
||||
TableCellBox* next_cell() { return next_sibling_of_type<TableCellBox>(); }
|
||||
const TableCellBox* next_cell() const { return next_sibling_of_type<TableCellBox>(); }
|
||||
|
||||
size_t colspan() const;
|
||||
|
||||
static CSS::Display static_display() { return CSS::Display::TableCell; }
|
||||
|
||||
private:
|
||||
virtual float width_of_logical_containing_block() const override;
|
||||
};
|
||||
|
||||
}
|
133
Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
Normal file
133
Userland/Libraries/LibWeb/Layout/TableFormattingContext.cpp
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright (c) 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/CSS/Length.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/Box.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/TableBox.h>
|
||||
#include <LibWeb/Layout/TableCellBox.h>
|
||||
#include <LibWeb/Layout/TableFormattingContext.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
#include <LibWeb/Layout/TableRowGroupBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TableFormattingContext::TableFormattingContext(Box& context_box, FormattingContext* parent)
|
||||
: BlockFormattingContext(context_box, parent)
|
||||
{
|
||||
}
|
||||
|
||||
TableFormattingContext::~TableFormattingContext()
|
||||
{
|
||||
}
|
||||
|
||||
void TableFormattingContext::run(Box& box, LayoutMode)
|
||||
{
|
||||
compute_width(box);
|
||||
|
||||
float total_content_height = 0;
|
||||
|
||||
box.for_each_child_of_type<TableRowGroupBox>([&](auto& row_group_box) {
|
||||
compute_width(row_group_box);
|
||||
auto column_count = row_group_box.column_count();
|
||||
Vector<float> column_widths;
|
||||
column_widths.resize(column_count);
|
||||
|
||||
row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
|
||||
calculate_column_widths(row, column_widths);
|
||||
});
|
||||
|
||||
float content_height = 0;
|
||||
|
||||
row_group_box.template for_each_child_of_type<TableRowBox>([&](auto& row) {
|
||||
row.set_offset(0, content_height);
|
||||
layout_row(row, column_widths);
|
||||
content_height += row.height();
|
||||
});
|
||||
|
||||
row_group_box.set_height(content_height);
|
||||
|
||||
total_content_height += content_height;
|
||||
});
|
||||
|
||||
// FIXME: This is a total hack, we should respect the 'height' property.
|
||||
box.set_height(total_content_height);
|
||||
}
|
||||
|
||||
void TableFormattingContext::calculate_column_widths(Box& row, Vector<float>& column_widths)
|
||||
{
|
||||
size_t column_index = 0;
|
||||
auto* table = row.first_ancestor_of_type<TableBox>();
|
||||
bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto();
|
||||
row.for_each_child_of_type<TableCellBox>([&](auto& cell) {
|
||||
compute_width(cell);
|
||||
if (use_auto_layout) {
|
||||
layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks);
|
||||
} else {
|
||||
layout_inside(cell, LayoutMode::Default);
|
||||
}
|
||||
column_widths[column_index] = max(column_widths[column_index], cell.width());
|
||||
column_index += cell.colspan();
|
||||
});
|
||||
}
|
||||
|
||||
void TableFormattingContext::layout_row(Box& row, Vector<float>& column_widths)
|
||||
{
|
||||
size_t column_index = 0;
|
||||
float tallest_cell_height = 0;
|
||||
float content_width = 0;
|
||||
auto* table = row.first_ancestor_of_type<TableBox>();
|
||||
bool use_auto_layout = !table || table->computed_values().width().is_undefined_or_auto();
|
||||
|
||||
row.for_each_child_of_type<TableCellBox>([&](auto& cell) {
|
||||
cell.set_offset(row.effective_offset().translated(content_width, 0));
|
||||
|
||||
// Layout the cell contents a second time, now that we know its final width.
|
||||
if (use_auto_layout) {
|
||||
layout_inside(cell, LayoutMode::OnlyRequiredLineBreaks);
|
||||
} else {
|
||||
layout_inside(cell, LayoutMode::Default);
|
||||
}
|
||||
|
||||
size_t cell_colspan = cell.colspan();
|
||||
for (size_t i = 0; i < cell_colspan; ++i)
|
||||
content_width += column_widths[column_index++];
|
||||
tallest_cell_height = max(tallest_cell_height, cell.height());
|
||||
});
|
||||
|
||||
if (use_auto_layout) {
|
||||
row.set_width(content_width);
|
||||
} else {
|
||||
row.set_width(table->width());
|
||||
}
|
||||
|
||||
row.set_height(tallest_cell_height);
|
||||
}
|
||||
|
||||
}
|
46
Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
Normal file
46
Userland/Libraries/LibWeb/Layout/TableFormattingContext.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 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/Forward.h>
|
||||
#include <LibWeb/Layout/BlockFormattingContext.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TableFormattingContext final : public BlockFormattingContext {
|
||||
public:
|
||||
explicit TableFormattingContext(Box&, FormattingContext* parent);
|
||||
~TableFormattingContext();
|
||||
|
||||
virtual void run(Box&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
void calculate_column_widths(Box& row, Vector<float>& column_widths);
|
||||
void layout_row(Box& row, Vector<float>& column_widths);
|
||||
};
|
||||
|
||||
}
|
46
Userland/Libraries/LibWeb/Layout/TableRowBox.cpp
Normal file
46
Userland/Libraries/LibWeb/Layout/TableRowBox.cpp
Normal 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.
|
||||
*/
|
||||
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Box(document, element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
TableRowBox::TableRowBox(DOM::Document& document, DOM::Element* element, CSS::ComputedValues computed_values)
|
||||
: Box(document, element, move(computed_values))
|
||||
{
|
||||
}
|
||||
|
||||
TableRowBox::~TableRowBox()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
42
Userland/Libraries/LibWeb/Layout/TableRowBox.h
Normal file
42
Userland/Libraries/LibWeb/Layout/TableRowBox.h
Normal 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/Box.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TableRowBox final : public Box {
|
||||
public:
|
||||
TableRowBox(DOM::Document&, DOM::Element*, NonnullRefPtr<CSS::StyleProperties>);
|
||||
TableRowBox(DOM::Document&, DOM::Element*, CSS::ComputedValues);
|
||||
virtual ~TableRowBox() override;
|
||||
|
||||
static CSS::Display static_display() { return CSS::Display::TableRow; }
|
||||
};
|
||||
|
||||
}
|
56
Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp
Normal file
56
Userland/Libraries/LibWeb/Layout/TableRowGroupBox.cpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 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/TableCellBox.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
#include <LibWeb/Layout/TableRowGroupBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TableRowGroupBox::TableRowGroupBox(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style)
|
||||
: Layout::BlockBox(document, &element, move(style))
|
||||
{
|
||||
}
|
||||
|
||||
TableRowGroupBox::~TableRowGroupBox()
|
||||
{
|
||||
}
|
||||
|
||||
size_t TableRowGroupBox::column_count() const
|
||||
{
|
||||
size_t table_column_count = 0;
|
||||
for_each_child_of_type<TableRowBox>([&](auto& row) {
|
||||
size_t row_column_count = 0;
|
||||
row.template for_each_child_of_type<TableCellBox>([&](auto& cell) {
|
||||
row_column_count += cell.colspan();
|
||||
});
|
||||
table_column_count = max(table_column_count, row_column_count);
|
||||
});
|
||||
return table_column_count;
|
||||
}
|
||||
|
||||
}
|
41
Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h
Normal file
41
Userland/Libraries/LibWeb/Layout/TableRowGroupBox.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 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/BlockBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TableRowGroupBox final : public BlockBox {
|
||||
public:
|
||||
TableRowGroupBox(DOM::Document&, DOM::Element&, NonnullRefPtr<CSS::StyleProperties>);
|
||||
virtual ~TableRowGroupBox() override;
|
||||
|
||||
size_t column_count() const;
|
||||
};
|
||||
|
||||
}
|
311
Userland/Libraries/LibWeb/Layout/TextNode.cpp
Normal file
311
Userland/Libraries/LibWeb/Layout/TextNode.cpp
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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 <LibGUI/Painter.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Layout/BlockBox.h>
|
||||
#include <LibWeb/Layout/InlineFormattingContext.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
#include <ctype.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TextNode::TextNode(DOM::Document& document, DOM::Text& text)
|
||||
: Node(document, &text)
|
||||
{
|
||||
set_inline(true);
|
||||
}
|
||||
|
||||
TextNode::~TextNode()
|
||||
{
|
||||
}
|
||||
|
||||
static bool is_all_whitespace(const StringView& string)
|
||||
{
|
||||
for (size_t i = 0; i < string.length(); ++i) {
|
||||
if (!isspace(string[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextNode::paint_fragment(PaintContext& context, const LineBoxFragment& fragment, PaintPhase phase) const
|
||||
{
|
||||
auto& painter = context.painter();
|
||||
|
||||
if (phase == PaintPhase::Background) {
|
||||
painter.fill_rect(enclosing_int_rect(fragment.absolute_rect()), computed_values().background_color());
|
||||
}
|
||||
|
||||
if (phase == PaintPhase::Foreground) {
|
||||
painter.set_font(font());
|
||||
|
||||
if (document().inspected_node() == &dom_node())
|
||||
context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta);
|
||||
|
||||
if (computed_values().text_decoration_line() == CSS::TextDecorationLine::Underline)
|
||||
painter.draw_line(enclosing_int_rect(fragment.absolute_rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.absolute_rect()).bottom_right().translated(0, 1), computed_values().color());
|
||||
|
||||
// FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
|
||||
auto text = m_text_for_rendering;
|
||||
auto text_transform = computed_values().text_transform();
|
||||
if (text_transform == CSS::TextTransform::Uppercase)
|
||||
text = m_text_for_rendering.to_uppercase();
|
||||
if (text_transform == CSS::TextTransform::Lowercase)
|
||||
text = m_text_for_rendering.to_lowercase();
|
||||
|
||||
painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, computed_values().color());
|
||||
|
||||
auto selection_rect = fragment.selection_rect(font());
|
||||
if (!selection_rect.is_empty()) {
|
||||
painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
painter.add_clip_rect(enclosing_int_rect(selection_rect));
|
||||
painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::CenterLeft, context.palette().selection_text());
|
||||
}
|
||||
|
||||
paint_cursor_if_needed(context, fragment);
|
||||
}
|
||||
}
|
||||
|
||||
void TextNode::paint_cursor_if_needed(PaintContext& context, const LineBoxFragment& fragment) const
|
||||
{
|
||||
if (!frame().is_focused_frame())
|
||||
return;
|
||||
|
||||
if (!frame().cursor_blink_state())
|
||||
return;
|
||||
|
||||
if (frame().cursor_position().node() != &dom_node())
|
||||
return;
|
||||
|
||||
if (!(frame().cursor_position().offset() >= (unsigned)fragment.start() && frame().cursor_position().offset() < (unsigned)(fragment.start() + fragment.length())))
|
||||
return;
|
||||
|
||||
if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
|
||||
return;
|
||||
|
||||
auto fragment_rect = fragment.absolute_rect();
|
||||
|
||||
float cursor_x = fragment_rect.x() + font().width(fragment.text().substring_view(0, frame().cursor_position().offset() - fragment.start()));
|
||||
float cursor_top = fragment_rect.top();
|
||||
float cursor_height = fragment_rect.height();
|
||||
Gfx::IntRect cursor_rect(cursor_x, cursor_top, 1, cursor_height);
|
||||
|
||||
context.painter().draw_rect(cursor_rect, computed_values().color());
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void TextNode::for_each_chunk(Callback callback, LayoutMode layout_mode, bool do_wrap_lines, bool do_wrap_breaks) const
|
||||
{
|
||||
Utf8View view(m_text_for_rendering);
|
||||
if (view.is_empty())
|
||||
return;
|
||||
|
||||
auto start_of_chunk = view.begin();
|
||||
|
||||
auto commit_chunk = [&](auto it, bool has_breaking_newline, bool must_commit = false) {
|
||||
if (layout_mode == LayoutMode::OnlyRequiredLineBreaks && !must_commit)
|
||||
return;
|
||||
|
||||
int start = view.byte_offset_of(start_of_chunk);
|
||||
int length = view.byte_offset_of(it) - view.byte_offset_of(start_of_chunk);
|
||||
|
||||
if (has_breaking_newline || length > 0) {
|
||||
auto chunk_view = view.substring_view(start, length);
|
||||
callback(chunk_view, start, length, has_breaking_newline, is_all_whitespace(chunk_view.as_string()));
|
||||
}
|
||||
|
||||
start_of_chunk = it;
|
||||
};
|
||||
|
||||
bool last_was_space = isspace(*view.begin());
|
||||
bool last_was_newline = false;
|
||||
for (auto it = view.begin(); it != view.end();) {
|
||||
if (layout_mode == LayoutMode::AllPossibleLineBreaks) {
|
||||
commit_chunk(it, false);
|
||||
}
|
||||
if (last_was_newline) {
|
||||
last_was_newline = false;
|
||||
commit_chunk(it, true);
|
||||
}
|
||||
if (do_wrap_breaks && *it == '\n') {
|
||||
last_was_newline = true;
|
||||
commit_chunk(it, false);
|
||||
}
|
||||
if (do_wrap_lines) {
|
||||
bool is_space = isspace(*it);
|
||||
if (is_space != last_was_space) {
|
||||
last_was_space = is_space;
|
||||
commit_chunk(it, false);
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (last_was_newline)
|
||||
commit_chunk(view.end(), true);
|
||||
if (start_of_chunk != view.end())
|
||||
commit_chunk(view.end(), false, true);
|
||||
}
|
||||
|
||||
void TextNode::split_into_lines_by_rules(InlineFormattingContext& context, LayoutMode layout_mode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks)
|
||||
{
|
||||
auto& containing_block = context.containing_block();
|
||||
|
||||
auto& font = this->font();
|
||||
|
||||
auto& line_boxes = containing_block.line_boxes();
|
||||
containing_block.ensure_last_line_box();
|
||||
float available_width = context.available_width_at_line(line_boxes.size() - 1) - line_boxes.last().width();
|
||||
|
||||
// Collapse whitespace into single spaces
|
||||
if (do_collapse) {
|
||||
auto utf8_view = Utf8View(dom_node().data());
|
||||
StringBuilder builder(dom_node().data().length());
|
||||
auto it = utf8_view.begin();
|
||||
auto skip_over_whitespace = [&] {
|
||||
auto prev = it;
|
||||
while (it != utf8_view.end() && isspace(*it)) {
|
||||
prev = it;
|
||||
++it;
|
||||
}
|
||||
it = prev;
|
||||
};
|
||||
if (line_boxes.last().is_empty_or_ends_in_whitespace())
|
||||
skip_over_whitespace();
|
||||
for (; 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.code_point_length_in_bytes());
|
||||
} else {
|
||||
builder.append(' ');
|
||||
skip_over_whitespace();
|
||||
}
|
||||
}
|
||||
m_text_for_rendering = builder.to_string();
|
||||
} else {
|
||||
m_text_for_rendering = dom_node().data();
|
||||
}
|
||||
|
||||
// do_wrap_lines => chunks_are_words
|
||||
// !do_wrap_lines => chunks_are_lines
|
||||
struct Chunk {
|
||||
Utf8View view;
|
||||
int start { 0 };
|
||||
int length { 0 };
|
||||
bool is_break { false };
|
||||
bool is_all_whitespace { false };
|
||||
};
|
||||
Vector<Chunk, 128> chunks;
|
||||
|
||||
for_each_chunk(
|
||||
[&](const Utf8View& view, int start, int length, bool is_break, bool is_all_whitespace) {
|
||||
chunks.append({ Utf8View(view), start, length, is_break, is_all_whitespace });
|
||||
},
|
||||
layout_mode, do_wrap_lines, do_wrap_breaks);
|
||||
|
||||
for (size_t i = 0; i < chunks.size(); ++i) {
|
||||
auto& chunk = chunks[i];
|
||||
|
||||
// Collapse entire fragment into non-existence if previous fragment on line ended in whitespace.
|
||||
if (do_collapse && line_boxes.last().is_empty_or_ends_in_whitespace() && chunk.is_all_whitespace)
|
||||
continue;
|
||||
|
||||
float chunk_width;
|
||||
if (do_wrap_lines) {
|
||||
if (do_collapse && isspace(*chunk.view.begin()) && line_boxes.last().is_empty_or_ends_in_whitespace()) {
|
||||
// This is a non-empty chunk that starts with collapsible whitespace.
|
||||
// We are at either at the start of a new line, or after something that ended in whitespace,
|
||||
// so we don't need to contribute our own whitespace to the line. Skip over it instead!
|
||||
++chunk.start;
|
||||
--chunk.length;
|
||||
chunk.view = chunk.view.substring_view(1, chunk.view.byte_length() - 1);
|
||||
}
|
||||
|
||||
chunk_width = font.width(chunk.view) + font.glyph_spacing();
|
||||
|
||||
if (line_boxes.last().width() > 0 && chunk_width > available_width) {
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size() - 1);
|
||||
|
||||
if (do_collapse && chunk.is_all_whitespace)
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
chunk_width = font.width(chunk.view);
|
||||
}
|
||||
|
||||
line_boxes.last().add_fragment(*this, chunk.start, chunk.length, chunk_width, font.glyph_height());
|
||||
available_width -= chunk_width;
|
||||
|
||||
if (do_wrap_lines) {
|
||||
if (available_width < 0) {
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_wrap_breaks) {
|
||||
if (chunk.is_break) {
|
||||
containing_block.add_line_box();
|
||||
available_width = context.available_width_at_line(line_boxes.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextNode::split_into_lines(InlineFormattingContext& context, LayoutMode layout_mode)
|
||||
{
|
||||
bool do_collapse = true;
|
||||
bool do_wrap_lines = true;
|
||||
bool do_wrap_breaks = false;
|
||||
|
||||
if (computed_values().white_space() == CSS::WhiteSpace::Nowrap) {
|
||||
do_collapse = true;
|
||||
do_wrap_lines = false;
|
||||
do_wrap_breaks = false;
|
||||
} else if (computed_values().white_space() == CSS::WhiteSpace::Pre) {
|
||||
do_collapse = false;
|
||||
do_wrap_lines = false;
|
||||
do_wrap_breaks = true;
|
||||
} else if (computed_values().white_space() == CSS::WhiteSpace::PreLine) {
|
||||
do_collapse = true;
|
||||
do_wrap_lines = true;
|
||||
do_wrap_breaks = true;
|
||||
} else if (computed_values().white_space() == CSS::WhiteSpace::PreWrap) {
|
||||
do_collapse = false;
|
||||
do_wrap_lines = true;
|
||||
do_wrap_breaks = true;
|
||||
}
|
||||
|
||||
split_into_lines_by_rules(context, layout_mode, do_collapse, do_wrap_lines, do_wrap_breaks);
|
||||
}
|
||||
|
||||
}
|
59
Userland/Libraries/LibWeb/Layout/TextNode.h
Normal file
59
Userland/Libraries/LibWeb/Layout/TextNode.h
Normal 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/Text.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class LineBoxFragment;
|
||||
|
||||
class TextNode : public Node {
|
||||
public:
|
||||
TextNode(DOM::Document&, DOM::Text&);
|
||||
virtual ~TextNode() override;
|
||||
|
||||
const DOM::Text& dom_node() const { return static_cast<const DOM::Text&>(*Node::dom_node()); }
|
||||
|
||||
const String& text_for_rendering() const { return m_text_for_rendering; }
|
||||
|
||||
virtual void paint_fragment(PaintContext&, const LineBoxFragment&, PaintPhase) const override;
|
||||
|
||||
virtual void split_into_lines(InlineFormattingContext&, LayoutMode) override;
|
||||
|
||||
private:
|
||||
void split_into_lines_by_rules(InlineFormattingContext&, LayoutMode, bool do_collapse, bool do_wrap_lines, bool do_wrap_breaks);
|
||||
void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const;
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_chunk(Callback, LayoutMode, bool do_wrap_lines, bool do_wrap_breaks) const;
|
||||
|
||||
String m_text_for_rendering;
|
||||
};
|
||||
|
||||
}
|
302
Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
Normal file
302
Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
Normal file
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, 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/Element.h>
|
||||
#include <LibWeb/DOM/ParentNode.h>
|
||||
#include <LibWeb/Dump.h>
|
||||
#include <LibWeb/Layout/InitialContainingBlockBox.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Layout/TableBox.h>
|
||||
#include <LibWeb/Layout/TableCellBox.h>
|
||||
#include <LibWeb/Layout/TableRowBox.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Layout/TreeBuilder.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
TreeBuilder::TreeBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
// The insertion_parent_for_*() functions maintain the invariant that block-level boxes must have either
|
||||
// only block-level children or only inline-level children.
|
||||
|
||||
static Layout::Node& insertion_parent_for_inline_node(Layout::NodeWithStyle& layout_parent)
|
||||
{
|
||||
if (layout_parent.is_inline() && !layout_parent.is_inline_block())
|
||||
return layout_parent;
|
||||
|
||||
if (!layout_parent.has_children() || layout_parent.children_are_inline())
|
||||
return layout_parent;
|
||||
|
||||
// Parent has block-level children, insert into an anonymous wrapper block (and create it first if needed)
|
||||
if (!layout_parent.last_child()->is_anonymous() || !layout_parent.last_child()->children_are_inline()) {
|
||||
layout_parent.append_child(layout_parent.create_anonymous_wrapper());
|
||||
}
|
||||
return *layout_parent.last_child();
|
||||
}
|
||||
|
||||
static Layout::Node& insertion_parent_for_block_node(Layout::Node& layout_parent, Layout::Node& layout_node)
|
||||
{
|
||||
if (!layout_parent.has_children()) {
|
||||
// Parent block has no children, insert this block into parent.
|
||||
return layout_parent;
|
||||
}
|
||||
|
||||
if (!layout_parent.children_are_inline()) {
|
||||
// Parent block has block-level children, insert this block into parent.
|
||||
return layout_parent;
|
||||
}
|
||||
|
||||
// Parent block has inline-level children (our siblings).
|
||||
// First move these siblings into an anonymous wrapper block.
|
||||
NonnullRefPtrVector<Layout::Node> children;
|
||||
while (RefPtr<Layout::Node> child = layout_parent.first_child()) {
|
||||
layout_parent.remove_child(*child);
|
||||
children.append(child.release_nonnull());
|
||||
}
|
||||
layout_parent.append_child(adopt(*new BlockBox(layout_node.document(), nullptr, layout_parent.computed_values().clone_inherited_values())));
|
||||
layout_parent.set_children_are_inline(false);
|
||||
for (auto& child : children) {
|
||||
layout_parent.last_child()->append_child(child);
|
||||
}
|
||||
layout_parent.last_child()->set_children_are_inline(true);
|
||||
// Then it's safe to insert this block into parent.
|
||||
return layout_parent;
|
||||
}
|
||||
|
||||
void TreeBuilder::create_layout_tree(DOM::Node& dom_node)
|
||||
{
|
||||
// If the parent doesn't have a layout node, we don't need one either.
|
||||
if (dom_node.parent() && !dom_node.parent()->layout_node())
|
||||
return;
|
||||
|
||||
auto layout_node = dom_node.create_layout_node();
|
||||
if (!layout_node)
|
||||
return;
|
||||
|
||||
if (!dom_node.parent()) {
|
||||
m_layout_root = layout_node;
|
||||
} else {
|
||||
if (layout_node->is_inline()) {
|
||||
// Inlines can be inserted into the nearest ancestor.
|
||||
auto& insertion_point = insertion_parent_for_inline_node(*m_parent_stack.last());
|
||||
insertion_point.append_child(*layout_node);
|
||||
insertion_point.set_children_are_inline(true);
|
||||
} else {
|
||||
// Non-inlines can't be inserted into an inline parent, so find the nearest non-inline ancestor.
|
||||
auto& nearest_non_inline_ancestor = [&]() -> Layout::Node& {
|
||||
for (ssize_t i = m_parent_stack.size() - 1; i >= 0; --i) {
|
||||
if (!m_parent_stack[i]->is_inline() || m_parent_stack[i]->is_inline_block())
|
||||
return *m_parent_stack[i];
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}();
|
||||
auto& insertion_point = insertion_parent_for_block_node(nearest_non_inline_ancestor, *layout_node);
|
||||
insertion_point.append_child(*layout_node);
|
||||
insertion_point.set_children_are_inline(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (dom_node.has_children() && layout_node->can_have_children()) {
|
||||
push_parent(downcast<NodeWithStyle>(*layout_node));
|
||||
downcast<DOM::ParentNode>(dom_node).for_each_child([&](auto& dom_child) {
|
||||
create_layout_tree(dom_child);
|
||||
});
|
||||
pop_parent();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<Node> TreeBuilder::build(DOM::Node& dom_node)
|
||||
{
|
||||
if (dom_node.parent()) {
|
||||
// We're building a partial layout tree, so start by building up the stack of parent layout nodes.
|
||||
for (auto* ancestor = dom_node.parent()->layout_node(); ancestor; ancestor = ancestor->parent())
|
||||
m_parent_stack.prepend(downcast<NodeWithStyle>(ancestor));
|
||||
}
|
||||
|
||||
create_layout_tree(dom_node);
|
||||
|
||||
if (auto* root = dom_node.document().layout_node())
|
||||
fixup_tables(*root);
|
||||
|
||||
return move(m_layout_root);
|
||||
}
|
||||
|
||||
template<CSS::Display display, typename Callback>
|
||||
void TreeBuilder::for_each_in_tree_with_display(NodeWithStyle& root, Callback callback)
|
||||
{
|
||||
root.for_each_in_subtree_of_type<Box>([&](auto& box) {
|
||||
if (box.computed_values().display() == display)
|
||||
callback(box);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void TreeBuilder::fixup_tables(NodeWithStyle& root)
|
||||
{
|
||||
// NOTE: Even if we only do a partial build, we always do fixup from the root.
|
||||
|
||||
remove_irrelevant_boxes(root);
|
||||
generate_missing_child_wrappers(root);
|
||||
generate_missing_parents(root);
|
||||
}
|
||||
|
||||
void TreeBuilder::remove_irrelevant_boxes(NodeWithStyle& root)
|
||||
{
|
||||
// The following boxes are discarded as if they were display:none:
|
||||
|
||||
NonnullRefPtrVector<Box> to_remove;
|
||||
|
||||
// Children of a table-column.
|
||||
for_each_in_tree_with_display<CSS::Display::TableColumn>(root, [&](Box& table_column) {
|
||||
table_column.for_each_child([&](auto& child) {
|
||||
to_remove.append(child);
|
||||
});
|
||||
});
|
||||
|
||||
// Children of a table-column-group which are not a table-column.
|
||||
for_each_in_tree_with_display<CSS::Display::TableColumnGroup>(root, [&](Box& table_column_group) {
|
||||
table_column_group.for_each_child([&](auto& child) {
|
||||
if (child.computed_values().display() != CSS::Display::TableColumn)
|
||||
to_remove.append(child);
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME:
|
||||
// Anonymous inline boxes which contain only white space and are between two immediate siblings each of which is a table-non-root box.
|
||||
// Anonymous inline boxes which meet all of the following criteria:
|
||||
// - they contain only white space
|
||||
// - they are the first and/or last child of a tabular container
|
||||
// - whose immediate sibling, if any, is a table-non-root box
|
||||
|
||||
for (auto& box : to_remove)
|
||||
box.parent()->remove_child(box);
|
||||
}
|
||||
|
||||
static bool is_table_track(CSS::Display display)
|
||||
{
|
||||
return display == CSS::Display::TableRow || display == CSS::Display::TableColumn;
|
||||
}
|
||||
|
||||
static bool is_table_track_group(CSS::Display display)
|
||||
{
|
||||
return display == CSS::Display::TableRowGroup || display == CSS::Display::TableColumnGroup;
|
||||
}
|
||||
|
||||
static bool is_not_proper_table_child(const Node& node)
|
||||
{
|
||||
if (!node.has_style())
|
||||
return true;
|
||||
auto display = node.computed_values().display();
|
||||
return !is_table_track_group(display) && !is_table_track(display) && display != CSS::Display::TableCaption;
|
||||
}
|
||||
|
||||
static bool is_not_table_row(const Node& node)
|
||||
{
|
||||
if (!node.has_style())
|
||||
return true;
|
||||
auto display = node.computed_values().display();
|
||||
return display != CSS::Display::TableRow;
|
||||
}
|
||||
|
||||
static bool is_not_table_cell(const Node& node)
|
||||
{
|
||||
if (!node.has_style())
|
||||
return true;
|
||||
auto display = node.computed_values().display();
|
||||
return display != CSS::Display::TableCell;
|
||||
}
|
||||
|
||||
template<typename Matcher, typename Callback>
|
||||
static void for_each_sequence_of_consecutive_children_matching(NodeWithStyle& parent, Matcher matcher, Callback callback)
|
||||
{
|
||||
NonnullRefPtrVector<Node> sequence;
|
||||
Node* next_sibling = nullptr;
|
||||
for (auto* child = parent.first_child(); child; child = next_sibling) {
|
||||
next_sibling = child->next_sibling();
|
||||
if (matcher(*child)) {
|
||||
sequence.append(*child);
|
||||
} else {
|
||||
if (!sequence.is_empty()) {
|
||||
callback(sequence, next_sibling);
|
||||
sequence.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!sequence.is_empty())
|
||||
callback(sequence, nullptr);
|
||||
}
|
||||
|
||||
template<typename WrapperBoxType>
|
||||
static void wrap_in_anonymous(NonnullRefPtrVector<Node>& sequence, Node* nearest_sibling)
|
||||
{
|
||||
ASSERT(!sequence.is_empty());
|
||||
auto& parent = *sequence.first().parent();
|
||||
auto computed_values = parent.computed_values().clone_inherited_values();
|
||||
static_cast<CSS::MutableComputedValues&>(computed_values).set_display(WrapperBoxType::static_display());
|
||||
auto wrapper = adopt(*new WrapperBoxType(parent.document(), nullptr, move(computed_values)));
|
||||
for (auto& child : sequence) {
|
||||
parent.remove_child(child);
|
||||
wrapper->append_child(child);
|
||||
}
|
||||
if (nearest_sibling)
|
||||
parent.insert_before(move(wrapper), *nearest_sibling);
|
||||
else
|
||||
parent.append_child(move(wrapper));
|
||||
}
|
||||
|
||||
void TreeBuilder::generate_missing_child_wrappers(NodeWithStyle& root)
|
||||
{
|
||||
// An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes.
|
||||
for_each_in_tree_with_display<CSS::Display::Table>(root, [&](auto& parent) {
|
||||
for_each_sequence_of_consecutive_children_matching(parent, is_not_proper_table_child, [&](auto sequence, auto nearest_sibling) {
|
||||
wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
|
||||
});
|
||||
});
|
||||
|
||||
// An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-group box which are not table-row boxes.
|
||||
for_each_in_tree_with_display<CSS::Display::TableRowGroup>(root, [&](auto& parent) {
|
||||
for_each_sequence_of_consecutive_children_matching(parent, is_not_table_row, [&](auto& sequence, auto nearest_sibling) {
|
||||
wrap_in_anonymous<TableRowBox>(sequence, nearest_sibling);
|
||||
});
|
||||
});
|
||||
|
||||
// An anonymous table-cell box must be generated around each sequence of consecutive children of a table-row box which are not table-cell boxes. !Testcase
|
||||
for_each_in_tree_with_display<CSS::Display::TableRow>(root, [&](auto& parent) {
|
||||
for_each_sequence_of_consecutive_children_matching(parent, is_not_table_cell, [&](auto& sequence, auto nearest_sibling) {
|
||||
wrap_in_anonymous<TableCellBox>(sequence, nearest_sibling);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void TreeBuilder::generate_missing_parents(NodeWithStyle&)
|
||||
{
|
||||
// FIXME: Implement.
|
||||
}
|
||||
|
||||
}
|
59
Userland/Libraries/LibWeb/Layout/TreeBuilder.h
Normal file
59
Userland/Libraries/LibWeb/Layout/TreeBuilder.h
Normal 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 <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class TreeBuilder {
|
||||
public:
|
||||
TreeBuilder();
|
||||
|
||||
RefPtr<Layout::Node> build(DOM::Node&);
|
||||
|
||||
private:
|
||||
void create_layout_tree(DOM::Node&);
|
||||
|
||||
void push_parent(Layout::NodeWithStyle& node) { m_parent_stack.append(&node); }
|
||||
void pop_parent() { m_parent_stack.take_last(); }
|
||||
|
||||
template<CSS::Display, typename Callback>
|
||||
void for_each_in_tree_with_display(NodeWithStyle& root, Callback);
|
||||
|
||||
void fixup_tables(NodeWithStyle& root);
|
||||
void remove_irrelevant_boxes(NodeWithStyle& root);
|
||||
void generate_missing_child_wrappers(NodeWithStyle& root);
|
||||
void generate_missing_parents(NodeWithStyle& root);
|
||||
|
||||
RefPtr<Layout::Node> m_layout_root;
|
||||
Vector<Layout::NodeWithStyle*> m_parent_stack;
|
||||
};
|
||||
|
||||
}
|
68
Userland/Libraries/LibWeb/Layout/WidgetBox.cpp
Normal file
68
Userland/Libraries/LibWeb/Layout/WidgetBox.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 <LibGUI/ScrollBar.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/InProcessWebView.h>
|
||||
#include <LibWeb/Layout/WidgetBox.h>
|
||||
#include <LibWeb/Page/Frame.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
WidgetBox::WidgetBox(DOM::Document& document, DOM::Element& element, GUI::Widget& widget)
|
||||
: ReplacedBox(document, element, CSS::StyleProperties::create())
|
||||
, m_widget(widget)
|
||||
{
|
||||
set_has_intrinsic_width(true);
|
||||
set_has_intrinsic_height(true);
|
||||
set_intrinsic_width(widget.width());
|
||||
set_intrinsic_height(widget.height());
|
||||
}
|
||||
|
||||
WidgetBox::~WidgetBox()
|
||||
{
|
||||
widget().remove_from_parent();
|
||||
}
|
||||
|
||||
void WidgetBox::did_set_rect()
|
||||
{
|
||||
ReplacedBox::did_set_rect();
|
||||
update_widget();
|
||||
}
|
||||
|
||||
void WidgetBox::update_widget()
|
||||
{
|
||||
auto adjusted_widget_position = absolute_rect().location().to_type<int>();
|
||||
auto& page_view = static_cast<const InProcessWebView&>(frame().page()->client());
|
||||
adjusted_widget_position.move_by(-page_view.horizontal_scrollbar().value(), -page_view.vertical_scrollbar().value());
|
||||
widget().move_to(adjusted_widget_position);
|
||||
}
|
||||
|
||||
}
|
49
Userland/Libraries/LibWeb/Layout/WidgetBox.h
Normal file
49
Userland/Libraries/LibWeb/Layout/WidgetBox.h
Normal 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/Layout/ReplacedBox.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
||||
class WidgetBox final : public ReplacedBox {
|
||||
public:
|
||||
WidgetBox(DOM::Document&, DOM::Element&, GUI::Widget&);
|
||||
virtual ~WidgetBox() override;
|
||||
|
||||
GUI::Widget& widget() { return m_widget; }
|
||||
const GUI::Widget& widget() const { return m_widget; }
|
||||
|
||||
void update_widget();
|
||||
|
||||
private:
|
||||
virtual void did_set_rect() override;
|
||||
|
||||
NonnullRefPtr<GUI::Widget> m_widget;
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue