1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 14:28:12 +00:00

LibHTML: Use floating point numbers throughout the layout tree

This commit is contained in:
Andreas Kling 2019-11-18 16:25:38 +01:00
parent da23864c8d
commit c628ebda0f
15 changed files with 59 additions and 52 deletions

View file

@ -15,21 +15,26 @@ public:
, m_value(value)
{
}
Length(float value, Type type)
: m_type(type)
, m_value(value)
{
}
~Length() {}
bool is_auto() const { return m_type == Type::Auto; }
bool is_absolute() const { return m_type == Type::Absolute; }
int value() const { return m_value; }
float value() const { return m_value; }
String to_string() const
{
if (is_auto())
return "[Length/auto]";
return String::format("%d [Length/px]", m_value);
return String::format("%g [Length/px]", m_value);
}
int to_px() const
float to_px() const
{
if (is_auto())
return 0;
@ -38,7 +43,7 @@ public:
private:
Type m_type { Type::Auto };
int m_value { 0 };
float m_value { 0 };
};
inline const LogStream& operator<<(const LogStream& stream, const Length& value)

View file

@ -101,10 +101,9 @@ void StyleProperties::load_font() const
FontCache::the().set({ font_family, font_weight }, *m_font);
}
int StyleProperties::line_height() const
float StyleProperties::line_height() const
{
// FIXME: Allow overriding the line-height. We currently default to 140% which seems to look nice.
return (int)(font().glyph_height() * 1.4f);
return (float)font().glyph_height() * 1.4f;
}
bool StyleProperties::operator==(const StyleProperties& other) const

View file

@ -32,7 +32,7 @@ public:
return *m_font;
}
int line_height() const;
float line_height() const;
bool operator==(const StyleProperties&) const;
bool operator!=(const StyleProperties& other) const { return !(*this == other); }

View file

@ -85,14 +85,14 @@ void HtmlView::layout_and_sync_size()
main_frame().set_size(available_size());
document()->layout();
set_content_size(layout_root()->size());
set_content_size(enclosing_int_rect(layout_root()->rect()).size());
// NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again
// since the scrollbars now take up some of the available space.
if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) {
main_frame().set_size(available_size());
document()->layout();
set_content_size(layout_root()->size());
set_content_size(enclosing_int_rect(layout_root()->rect()).size());
}
#ifdef HTML_DEBUG
@ -360,7 +360,8 @@ void HtmlView::scroll_to_anchor(const StringView& name)
return;
}
auto& layout_node = *element->layout_node();
scroll_into_view({ layout_node.box_type_agnostic_position(), visible_content_rect().size() }, true, true);
FloatRect float_rect { layout_node.box_type_agnostic_position(), { (float)visible_content_rect().width(), (float)visible_content_rect().height() } };
scroll_into_view(enclosing_int_rect(float_rect), true, true);
window()->set_override_cursor(GStandardCursor::None);
}

View file

@ -17,10 +17,10 @@ public:
const LengthBox& border() const { return m_border; }
struct PixelBox {
int top;
int right;
int bottom;
int left;
float top;
float right;
float bottom;
float left;
};
PixelBox full_margin() const;

View file

