summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-06-29 00:09:32 +0200
committerAndreas Kling <kling@serenityos.org>2020-06-29 00:39:51 +0200
commit706fc3d1aa4647e2c7c2a454cbca4e8646097f0e (patch)
tree57160479dcc296ef0d7b79d5b7077ed9ab61e447 /Libraries
parent2762e3d80a935f1fc2cedeec11a19a19e477a1d8 (diff)
downloadserenity-706fc3d1aa4647e2c7c2a454cbca4e8646097f0e.zip
LibWeb: Paint the text selection :^)
Text selection currently works at the LayoutNode level. The root of the layout tree has a LayoutRange selection() which in turn has two LayoutPosition objects: start() and end(). A LayoutPosition is a LayoutNode + a text offset into that node. We handle the selection painting in LayoutText::paint_fragment(), after the normal painting is finished. The basic mechanism is that each LayoutFragment is queried for its selection_rect(), and if a non-empty rect is returned, we clip to it and paint the text once more.
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibWeb/Layout/LayoutText.cpp9
-rw-r--r--Libraries/LibWeb/Layout/LineBoxFragment.cpp76
-rw-r--r--Libraries/LibWeb/Layout/LineBoxFragment.h3
3 files changed, 88 insertions, 0 deletions
diff --git a/Libraries/LibWeb/Layout/LayoutText.cpp b/Libraries/LibWeb/Layout/LayoutText.cpp
index 71ee4d9fe8..4199a50d0f 100644
--- a/Libraries/LibWeb/Layout/LayoutText.cpp
+++ b/Libraries/LibWeb/Layout/LayoutText.cpp
@@ -84,6 +84,7 @@ void LayoutText::paint_fragment(PaintContext& context, const LineBoxFragment& fr
if (is_underline)
painter.draw_line(enclosing_int_rect(fragment.absolute_rect()).bottom_left().translated(0, 1), enclosing_int_rect(fragment.absolute_rect()).bottom_right().translated(0, 1), color);
+ // FIXME: text-transform should be done already in layout, since uppercase glyphs may be wider than lowercase, etc.
auto text = m_text_for_rendering;
auto text_transform = specified_style().string_or_fallback(CSS::PropertyID::TextTransform, "none");
if (text_transform == "uppercase")
@@ -92,6 +93,14 @@ void LayoutText::paint_fragment(PaintContext& context, const LineBoxFragment& fr
text = m_text_for_rendering.to_lowercase();
painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, color);
+
+ auto selection_rect = fragment.selection_rect(specified_style().font());
+ if (!selection_rect.is_empty()) {
+ painter.fill_rect(enclosing_int_rect(selection_rect), context.palette().selection());
+ Gfx::PainterStateSaver saver(painter);
+ painter.add_clip_rect(enclosing_int_rect(selection_rect));
+ painter.draw_text(enclosing_int_rect(fragment.absolute_rect()), text.substring_view(fragment.start(), fragment.length()), Gfx::TextAlignment::TopLeft, context.palette().selection_text());
+ }
}
template<typename Callback>
diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.cpp b/Libraries/LibWeb/Layout/LineBoxFragment.cpp
index 714348fd50..651a3a251d 100644
--- a/Libraries/LibWeb/Layout/LineBoxFragment.cpp
+++ b/Libraries/LibWeb/Layout/LineBoxFragment.cpp
@@ -95,4 +95,80 @@ int LineBoxFragment::text_index_at(float x) const
return m_start + m_length - 1;
}
+Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
+{
+ auto& selection = layout_node().root().selection();
+ if (!selection.is_valid())
+ return {};
+ if (!layout_node().is_text())
+ return {};
+
+ const auto start_index = m_start;
+ const auto end_index = m_start + m_length;
+
+ if (&layout_node() == selection.start().layout_node && &layout_node() == selection.end().layout_node) {
+ // we are in the start/end node (both the same)
+ if (start_index > selection.end().index_in_node)
+ return {};
+ if (end_index < selection.start().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = selection.start().index_in_node - m_start;
+ auto selection_end_in_this_fragment = selection.end().index_in_node - m_start + 1;
+ auto pixel_distance_to_first_selected_character = font.width(text().substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text().substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment));
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+ if (&layout_node() == selection.start().layout_node) {
+ // we are in the start node
+ if (end_index < selection.start().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = selection.start().index_in_node - m_start;
+ auto selection_end_in_this_fragment = m_length;
+ auto pixel_distance_to_first_selected_character = font.width(text().substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text().substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment));
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+ if (&layout_node() == selection.end().layout_node) {
+ // we are in the end node
+ if (start_index > selection.end().index_in_node)
+ return {};
+
+ auto selection_start_in_this_fragment = 0;
+ auto selection_end_in_this_fragment = selection.end().index_in_node + 1;
+ auto pixel_distance_to_first_selected_character = font.width(text().substring_view(0, selection_start_in_this_fragment));
+ auto pixel_width_of_selection = font.width(text().substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment));
+
+ auto rect = absolute_rect();
+ rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
+ rect.set_width(pixel_width_of_selection);
+
+ return rect;
+ }
+
+ // are we in between start and end?
+ auto* node = selection.start().layout_node.ptr();
+ bool is_fully_selected = false;
+ for (; node && node != selection.end().layout_node.ptr(); node = node->next_in_pre_order()) {
+ if (node == &layout_node()) {
+ is_fully_selected = true;
+ break;
+ }
+ }
+ if (is_fully_selected)
+ return absolute_rect();
+ return {};
+}
+
}
diff --git a/Libraries/LibWeb/Layout/LineBoxFragment.h b/Libraries/LibWeb/Layout/LineBoxFragment.h
index 71d840c52e..5d08e1b72f 100644
--- a/Libraries/LibWeb/Layout/LineBoxFragment.h
+++ b/Libraries/LibWeb/Layout/LineBoxFragment.h
@@ -28,6 +28,7 @@
#include <AK/Weakable.h>
#include <LibGfx/FloatRect.h>
+#include <LibGfx/Forward.h>
#include <LibWeb/Forward.h>
namespace Web {
@@ -68,6 +69,8 @@ public:
int text_index_at(float x) const;
+ Gfx::FloatRect selection_rect(const Gfx::Font&) const;
+
private:
const LayoutNode& m_layout_node;
int m_start { 0 };