summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/Painting/PaintableBox.h
blob: fbe163f890d858dbc2a4b2477e88ad45c7a123e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <LibWeb/Painting/BorderPainting.h>
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
#include <LibWeb/Painting/Paintable.h>

namespace Web::Painting {

class PaintableBox : public Paintable {
public:
    static NonnullRefPtr<PaintableBox> create(Layout::Box const&);
    virtual ~PaintableBox();

    virtual void paint(PaintContext&, PaintPhase) const override;

    bool is_visible() const { return layout_box().is_visible(); }

    Layout::Box& layout_box() { return static_cast<Layout::Box&>(Paintable::layout_node()); }
    Layout::Box const& layout_box() const { return static_cast<Layout::Box const&>(Paintable::layout_node()); }

    auto const& box_model() const { return layout_box().box_model(); }

    struct OverflowData {
        Gfx::FloatRect scrollable_overflow_rect;
        Gfx::FloatPoint scroll_offset;
    };

    Gfx::FloatRect absolute_rect() const;
    Gfx::FloatPoint effective_offset() const;

    void set_offset(Gfx::FloatPoint const&);
    void set_offset(float x, float y) { set_offset({ x, y }); }

    Gfx::FloatSize const& content_size() const { return m_content_size; }
    void set_content_size(Gfx::FloatSize const&);
    void set_content_size(float width, float height) { set_content_size({ width, height }); }

    void set_content_width(float width) { set_content_size(width, content_height()); }
    void set_content_height(float height) { set_content_size(content_width(), height); }
    float content_width() const { return m_content_size.width(); }
    float content_height() const { return m_content_size.height(); }

    Gfx::FloatRect absolute_padding_box_rect() const
    {
        auto absolute_rect = this->absolute_rect();
        Gfx::FloatRect rect;
        rect.set_x(absolute_rect.x() - box_model().padding.left);
        rect.set_width(content_width() + box_model().padding.left + box_model().padding.right);
        rect.set_y(absolute_rect.y() - box_model().padding.top);
        rect.set_height(content_height() + box_model().padding.top + box_model().padding.bottom);
        return rect;
    }

    Gfx::FloatRect absolute_border_box_rect() const
    {
        auto padded_rect = this->absolute_padding_box_rect();
        Gfx::FloatRect rect;
        rect.set_x(padded_rect.x() - box_model().border.left);
        rect.set_width(padded_rect.width() + box_model().border.left + box_model().border.right);
        rect.set_y(padded_rect.y() - box_model().border.top);
        rect.set_height(padded_rect.height() + box_model().border.top + box_model().border.bottom);
        return rect;
    }

    float border_box_width() const
    {
        auto border_box = box_model().border_box();
        return content_width() + border_box.left + border_box.right;
    }

    float border_box_height() const
    {
        auto border_box = box_model().border_box();
        return content_height() + border_box.top + border_box.bottom;
    }

    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(); }

    bool has_overflow() const { return m_overflow_data.has_value(); }

    Optional<Gfx::FloatRect> scrollable_overflow_rect() const
    {
        if (!m_overflow_data.has_value())
            return {};
        return m_overflow_data->scrollable_overflow_rect;
    }

    void set_overflow_data(Optional<OverflowData> data) { m_overflow_data = move(data); }
    void set_containing_line_box_fragment(Optional<Layout::LineBoxFragmentCoordinate>);

    StackingContext* stacking_context() { return m_stacking_context; }
    StackingContext const* stacking_context() const { return m_stacking_context; }
    void set_stacking_context(NonnullOwnPtr<Painting::StackingContext>);
    StackingContext* enclosing_stacking_context();

    DOM::Node const* dom_node() const { return layout_box().dom_node(); }
    DOM::Node* dom_node() { return layout_box().dom_node(); }

    DOM::Document const& document() const { return layout_box().document(); }
    DOM::Document& document() { return layout_box().document(); }

    virtual void before_children_paint(PaintContext&, PaintPhase) const override;
    virtual void after_children_paint(PaintContext&, PaintPhase) const override;

    virtual Optional<HitTestResult> hit_test(Gfx::FloatPoint const&, HitTestType) const override;

    void invalidate_stacking_context();

protected:
    explicit PaintableBox(Layout::Box const&);

    virtual void paint_border(PaintContext&) const;
    virtual void paint_background(PaintContext&) const;
    virtual void paint_box_shadow(PaintContext&) const;

    virtual Gfx::FloatRect compute_absolute_rect() const;

    enum class ShrinkRadiiForBorders {
        Yes,
        No
    };

    Painting::BorderRadiiData normalized_border_radii_data(ShrinkRadiiForBorders shrink = ShrinkRadiiForBorders::No) const;

private:
    Optional<OverflowData> m_overflow_data;

    Gfx::FloatPoint m_offset;
    Gfx::FloatSize m_content_size;

    // Some boxes hang off of line box fragments. (inline-block, inline-table, replaced, etc)
    Optional<Layout::LineBoxFragmentCoordinate> m_containing_line_box_fragment;

    OwnPtr<Painting::StackingContext> m_stacking_context;

    Optional<Gfx::FloatRect> mutable m_absolute_rect;

    mutable bool m_clipping_overflow { false };
    Optional<BorderRadiusCornerClipper> mutable m_overflow_corner_radius_clipper;
};

class PaintableWithLines : public PaintableBox {
public:
    static NonnullRefPtr<PaintableWithLines> create(Layout::BlockContainer const& block_container)
    {
        return adopt_ref(*new PaintableWithLines(block_container));
    }
    virtual ~PaintableWithLines() override;

    Layout::BlockContainer const& layout_box() const;
    Layout::BlockContainer& layout_box();

    Vector<Layout::LineBox> const& line_boxes() const { return m_line_boxes; }
    void set_line_boxes(Vector<Layout::LineBox>&& line_boxes) { m_line_boxes = move(line_boxes); }

    template<typename Callback>
    void for_each_fragment(Callback callback) const
    {
        for (auto& line_box : line_boxes()) {
            for (auto& fragment : line_box.fragments()) {
                if (callback(fragment) == IterationDecision::Break)
                    return;
            }
        }
    }

    virtual void paint(PaintContext&, PaintPhase) const override;
    virtual bool wants_mouse_events() const override { return false; }
    virtual bool handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override;

    virtual Optional<HitTestResult> hit_test(Gfx::FloatPoint const&, HitTestType) const override;

protected:
    PaintableWithLines(Layout::BlockContainer const&);

private:
    Vector<Layout::LineBox> m_line_boxes;
};

}