@ -41,7 +41,7 @@ void LayoutBlock::layout()
void LayoutBlock::layout_block_children()
{
ASSERT(!children_are_inline());
int content_height = 0;
float content_height = 0;
for_each_child([&](auto& child) {
// FIXME: What should we do here? Something like a <table> might have a bunch of useless text children..
if (child.is_inline())
@ -66,8 +66,8 @@ void LayoutBlock::layout_inline_children()
line_box.trim_trailing_whitespace();
}
int min_line_height = style().line_height();
int content_height = 0;
float min_line_height = style().line_height();
float content_height = 0;
// FIXME: This should be done by the CSS parser!
CSS::ValueID text_align = CSS::ValueID::Left;
@ -82,9 +82,9 @@ void LayoutBlock::layout_inline_children()
text_align = CSS::ValueID::Justify;
for (auto& line_box : m_line_boxes) {
int max_height = min_line_height;
float max_height = min_line_height;
for (auto& fragment : line_box.fragments()) {
max_height = max(max_height, enclosing_int_rect(fragment.rect()).height());
max_height = max(max_height, fragment.rect().height());
}
float x_offset = x();
@ -137,7 +137,7 @@ void LayoutBlock::layout_inline_children()
}
if (is<LayoutReplaced>(fragment.layout_node()))
const_cast<LayoutReplaced&>(to<LayoutReplaced>(fragment.layout_node())).set_rect(enclosing_int_rect(fragment.rect()));
const_cast<LayoutReplaced&>(to<LayoutReplaced>(fragment.layout_node())).set_rect(fragment.rect());
float final_line_box_width = 0;
for (auto& fragment : line_box.fragments())
@ -178,7 +178,7 @@ void LayoutBlock::compute_width()
padding_left = style.length_or_fallback(CSS::PropertyID::PaddingLeft, zero_value);
padding_right = style.length_or_fallback(CSS::PropertyID::PaddingRight, zero_value);
int total_px = 0;
float total_px = 0;
for (auto& value : { margin_left, border_left, padding_left, width, padding_right, border_right, margin_right }) {
total_px += value.to_px();
}
@ -275,7 +275,7 @@ void LayoutBlock::compute_position()
box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value);
rect().set_x(containing_block()->x() + box_model().margin().left.to_px() + box_model().border().left.to_px() + box_model().padding().left.to_px());
int top_border = -1;
float top_border = -1;
if (previous_sibling() != nullptr) {
auto& previous_sibling_rect = previous_sibling()->rect();
auto& previous_sibling_style = previous_sibling()->box_model();

View file

@ -22,7 +22,7 @@ void LayoutBox::render(RenderingContext& context)
#endif
if (node() && document().inspected_node() == node())
context.painter().draw_rect(m_rect, Color::Magenta);
context.painter().draw_rect(enclosing_int_rect(m_rect), Color::Magenta);
Rect padded_rect;
padded_rect.set_x(x() - box_model().padding().left.to_px());
@ -51,7 +51,7 @@ void LayoutBox::render(RenderingContext& context)
auto border_style_value = style().property(CSS::PropertyID::BorderTopStyle);
if (border_width_value.has_value()) {
int border_width = border_width_value.value()->to_length().to_px();
float border_width = border_width_value.value()->to_length().to_px();
Color border_color;
if (border_color_value.has_value())
@ -99,7 +99,7 @@ HitTestResult LayoutBox::hit_test(const Point& position) const
// FIXME: It would be nice if we could confidently skip over hit testing
// parts of the layout tree, but currently we can't just check
// m_rect.contains() since inline text rects can't be trusted..
HitTestResult result { m_rect.contains(position) ? this : nullptr };
HitTestResult result { m_rect.contains(FloatPoint(position.x(), position.y())) ? this : nullptr };
for_each_child([&](auto& child) {
auto child_result = child.hit_test(position);
if (child_result.layout_node)
@ -114,7 +114,7 @@ void LayoutBox::set_needs_display()
ASSERT(frame);
if (!is_inline()) {
const_cast<Frame*>(frame)->set_needs_display(rect());
const_cast<Frame*>(frame)->set_needs_display(enclosing_int_rect(rect()));
return;
}

View file

@ -1,19 +1,20 @@
#pragma once
#include <LibDraw/FloatRect.h>
#include <LibHTML/Layout/LayoutNode.h>
class LayoutBox : public LayoutNodeWithStyleAndBoxModelMetrics {
public:
const Rect& rect() const { return m_rect; }
Rect& rect() { return m_rect; }
void set_rect(const Rect& rect) { m_rect = rect; }
const FloatRect& rect() const { return m_rect; }
FloatRect& rect() { return m_rect; }
void set_rect(const FloatRect& rect) { m_rect = rect; }
int x() const { return rect().x(); }
int y() const { return rect().y(); }
int width() const { return rect().width(); }
int height() const { return rect().height(); }
Size size() const { return rect().size(); }
Point position() const { return rect().location(); }
float x() const { return rect().x(); }
float y() const { return rect().y(); }
float width() const { return rect().width(); }
float height() const { return rect().height(); }
FloatSize size() const { return rect().size(); }
FloatPoint position() const { return rect().location(); }
virtual HitTestResult hit_test(const Point& position) const override;
virtual void set_needs_display() override;
@ -31,7 +32,7 @@ protected:
private:
virtual bool is_box() const override { return true; }
Rect m_rect;
FloatRect m_rect;
};
template<>

View file

@ -38,18 +38,18 @@ void LayoutImage::render(RenderingContext& context)
return;
// FIXME: This should be done at a different level. Also rect() does not include padding etc!
if (!context.viewport_rect().intersects(rect()))
if (!context.viewport_rect().intersects(enclosing_int_rect(rect())))
return;
if (renders_as_alt_text()) {
context.painter().set_font(Font::default_font());
StylePainter::paint_frame(context.painter(), rect(), FrameShape::Container, FrameShadow::Sunken, 2);
StylePainter::paint_frame(context.painter(), enclosing_int_rect(rect()), FrameShape::Container, FrameShadow::Sunken, 2);
auto alt = node().alt();
if (alt.is_empty())
alt = node().src();
context.painter().draw_text(rect(), alt, TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), TextElision::Right);
context.painter().draw_text(enclosing_int_rect(rect()), alt, TextAlignment::Center, style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black), TextElision::Right);
} else {
context.painter().draw_scaled_bitmap(rect(), *node().bitmap(), node().bitmap()->rect());
context.painter().draw_scaled_bitmap(enclosing_int_rect(rect()), *node().bitmap(), node().bitmap()->rect());
}
LayoutReplaced::render(context);
}

View file

@ -21,6 +21,6 @@ void LayoutListItem::layout()
append_child(*m_marker);
}
Rect marker_rect { x() - 8, y(), 4, height() };
FloatRect marker_rect { x() - 8, y(), 4, height() };
m_marker->set_rect(marker_rect);
}

