diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 8dd6edf412..1c2073a205 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -674,6 +674,8 @@ void Document::visit_edges(Cell::Visitor& visitor) visitor.visit(m_hovered_node); visitor.visit(m_inspected_node); visitor.visit(m_highlighted_node); + for (auto const& flexbox_highlight : m_flexbox_highlights) + visitor.visit(flexbox_highlight.node); for (auto const& grid_highlight : m_grid_highlights) visitor.visit(grid_highlight.node); visitor.visit(m_active_favicon); @@ -2671,6 +2673,44 @@ void Document::set_grid_highlighted_node(GC::Ptr node, Painting::GridInspe node->set_needs_repaint(); } +void Document::set_flexbox_highlighted_node(GC::Ptr node, Painting::FlexboxInspectorOverlayOptions options) +{ + if (!node) + return; + + for (auto& flexbox_highlight : m_flexbox_highlights) { + if (flexbox_highlight.node != node) + continue; + + flexbox_highlight.options = options; + node->set_needs_repaint(); + return; + } + + m_flexbox_highlights.append({ node, options }); + node->set_needs_repaint(); +} + +void Document::clear_flexbox_highlighted_node(GC::Ptr node) +{ + if (!node) { + for (auto const& flexbox_highlight : m_flexbox_highlights) { + if (flexbox_highlight.node) + flexbox_highlight.node->set_needs_repaint(); + } + m_flexbox_highlights.clear(); + return; + } + + auto old_size = m_flexbox_highlights.size(); + m_flexbox_highlights.remove_all_matching([&](auto const& flexbox_highlight) { + return flexbox_highlight.node == node; + }); + + if (m_flexbox_highlights.size() != old_size) + node->set_needs_repaint(); +} + void Document::clear_grid_highlighted_node(GC::Ptr node) { if (!node) { @@ -8375,6 +8415,16 @@ RefPtr Document::record_display_list(HTML::PaintConfig co highlighted_node()->paintable()->paint_inspector_overlay(context); } + for (auto const& flexbox_highlight : m_flexbox_highlights) { + if (!flexbox_highlight.node) + continue; + auto paintable = flexbox_highlight.node->paintable(); + auto const* paintable_box = as_if(paintable.ptr()); + if (!paintable_box) + continue; + paintable_box->paint_flexbox_inspector_overlay(context, flexbox_highlight.options); + } + for (auto const& grid_highlight : m_grid_highlights) { if (!grid_highlight.node) continue; diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 2e003f4c71..13aeca8987 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -308,6 +309,8 @@ public: GC::Ptr highlighted_node() const { return m_highlighted_node; } GC::Ptr highlighted_layout_node(); GC::Ptr highlighted_layout_node() const { return const_cast(this)->highlighted_layout_node(); } + void set_flexbox_highlighted_node(GC::Ptr, Painting::FlexboxInspectorOverlayOptions); + void clear_flexbox_highlighted_node(GC::Ptr); void set_grid_highlighted_node(GC::Ptr, Painting::GridInspectorOverlayOptions); void clear_grid_highlighted_node(GC::Ptr); @@ -1214,6 +1217,13 @@ private: GC::Ptr node; Painting::GridInspectorOverlayOptions options; }; + + struct FlexboxHighlight { + GC::Ptr node; + Painting::FlexboxInspectorOverlayOptions options; + }; + + Vector m_flexbox_highlights; Vector m_grid_highlights; Optional m_normal_link_color; diff --git a/Libraries/LibWeb/Painting/FlexboxInspectorOverlay.h b/Libraries/LibWeb/Painting/FlexboxInspectorOverlay.h new file mode 100644 index 0000000000..7f5ea555b2 --- /dev/null +++ b/Libraries/LibWeb/Painting/FlexboxInspectorOverlay.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2026, Ladybird contributors + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::Painting { + +struct FlexboxInspectorOverlayOptions { + Gfx::Color color { 148, 0, 255 }; +}; + +} diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 28cf2ffb6a..abc81cb809 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1320,6 +1321,75 @@ void PaintableBox::paint_grid_inspector_overlay(DisplayListRecordingContext& con }); } +void PaintableBox::paint_flexbox_inspector_overlay(DisplayListRecordingContext& context, FlexboxInspectorOverlayOptions const& options) const +{ + if (!m_flex_layout_data) + return; + + paint_with_inspector_overlay_context(context, [&] { + auto content_rect = absolute_united_content_rect(); + auto const origin = content_rect.location(); + auto const viewport_rect = document().viewport_rect(); + auto const& color = options.color; + auto line_color = color.with_alpha(220); + auto container_fill_color = color.with_alpha(28); + auto line_fill_color = color.with_alpha(18); + auto item_fill_color = color.with_alpha(32); + auto line_thickness = CSSPixels(1); + auto main_axis_is_horizontal = m_flex_layout_data->flex_direction == CSS::FlexDirection::Row + || m_flex_layout_data->flex_direction == CSS::FlexDirection::RowReverse; + + auto paint_rect = [&](CSSPixelRect const& rect, Gfx::Color rect_color) { + auto visible_rect = rect.intersected(viewport_rect); + if (visible_rect.is_empty()) + return; + context.display_list_recorder().fill_rect(context.enclosing_device_rect(visible_rect).to_type(), rect_color); + }; + + auto paint_outline = [&](CSSPixelRect const& rect, Gfx::Color rect_color) { + auto visible_rect = rect.intersected(viewport_rect); + if (visible_rect.is_empty()) + return; + context.display_list_recorder().draw_rect(context.enclosing_device_rect(visible_rect).to_type(), rect_color); + }; + + paint_rect(content_rect, container_fill_color); + paint_outline(content_rect, line_color); + + for (auto const& line : m_flex_layout_data->lines) { + auto line_rect = main_axis_is_horizontal + ? CSSPixelRect { content_rect.x(), origin.y() + line.cross_start, content_rect.width(), line.cross_size } + : CSSPixelRect { origin.x() + line.cross_start, content_rect.y(), line.cross_size, content_rect.height() }; + + paint_rect(line_rect, line_fill_color); + paint_outline(line_rect, line_color); + + for (auto const& item : line.items) { + auto item_rect = item.rect.translated(origin); + paint_rect(item_rect, item_fill_color); + paint_outline(item_rect, line_color); + } + } + + // Repaint the container border last so adjacent flex lines and items do not obscure it. + paint_outline(content_rect, line_color); + + if (main_axis_is_horizontal) { + for (auto const& line : m_flex_layout_data->lines) { + auto y = origin.y() + line.cross_start; + paint_rect({ content_rect.x(), y, content_rect.width(), line_thickness }, line_color); + paint_rect({ content_rect.x(), y + line.cross_size, content_rect.width(), line_thickness }, line_color); + } + } else { + for (auto const& line : m_flex_layout_data->lines) { + auto x = origin.x() + line.cross_start; + paint_rect({ x, content_rect.y(), line_thickness, content_rect.height() }, line_color); + paint_rect({ x + line.cross_size, content_rect.y(), line_thickness, content_rect.height() }, line_color); + } + } + }); +} + void PaintableBox::set_stacking_context(NonnullRefPtr stacking_context) { m_stacking_context = move(stacking_context); diff --git a/Libraries/LibWeb/Painting/PaintableBox.h b/Libraries/LibWeb/Painting/PaintableBox.h index 5db5944fd9..4f5090ec82 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Libraries/LibWeb/Painting/PaintableBox.h @@ -30,6 +30,7 @@ namespace Web::Painting { +struct FlexboxInspectorOverlayOptions; struct GridInspectorOverlayOptions; class ResizeHandle; class Scrollbar; @@ -283,6 +284,7 @@ public: Layout::GridLayoutData const* grid_layout_data() const { return m_grid_layout_data.ptr(); } void set_flex_layout_data(OwnPtr flex_layout_data) { m_flex_layout_data = move(flex_layout_data); } Layout::FlexLayoutData const* flex_layout_data() const { return m_flex_layout_data.ptr(); } + void paint_flexbox_inspector_overlay(DisplayListRecordingContext&, FlexboxInspectorOverlayOptions const&) const; void paint_grid_inspector_overlay(DisplayListRecordingContext&, GridInspectorOverlayOptions const&) const; void set_enclosing_scroll_frame_index(ScrollFrameIndex index) { m_enclosing_scroll_frame_index = index; }