mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 12:05:15 +00:00
LibWeb: Remember the selection state of each LayoutNode
Instead of computing it on the fly while painting each layout node, they now remember their selection state. This avoids a whole bunch of tree traversal while painting with anything selected.
This commit is contained in:
parent
cf4870c93e
commit
d47f77169f
Notes:
sideshowbarker
2024-07-19 03:21:04 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/d47f77169f8
6 changed files with 60 additions and 15 deletions
|
@ -110,6 +110,7 @@ void InProcessWebView::select_all()
|
|||
last_layout_node_index_in_node = downcast<LayoutText>(*last_layout_node).text_for_rendering().length() - 1;
|
||||
|
||||
layout_root->selection().set({ first_layout_node, 0 }, { last_layout_node, last_layout_node_index_in_node });
|
||||
layout_root->recompute_selection_states();
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -120,4 +120,30 @@ HitTestResult LayoutDocument::hit_test(const Gfx::IntPoint& position, HitTestTyp
|
|||
return stacking_context()->hit_test(position, type);
|
||||
}
|
||||
|
||||
void LayoutDocument::recompute_selection_states()
|
||||
{
|
||||
SelectionState state = SelectionState::None;
|
||||
|
||||
auto selection = this->selection().normalized();
|
||||
|
||||
for_each_in_subtree([&](auto& layout_node) {
|
||||
if (!selection.is_valid()) {
|
||||
// Everything gets SelectionState::None.
|
||||
} else if (&layout_node == selection.start().layout_node && &layout_node == selection.end().layout_node) {
|
||||
state = SelectionState::StartAndEnd;
|
||||
} else if (&layout_node == selection.start().layout_node) {
|
||||
state = SelectionState::Start;
|
||||
} else if (&layout_node == selection.end().layout_node) {
|
||||
state = SelectionState::End;
|
||||
} else {
|
||||
if (state == SelectionState::Start)
|
||||
state = SelectionState::Full;
|
||||
else if (state == SelectionState::End || state == SelectionState::StartAndEnd)
|
||||
state = SelectionState::None;
|
||||
}
|
||||
layout_node.set_selection_state(state);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
|
||||
void build_stacking_context_tree();
|
||||
|
||||
void recompute_selection_states();
|
||||
|
||||
private:
|
||||
LayoutRange m_selection;
|
||||
};
|
||||
|
|
|
@ -43,6 +43,14 @@ namespace Web {
|
|||
struct HitTestResult {
|
||||
RefPtr<LayoutNode> layout_node;
|
||||
int index_in_node { 0 };
|
||||
|
||||
enum InternalPosition {
|
||||
None,
|
||||
Before,
|
||||
Inside,
|
||||
After,
|
||||
};
|
||||
InternalPosition internal_position { None };
|
||||
};
|
||||
|
||||
enum class HitTestType {
|
||||
|
@ -142,6 +150,17 @@ public:
|
|||
|
||||
float font_size() const;
|
||||
|
||||
enum class SelectionState {
|
||||
None, // No selection
|
||||
Start, // Selection starts in this LayoutNode
|
||||
End, // Selection ends in this LayoutNode
|
||||
StartAndEnd, // Selection starts and ends in this LayoutNode
|
||||
Full, // Selection starts before and ends after this LayoutNode
|
||||
};
|
||||
|
||||
SelectionState selection_state() const { return m_selection_state; }
|
||||
void set_selection_state(SelectionState state) { m_selection_state = state; }
|
||||
|
||||
protected:
|
||||
LayoutNode(DOM::Document&, DOM::Node*);
|
||||
|
||||
|
@ -155,6 +174,7 @@ private:
|
|||
bool m_has_style { false };
|
||||
bool m_visible { true };
|
||||
bool m_children_are_inline { false };
|
||||
SelectionState m_selection_state { SelectionState::None };
|
||||
};
|
||||
|
||||
class LayoutNodeWithStyle : public LayoutNode {
|
||||
|
|
|
@ -100,6 +100,12 @@ int LineBoxFragment::text_index_at(float x) const
|
|||
|
||||
Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
||||
{
|
||||
if (layout_node().selection_state() == LayoutNode::SelectionState::None)
|
||||
return {};
|
||||
|
||||
if (layout_node().selection_state() == LayoutNode::SelectionState::Full)
|
||||
return absolute_rect();
|
||||
|
||||
auto selection = layout_node().root().selection().normalized();
|
||||
if (!selection.is_valid())
|
||||
return {};
|
||||
|
@ -110,7 +116,7 @@ Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
|||
const auto end_index = m_start + m_length;
|
||||
auto text = this->text();
|
||||
|
||||
if (&layout_node() == selection.start().layout_node && &layout_node() == selection.end().layout_node) {
|
||||
if (layout_node().selection_state() == LayoutNode::SelectionState::StartAndEnd) {
|
||||
// we are in the start/end node (both the same)
|
||||
if (start_index > selection.end().index_in_node)
|
||||
return {};
|
||||
|
@ -128,7 +134,7 @@ Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
|||
|
||||
return rect;
|
||||
}
|
||||
if (&layout_node() == selection.start().layout_node) {
|
||||
if (layout_node().selection_state() == LayoutNode::SelectionState::Start) {
|
||||
// we are in the start node
|
||||
if (end_index < selection.start().index_in_node)
|
||||
return {};
|
||||
|
@ -144,7 +150,7 @@ Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
|||
|
||||
return rect;
|
||||
}
|
||||
if (&layout_node() == selection.end().layout_node) {
|
||||
if (layout_node().selection_state() == LayoutNode::SelectionState::End) {
|
||||
// we are in the end node
|
||||
if (start_index > selection.end().index_in_node)
|
||||
return {};
|
||||
|
@ -160,18 +166,6 @@ Gfx::FloatRect LineBoxFragment::selection_rect(const Gfx::Font& font) const
|
|||
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -157,6 +157,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt
|
|||
if (result.layout_node && result.layout_node->node()) {
|
||||
m_frame.set_cursor_position(DOM::Position(*node, result.index_in_node));
|
||||
layout_root()->selection().set({ result.layout_node, result.index_in_node }, {});
|
||||
layout_root()->recompute_selection_states();
|
||||
dump_selection("MouseDown");
|
||||
m_in_mouse_selection = true;
|
||||
}
|
||||
|
@ -209,6 +210,7 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt
|
|||
auto hit = layout_root()->hit_test(position, HitTestType::TextCursor);
|
||||
if (hit.layout_node && hit.layout_node->node()) {
|
||||
layout_root()->selection().set_end({ hit.layout_node, hit.index_in_node });
|
||||
layout_root()->recompute_selection_states();
|
||||
}
|
||||
dump_selection("MouseMove");
|
||||
page_client.page_did_change_selection();
|
||||
|
|
Loading…
Add table
Reference in a new issue