View file

@ -13,7 +13,7 @@ LayoutListItemMarker::~LayoutListItemMarker()
void LayoutListItemMarker::render(RenderingContext& context)
{
Rect bullet_rect { 0, 0, 4, 4 };
bullet_rect.center_within(rect());
bullet_rect.center_within(enclosing_int_rect(rect()));
// FIXME: It would be nicer to not have to go via the parent here to get our inherited style.
context.painter().fill_rect(bullet_rect, parent()->style().color_or_fallback(CSS::PropertyID::Color, document(), Color::Black));
}

View file

@ -109,16 +109,16 @@ void LayoutNode::set_needs_display()
}
}
Point LayoutNode::box_type_agnostic_position() const
FloatPoint LayoutNode::box_type_agnostic_position() const
{
if (is_box())
return to<LayoutBox>(*this).position();
ASSERT(is_inline());
Point position;
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 = enclosing_int_rect(fragment.rect()).location();
position = fragment.rect().location();
return IterationDecision::Break;
}
return IterationDecision::Continue;

View file

@ -2,6 +2,7 @@
#include <AK/NonnullRefPtr.h>
#include <AK/Vector.h>
#include <LibDraw/FloatRect.h>
#include <LibDraw/Rect.h>
#include <LibHTML/CSS/StyleProperties.h>
#include <LibHTML/Layout/BoxModelMetrics.h>
@ -108,7 +109,7 @@ public:
template<typename T>
T* first_ancestor_of_type();
Point box_type_agnostic_position() const;
FloatPoint box_type_agnostic_position() const;
protected:
explicit LayoutNode(const Node*);

View file

@ -126,13 +126,13 @@ void LayoutText::for_each_source_line(Callback callback) const
void LayoutText::split_into_lines(LayoutBlock& container)
{
auto& font = style().font();
int space_width = font.glyph_width(' ') + font.glyph_spacing();
int line_height = style().line_height();
float space_width = font.glyph_width(' ') + font.glyph_spacing();
float line_height = style().line_height();
auto& line_boxes = container.line_boxes();
if (line_boxes.is_empty())
line_boxes.append(LineBox());
int available_width = container.width() - line_boxes.last().width();
float available_width = container.width() - line_boxes.last().width();
bool is_preformatted = style().string_or_fallback(CSS::PropertyID::WhiteSpace, "normal") == "pre";
if (is_preformatted) {
@ -176,7 +176,7 @@ void LayoutText::split_into_lines(LayoutBlock& container)
for (int i = 0; i < words.size(); ++i) {
auto& word = words[i];
int word_width;
float word_width;
bool is_whitespace = isspace(*word.view.begin());
if (is_whitespace)

View file

@ -28,7 +28,7 @@ NonnullRefPtr<StyleValue> parse_css_value(const StringView& view)
char* endptr = nullptr;
long value = strtol(String(view).characters(), &endptr, 10);
if (endptr && ((!*endptr) || (endptr[0] == 'p' && endptr[1] == 'x' && endptr[2] == '\0')))
return LengthStyleValue::create(Length(value, Length::Type::Absolute));
return LengthStyleValue::create(Length((float)value, Length::Type::Absolute));
if (string == "inherit")
return InheritStyleValue::create();
if (string == "initial")