diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp index daae7ae6d8d..876ecebb6ed 100644 --- a/Userland/Libraries/LibWeb/Layout/TextNode.cpp +++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp @@ -466,7 +466,7 @@ Optional TextNode::ChunkIterator::try_commit_chunk(Utf8View::It JS::GCPtr TextNode::create_paintable() const { - return Painting::TextPaintable::create(*this); + return Painting::TextPaintable::create(*this, text_for_rendering()); } } diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index bc337ab4502..ca23a201007 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -611,16 +612,15 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre if (button == GUI::MouseButton::Primary) { if (auto result = paint_root()->hit_test(position, Painting::HitTestType::TextCursor); result.has_value()) { - auto hit_paintable = result->paintable; - if (!hit_paintable->dom_node()) + if (!result->paintable->dom_node()) return true; - auto const& hit_layout_node = hit_paintable->layout_node(); - if (!hit_layout_node.is_text_node()) + if (!is(*result->paintable)) return true; + auto& hit_paintable = static_cast(*result->paintable); - auto& hit_dom_node = verify_cast(*hit_paintable->dom_node()); - auto const& text_for_rendering = verify_cast(hit_layout_node).text_for_rendering(); + auto& hit_dom_node = const_cast(verify_cast(*hit_paintable.dom_node())); + auto const& text_for_rendering = hit_paintable.text_for_rendering(); int first_word_break_before = [&] { // Start from one before the index position to prevent selecting only spaces between words, caused by the addition below. diff --git a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp index a07dc9f1614..ae3c3114b05 100644 --- a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace Web::Painting { @@ -174,8 +175,8 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const if (phase == PaintPhase::Foreground) { for_each_fragment([&](auto const& fragment, bool, bool) { - if (is(fragment.layout_node())) - paint_text_fragment(context, static_cast(fragment.layout_node()), fragment, phase); + if (is(fragment.paintable())) + paint_text_fragment(context, static_cast(fragment.paintable()), fragment, phase); }); } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 055a0293251..83f112f0901 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -612,9 +612,9 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph } } -void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_node, PaintableFragment const& fragment) +void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment) { - auto const& browsing_context = text_node.browsing_context(); + auto const& browsing_context = paintable.browsing_context(); if (!browsing_context.is_focused_context()) return; @@ -622,7 +622,7 @@ void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_ if (!browsing_context.cursor_blink_state()) return; - if (browsing_context.cursor_position()->node() != &text_node.dom_node()) + if (browsing_context.cursor_position()->node() != paintable.dom_node()) return; // NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted. @@ -634,9 +634,9 @@ void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_ auto fragment_rect = fragment.absolute_rect(); - auto text = text_node.text_for_rendering().bytes_as_string_view().substring_view(fragment.start(), fragment.length()); + auto text = fragment.string_view(); CSSPixelRect cursor_rect { - fragment_rect.x() + CSSPixels::nearest_value_for(text_node.first_available_font().width(text.substring_view(0, text_node.browsing_context().cursor_position()->offset() - fragment.start()))), + fragment_rect.x() + CSSPixels::nearest_value_for(paintable.layout_node().first_available_font().width(text.substring_view(0, paintable.browsing_context().cursor_position()->offset() - fragment.start()))), fragment_rect.top(), 1, fragment_rect.height() @@ -644,10 +644,10 @@ void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_ auto cursor_device_rect = context.rounded_device_rect(cursor_rect).to_type(); - context.recording_painter().draw_rect(cursor_device_rect, text_node.computed_values().color()); + context.recording_painter().draw_rect(cursor_device_rect, paintable.computed_values().color()); } -void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, PaintableFragment const& fragment) +void paint_text_decoration(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment) { auto& painter = context.recording_painter(); auto& font = fragment.layout_node().first_available_font(); @@ -655,11 +655,11 @@ void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, CSSPixels glyph_height = CSSPixels::nearest_value_for(font.pixel_size()); auto baseline = fragment.baseline(); - auto line_color = text_node.computed_values().text_decoration_color(); + auto line_color = paintable.computed_values().text_decoration_color(); auto const& text_paintable = static_cast(fragment.paintable()); auto device_line_thickness = context.rounded_device_pixels(text_paintable.text_decoration_thickness()); - auto text_decoration_lines = text_node.computed_values().text_decoration_line(); + auto text_decoration_lines = paintable.computed_values().text_decoration_line(); for (auto line : text_decoration_lines) { DevicePixelPoint line_start_point {}; DevicePixelPoint line_end_point {}; @@ -686,7 +686,7 @@ void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, return; } - switch (text_node.computed_values().text_decoration_style()) { + switch (paintable.computed_values().text_decoration_style()) { case CSS::TextDecorationStyle::Solid: painter.draw_line(line_start_point.to_type(), line_end_point.to_type(), line_color, device_line_thickness.value(), Gfx::Painter::LineStyle::Solid); break; @@ -722,7 +722,7 @@ void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, } } -void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_node, PaintableFragment const& fragment, PaintPhase phase) +void paint_text_fragment(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment, PaintPhase phase) { auto& painter = context.recording_painter(); @@ -730,16 +730,16 @@ void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_nod auto fragment_absolute_rect = fragment.absolute_rect(); auto fragment_absolute_device_rect = context.enclosing_device_rect(fragment_absolute_rect); - if (text_node.document().inspected_layout_node() == &text_node) + if (paintable.document().inspected_layout_node() == &paintable.layout_node()) context.recording_painter().draw_rect(fragment_absolute_device_rect.to_type(), Color::Magenta); - auto text = text_node.text_for_rendering(); + auto text = paintable.text_for_rendering(); DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) }; auto scale = context.device_pixels_per_css_pixel(); - painter.draw_text_run(baseline_start.to_type(), fragment.glyph_run(), text_node.computed_values().color(), fragment_absolute_device_rect.to_type(), scale); + painter.draw_text_run(baseline_start.to_type(), fragment.glyph_run(), paintable.computed_values().color(), fragment_absolute_device_rect.to_type(), scale); - auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.first_available_font())).to_type(); + auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(paintable.layout_node().first_available_font())).to_type(); if (!selection_rect.is_empty()) { painter.fill_rect(selection_rect, CSS::SystemColor::highlight()); RecordingPainterStateSaver saver(painter); @@ -747,8 +747,8 @@ void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_nod painter.draw_text_run(baseline_start.to_type(), fragment.glyph_run(), CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type(), scale); } - paint_text_decoration(context, text_node, fragment); - paint_cursor_if_needed(context, text_node, fragment); + paint_text_decoration(context, paintable, fragment); + paint_cursor_if_needed(context, paintable, fragment); } } @@ -811,8 +811,8 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const context.rounded_device_point(fragment_absolute_rect.top_left().translated(0, fragment.baseline())).to_type(), context.rounded_device_point(fragment_absolute_rect.top_right().translated(-1, fragment.baseline())).to_type(), Color::Red); } - if (is(fragment.layout_node())) - paint_text_fragment(context, static_cast(fragment.layout_node()), fragment, phase); + if (is(fragment.paintable())) + paint_text_fragment(context, static_cast(fragment.paintable()), fragment, phase); } if (should_clip_overflow) { diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 31d3b454e43..82c0dab16f2 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -314,8 +314,8 @@ private: Vector m_fragments; }; -void paint_text_decoration(PaintContext& context, Layout::Node const& text_node, PaintableFragment const& fragment); -void paint_cursor_if_needed(PaintContext& context, Layout::TextNode const& text_node, PaintableFragment const& fragment); -void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_node, PaintableFragment const& fragment, PaintPhase phase); +void paint_text_decoration(PaintContext&, TextPaintable const&, PaintableFragment const&); +void paint_cursor_if_needed(PaintContext&, TextPaintable const&, PaintableFragment const&); +void paint_text_fragment(PaintContext&, TextPaintable const&, PaintableFragment const&, PaintPhase); } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableFragment.cpp b/Userland/Libraries/LibWeb/Painting/PaintableFragment.cpp index 7d989d25572..388679031b9 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableFragment.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableFragment.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace Web::Painting { @@ -33,11 +34,11 @@ CSSPixelRect const PaintableFragment::absolute_rect() const int PaintableFragment::text_index_at(CSSPixels x) const { - if (!is(*m_layout_node)) + if (!is(paintable())) return 0; auto& layout_text = verify_cast(layout_node()); auto& font = layout_text.first_available_font(); - Utf8View view(layout_text.text_for_rendering().bytes_as_string_view().substring_view(m_start, m_length)); + Utf8View view(string_view()); CSSPixels relative_x = x - absolute_rect().x(); CSSPixels glyph_spacing = font.glyph_spacing(); @@ -67,9 +68,6 @@ CSSPixelRect PaintableFragment::selection_rect(Gfx::Font const& font) const if (paintable().selection_state() == Paintable::SelectionState::Full) return absolute_rect(); - if (!is(layout_node())) - return {}; - auto selection = paintable().document().get_selection(); if (!selection) return {}; @@ -81,8 +79,7 @@ CSSPixelRect PaintableFragment::selection_rect(Gfx::Font const& font) const auto const start_index = static_cast(m_start); auto const end_index = static_cast(m_start) + static_cast(m_length); - auto& layout_text = verify_cast(layout_node()); - auto text = layout_text.text_for_rendering().bytes_as_string_view().substring_view(m_start, m_length); + auto text = string_view(); if (paintable().selection_state() == Paintable::SelectionState::StartAndEnd) { // we are in the start/end node (both the same) @@ -140,4 +137,11 @@ CSSPixelRect PaintableFragment::selection_rect(Gfx::Font const& font) const return {}; } +StringView PaintableFragment::string_view() const +{ + if (!is(paintable())) + return {}; + return static_cast(paintable()).text_for_rendering().bytes_as_string_view().substring_view(m_start, m_length); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableFragment.h b/Userland/Libraries/LibWeb/Painting/PaintableFragment.h index 148647f5843..222bde133b6 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableFragment.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableFragment.h @@ -47,6 +47,8 @@ public: int text_index_at(CSSPixels) const; + StringView string_view() const; + private: JS::NonnullGCPtr m_layout_node; CSSPixelPoint m_offset; diff --git a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp index dcb9d2f4435..a104489c8cc 100644 --- a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp @@ -12,13 +12,14 @@ namespace Web::Painting { -JS::NonnullGCPtr TextPaintable::create(Layout::TextNode const& layout_node) +JS::NonnullGCPtr TextPaintable::create(Layout::TextNode const& layout_node, String const& text_for_rendering) { - return layout_node.heap().allocate_without_realm(layout_node); + return layout_node.heap().allocate_without_realm(layout_node, text_for_rendering); } -TextPaintable::TextPaintable(Layout::TextNode const& layout_node) +TextPaintable::TextPaintable(Layout::TextNode const& layout_node, String const& text_for_rendering) : Paintable(layout_node) + , m_text_for_rendering(text_for_rendering) { } diff --git a/Userland/Libraries/LibWeb/Painting/TextPaintable.h b/Userland/Libraries/LibWeb/Painting/TextPaintable.h index 61d20aff2ea..f32270995ab 100644 --- a/Userland/Libraries/LibWeb/Painting/TextPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/TextPaintable.h @@ -14,7 +14,7 @@ class TextPaintable final : public Paintable { JS_CELL(TextPaintable, Paintable); public: - static JS::NonnullGCPtr create(Layout::TextNode const&); + static JS::NonnullGCPtr create(Layout::TextNode const&, String const& text_for_rendering); Layout::TextNode const& layout_node() const { return static_cast(Paintable::layout_node()); } @@ -27,11 +27,14 @@ public: void set_text_decoration_thickness(CSSPixels thickness) { m_text_decoration_thickness = thickness; } CSSPixels text_decoration_thickness() const { return m_text_decoration_thickness; } + String const& text_for_rendering() const { return m_text_for_rendering; } + private: virtual bool is_text_paintable() const override { return true; } - explicit TextPaintable(Layout::TextNode const&); + TextPaintable(Layout::TextNode const&, String const& text_for_rendering); + String m_text_for_rendering; CSSPixels m_text_decoration_thickness { 0 }; };