Skip to content

Commit 5b2c649

Browse files
committed
LibWeb/Painting: Paint flexbox inspector overlays
1 parent d465a5f commit 5b2c649

5 files changed

Lines changed: 149 additions & 0 deletions

File tree

Libraries/LibWeb/DOM/Document.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,8 @@ void Document::visit_edges(Cell::Visitor& visitor)
674674
visitor.visit(m_hovered_node);
675675
visitor.visit(m_inspected_node);
676676
visitor.visit(m_highlighted_node);
677+
for (auto const& flexbox_highlight : m_flexbox_highlights)
678+
visitor.visit(flexbox_highlight.node);
677679
for (auto const& grid_highlight : m_grid_highlights)
678680
visitor.visit(grid_highlight.node);
679681
visitor.visit(m_active_favicon);
@@ -2671,6 +2673,44 @@ void Document::set_grid_highlighted_node(GC::Ptr<Node> node, Painting::GridInspe
26712673
node->set_needs_repaint();
26722674
}
26732675

2676+
void Document::set_flexbox_highlighted_node(GC::Ptr<Node> node, Painting::FlexboxInspectorOverlayOptions options)
2677+
{
2678+
if (!node)
2679+
return;
2680+
2681+
for (auto& flexbox_highlight : m_flexbox_highlights) {
2682+
if (flexbox_highlight.node != node)
2683+
continue;
2684+
2685+
flexbox_highlight.options = options;
2686+
node->set_needs_repaint();
2687+
return;
2688+
}
2689+
2690+
m_flexbox_highlights.append({ node, options });
2691+
node->set_needs_repaint();
2692+
}
2693+
2694+
void Document::clear_flexbox_highlighted_node(GC::Ptr<Node> node)
2695+
{
2696+
if (!node) {
2697+
for (auto const& flexbox_highlight : m_flexbox_highlights) {
2698+
if (flexbox_highlight.node)
2699+
flexbox_highlight.node->set_needs_repaint();
2700+
}
2701+
m_flexbox_highlights.clear();
2702+
return;
2703+
}
2704+
2705+
auto old_size = m_flexbox_highlights.size();
2706+
m_flexbox_highlights.remove_all_matching([&](auto const& flexbox_highlight) {
2707+
return flexbox_highlight.node == node;
2708+
});
2709+
2710+
if (m_flexbox_highlights.size() != old_size)
2711+
node->set_needs_repaint();
2712+
}
2713+
26742714
void Document::clear_grid_highlighted_node(GC::Ptr<Node> node)
26752715
{
26762716
if (!node) {
@@ -8375,6 +8415,16 @@ RefPtr<Painting::DisplayList> Document::record_display_list(HTML::PaintConfig co
83758415
highlighted_node()->paintable()->paint_inspector_overlay(context);
83768416
}
83778417

8418+
for (auto const& flexbox_highlight : m_flexbox_highlights) {
8419+
if (!flexbox_highlight.node)
8420+
continue;
8421+
auto paintable = flexbox_highlight.node->paintable();
8422+
auto const* paintable_box = as_if<Painting::PaintableBox>(paintable.ptr());
8423+
if (!paintable_box)
8424+
continue;
8425+
paintable_box->paint_flexbox_inspector_overlay(context, flexbox_highlight.options);
8426+
}
8427+
83788428
for (auto const& grid_highlight : m_grid_highlights) {
83798429
if (!grid_highlight.node)
83808430
continue;

Libraries/LibWeb/DOM/Document.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <LibWeb/HTML/SessionHistoryEntry.h>
4848
#include <LibWeb/HTML/VisibilityState.h>
4949
#include <LibWeb/InvalidateDisplayList.h>
50+
#include <LibWeb/Painting/FlexboxInspectorOverlay.h>
5051
#include <LibWeb/Painting/GridInspectorOverlay.h>
5152
#include <LibWeb/ResizeObserver/ResizeObserver.h>
5253
#include <LibWeb/TrustedTypes/InjectionSink.h>
@@ -308,6 +309,8 @@ class WEB_API Document
308309
GC::Ptr<Node const> highlighted_node() const { return m_highlighted_node; }
309310
GC::Ptr<Layout::Node> highlighted_layout_node();
310311
GC::Ptr<Layout::Node const> highlighted_layout_node() const { return const_cast<Document*>(this)->highlighted_layout_node(); }
312+
void set_flexbox_highlighted_node(GC::Ptr<Node>, Painting::FlexboxInspectorOverlayOptions);
313+
void clear_flexbox_highlighted_node(GC::Ptr<Node>);
311314
void set_grid_highlighted_node(GC::Ptr<Node>, Painting::GridInspectorOverlayOptions);
312315
void clear_grid_highlighted_node(GC::Ptr<Node>);
313316

@@ -1214,6 +1217,13 @@ class WEB_API Document
12141217
GC::Ptr<Node> node;
12151218
Painting::GridInspectorOverlayOptions options;
12161219
};
1220+
1221+
struct FlexboxHighlight {
1222+
GC::Ptr<Node> node;
1223+
Painting::FlexboxInspectorOverlayOptions options;
1224+
};
1225+
1226+
Vector<FlexboxHighlight> m_flexbox_highlights;
12171227
Vector<GridHighlight> m_grid_highlights;
12181228

12191229
Optional<Color> m_normal_link_color;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (c) 2026, Ladybird contributors
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <LibGfx/Color.h>
10+
11+
namespace Web::Painting {
12+
13+
struct FlexboxInspectorOverlayOptions {
14+
Gfx::Color color { 148, 0, 255 };
15+
};
16+
17+
}

Libraries/LibWeb/Painting/PaintableBox.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <LibWeb/Painting/ChromeMetrics.h>
3232
#include <LibWeb/Painting/DisplayListRecorder.h>
3333
#include <LibWeb/Painting/DisplayListRecordingContext.h>
34+
#include <LibWeb/Painting/FlexboxInspectorOverlay.h>
3435
#include <LibWeb/Painting/GridInspectorOverlay.h>
3536
#include <LibWeb/Painting/PaintableBox.h>
3637
#include <LibWeb/Painting/ResizeHandle.h>
@@ -1320,6 +1321,75 @@ void PaintableBox::paint_grid_inspector_overlay(DisplayListRecordingContext& con
13201321
});
13211322
}
13221323

1324+
void PaintableBox::paint_flexbox_inspector_overlay(DisplayListRecordingContext& context, FlexboxInspectorOverlayOptions const& options) const
1325+
{
1326+
if (!m_flex_layout_data)
1327+
return;
1328+
1329+
paint_with_inspector_overlay_context(context, [&] {
1330+
auto content_rect = absolute_united_content_rect();
1331+
auto const origin = content_rect.location();
1332+
auto const viewport_rect = document().viewport_rect();
1333+
auto const& color = options.color;
1334+
auto line_color = color.with_alpha(220);
1335+
auto container_fill_color = color.with_alpha(28);
1336+
auto line_fill_color = color.with_alpha(18);
1337+
auto item_fill_color = color.with_alpha(32);
1338+
auto line_thickness = CSSPixels(1);
1339+
auto main_axis_is_horizontal = m_flex_layout_data->flex_direction == CSS::FlexDirection::Row
1340+
|| m_flex_layout_data->flex_direction == CSS::FlexDirection::RowReverse;
1341+
1342+
auto paint_rect = [&](CSSPixelRect const& rect, Gfx::Color rect_color) {
1343+
auto visible_rect = rect.intersected(viewport_rect);
1344+
if (visible_rect.is_empty())
1345+
return;
1346+
context.display_list_recorder().fill_rect(context.enclosing_device_rect(visible_rect).to_type<int>(), rect_color);
1347+
};
1348+
1349+
auto paint_outline = [&](CSSPixelRect const& rect, Gfx::Color rect_color) {
1350+
auto visible_rect = rect.intersected(viewport_rect);
1351+
if (visible_rect.is_empty())
1352+
return;
1353+
context.display_list_recorder().draw_rect(context.enclosing_device_rect(visible_rect).to_type<int>(), rect_color);
1354+
};
1355+
1356+
paint_rect(content_rect, container_fill_color);
1357+
paint_outline(content_rect, line_color);
1358+
1359+
for (auto const& line : m_flex_layout_data->lines) {
1360+
auto line_rect = main_axis_is_horizontal
1361+
? CSSPixelRect { content_rect.x(), origin.y() + line.cross_start, content_rect.width(), line.cross_size }
1362+
: CSSPixelRect { origin.x() + line.cross_start, content_rect.y(), line.cross_size, content_rect.height() };
1363+
1364+
paint_rect(line_rect, line_fill_color);
1365+
paint_outline(line_rect, line_color);
1366+
1367+
for (auto const& item : line.items) {
1368+
auto item_rect = item.rect.translated(origin);
1369+
paint_rect(item_rect, item_fill_color);
1370+
paint_outline(item_rect, line_color);
1371+
}
1372+
}
1373+
1374+
// Repaint the container border last so adjacent flex lines and items do not obscure it.
1375+
paint_outline(content_rect, line_color);
1376+
1377+
if (main_axis_is_horizontal) {
1378+
for (auto const& line : m_flex_layout_data->lines) {
1379+
auto y = origin.y() + line.cross_start;
1380+
paint_rect({ content_rect.x(), y, content_rect.width(), line_thickness }, line_color);
1381+
paint_rect({ content_rect.x(), y + line.cross_size, content_rect.width(), line_thickness }, line_color);
1382+
}
1383+
} else {
1384+
for (auto const& line : m_flex_layout_data->lines) {
1385+
auto x = origin.x() + line.cross_start;
1386+
paint_rect({ x, content_rect.y(), line_thickness, content_rect.height() }, line_color);
1387+
paint_rect({ x + line.cross_size, content_rect.y(), line_thickness, content_rect.height() }, line_color);
1388+
}
1389+
}
1390+
});
1391+
}
1392+
13231393
void PaintableBox::set_stacking_context(NonnullRefPtr<StackingContext> stacking_context)
13241394
{
13251395
m_stacking_context = move(stacking_context);

Libraries/LibWeb/Painting/PaintableBox.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
namespace Web::Painting {
3232

33+
struct FlexboxInspectorOverlayOptions;
3334
struct GridInspectorOverlayOptions;
3435
class ResizeHandle;
3536
class Scrollbar;
@@ -283,6 +284,7 @@ class WEB_API PaintableBox : public Paintable {
283284
Layout::GridLayoutData const* grid_layout_data() const { return m_grid_layout_data.ptr(); }
284285
void set_flex_layout_data(OwnPtr<Layout::FlexLayoutData> flex_layout_data) { m_flex_layout_data = move(flex_layout_data); }
285286
Layout::FlexLayoutData const* flex_layout_data() const { return m_flex_layout_data.ptr(); }
287+
void paint_flexbox_inspector_overlay(DisplayListRecordingContext&, FlexboxInspectorOverlayOptions const&) const;
286288
void paint_grid_inspector_overlay(DisplayListRecordingContext&, GridInspectorOverlayOptions const&) const;
287289

288290
void set_enclosing_scroll_frame_index(ScrollFrameIndex index) { m_enclosing_scroll_frame_index = index; }

0 commit comments

Comments
 (0)