LibWeb: Add a caret hit-test debug overlay
Add a debug-menu toggle for caret hit testing at the mouse position. Paint the insertion rect and log the result so selection bugs can be inspected without temporary probes. Request frames and repaint invalidation when the overlay state changes. Also repaint when the caret rect moves within the same text node.
This commit is contained in:
parent
5c26e86354
commit
acc86e9eb1
10 changed files with 89 additions and 2 deletions
|
|
@ -8436,10 +8436,32 @@ RefPtr<Painting::DisplayList> Document::record_display_list(HTML::PaintConfig co
|
|||
paintable_box->paint_grid_inspector_overlay(context, grid_highlight.options);
|
||||
}
|
||||
|
||||
if (config.should_show_caret_hit_test_debug_overlay && m_caret_hit_test_debug_rect.has_value()) {
|
||||
auto caret_rect = context.enclosing_device_rect(*m_caret_hit_test_debug_rect).to_type<int>();
|
||||
auto caret_x = caret_rect.x();
|
||||
auto caret_top = caret_rect.y();
|
||||
auto caret_bottom = caret_rect.bottom();
|
||||
auto marker_color = Color::Magenta;
|
||||
|
||||
display_list_recorder.draw_line({ caret_x, caret_top }, { caret_x, caret_bottom }, marker_color, 2);
|
||||
display_list_recorder.draw_line({ caret_x - 4, caret_top }, { caret_x + 4, caret_top }, marker_color, 2);
|
||||
display_list_recorder.draw_line({ caret_x - 4, caret_bottom }, { caret_x + 4, caret_bottom }, marker_color, 2);
|
||||
}
|
||||
|
||||
m_hit_test_display_list = move(hit_test_display_list);
|
||||
return display_list;
|
||||
}
|
||||
|
||||
void Document::set_caret_hit_test_debug_rect(Optional<CSSPixelRect> rect)
|
||||
{
|
||||
if (m_caret_hit_test_debug_rect == rect)
|
||||
return;
|
||||
|
||||
m_caret_hit_test_debug_rect = rect;
|
||||
set_needs_repaint(InvalidateDisplayList::Yes);
|
||||
page().client().request_frame();
|
||||
}
|
||||
|
||||
Painting::HitTestDisplayList const* Document::ensure_hit_test_display_list()
|
||||
{
|
||||
auto viewport_paintable = paintable();
|
||||
|
|
|
|||
|
|
@ -985,6 +985,8 @@ public:
|
|||
GC::Ptr<CaretPosition> caret_position_from_point(double x, double y, Bindings::CaretPositionFromPointOptions const&);
|
||||
TraversalDecision hit_test_all(CSSPixelPoint, Function<TraversalDecision(Painting::HitTestResult)> const&);
|
||||
|
||||
void set_caret_hit_test_debug_rect(Optional<CSSPixelRect>);
|
||||
|
||||
void set_needs_to_record_display_list();
|
||||
|
||||
Unicode::Segmenter& grapheme_segmenter() const;
|
||||
|
|
@ -1480,6 +1482,7 @@ private:
|
|||
bool m_needs_accumulated_visual_contexts_update { false };
|
||||
bool m_needs_invalidation_of_elements_affected_by_has { false };
|
||||
RefPtr<Painting::HitTestDisplayList> m_hit_test_display_list;
|
||||
Optional<CSSPixelRect> m_caret_hit_test_debug_rect;
|
||||
|
||||
mutable StyleInvalidationCounters m_style_invalidation_counters;
|
||||
mutable u64 m_style_invalidations_since_last_counter_dump { 0 };
|
||||
|
|
|
|||
|
|
@ -434,8 +434,10 @@ void Navigable::initialize_navigable(NonnullRefPtr<DocumentState> document_state
|
|||
|
||||
// 5. Set navigable's parent to parent.
|
||||
m_parent = parent;
|
||||
if (parent)
|
||||
if (parent) {
|
||||
m_should_show_line_box_borders = parent->m_should_show_line_box_borders;
|
||||
m_should_show_caret_hit_test_debug_overlay = parent->m_should_show_caret_hit_test_debug_overlay;
|
||||
}
|
||||
if (parent && !m_is_svg_page && has_compositor_context() && parent->has_compositor_context()) {
|
||||
m_compositor_surface_id = Painting::allocate_compositor_surface_id();
|
||||
compositor_context().set_presentation_mode(Compositor::PublishToCompositorSurface {
|
||||
|
|
@ -3444,6 +3446,21 @@ void Navigable::set_should_show_line_box_borders(bool value)
|
|||
child_navigable->set_should_show_line_box_borders(value);
|
||||
}
|
||||
|
||||
void Navigable::set_should_show_caret_hit_test_debug_overlay(bool value)
|
||||
{
|
||||
m_should_show_caret_hit_test_debug_overlay = value;
|
||||
|
||||
if (auto document = active_document()) {
|
||||
if (value)
|
||||
document->set_needs_repaint(Badge<HTML::Navigable> {}, InvalidateDisplayList::Yes);
|
||||
else
|
||||
document->set_caret_hit_test_debug_rect({});
|
||||
}
|
||||
|
||||
for (auto const& child_navigable : child_navigables())
|
||||
child_navigable->set_should_show_caret_hit_test_debug_overlay(value);
|
||||
}
|
||||
|
||||
bool Navigable::record_display_list_and_scroll_state(PaintConfig paint_config)
|
||||
{
|
||||
if (!has_compositor_context())
|
||||
|
|
@ -3504,7 +3521,7 @@ void Navigable::paint_next_frame()
|
|||
}
|
||||
|
||||
auto viewport_rect = page().css_to_device_rect(this->viewport_rect()).to_type<int>();
|
||||
PaintConfig paint_config { .paint_overlay = true, .should_show_line_box_borders = m_should_show_line_box_borders };
|
||||
PaintConfig paint_config { .paint_overlay = true, .should_show_line_box_borders = m_should_show_line_box_borders, .should_show_caret_hit_test_debug_overlay = m_should_show_caret_hit_test_debug_overlay };
|
||||
if (is_top_level_traversable()) {
|
||||
paint_config.canvas_fill_rect = Gfx::IntRect { {}, viewport_rect.size() };
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -257,6 +257,8 @@ public:
|
|||
bool pending_set_browser_zoom_request() const { return m_pending_set_browser_zoom_request; }
|
||||
|
||||
void set_should_show_line_box_borders(bool);
|
||||
void set_should_show_caret_hit_test_debug_overlay(bool);
|
||||
bool should_show_caret_hit_test_debug_overlay() const { return m_should_show_caret_hit_test_debug_overlay; }
|
||||
|
||||
bool is_svg_page() const { return m_is_svg_page; }
|
||||
|
||||
|
|
@ -344,6 +346,7 @@ private:
|
|||
bool m_needs_to_record_display_list { true };
|
||||
bool m_pending_set_browser_zoom_request { false };
|
||||
bool m_should_show_line_box_borders { false };
|
||||
bool m_should_show_caret_hit_test_debug_overlay { false };
|
||||
Optional<PaintConfig> m_compositor_display_list_paint_config;
|
||||
Painting::DisplayListResourceStorage m_display_list_resource_storage;
|
||||
Painting::DisplayListResourceSet m_compositor_display_list_resources;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Web::HTML {
|
|||
struct PaintConfig {
|
||||
bool paint_overlay { false };
|
||||
bool should_show_line_box_borders { false };
|
||||
bool should_show_caret_hit_test_debug_overlay { false };
|
||||
Optional<Gfx::IntRect> canvas_fill_rect {};
|
||||
|
||||
bool operator==(PaintConfig const& other) const = default;
|
||||
|
|
|
|||
|
|
@ -271,6 +271,34 @@ EventResult EventHandler::handle_mousemove(CSSPixelPoint visual_viewport_positio
|
|||
if (!paint_root())
|
||||
return EventResult::Dropped;
|
||||
|
||||
ArmedScopeGuard update_caret_hit_test_debug_overlay = [&] {
|
||||
if (!m_navigable->should_show_caret_hit_test_debug_overlay())
|
||||
return;
|
||||
if (m_navigable->active_document() != document)
|
||||
return;
|
||||
if (!document->is_fully_active())
|
||||
return;
|
||||
|
||||
document->update_layout(DOM::UpdateLayoutReason::EventHandlerHandleMouseMove);
|
||||
if (!paint_root())
|
||||
return;
|
||||
|
||||
auto caret_position = document->caret_position_from_point(visual_viewport_position);
|
||||
if (caret_position.has_value()) {
|
||||
document->set_caret_hit_test_debug_rect(caret_position->debug_rect);
|
||||
dbgln("Caret hit test: point=({}, {}) boundary=({}, {}) paintable={} debug_rect={}",
|
||||
visual_viewport_position.x(),
|
||||
visual_viewport_position.y(),
|
||||
caret_position->boundary.node->debug_description(),
|
||||
caret_position->boundary.offset,
|
||||
caret_position->paintable->debug_description(),
|
||||
caret_position->debug_rect);
|
||||
} else {
|
||||
document->set_caret_hit_test_debug_rect({});
|
||||
dbgln("Caret hit test: point=({}, {}) no caret position", visual_viewport_position.x(), visual_viewport_position.y());
|
||||
}
|
||||
};
|
||||
|
||||
if (m_mousedown_target_is_drag_candidate) {
|
||||
static constexpr CSSPixels DRAG_THRESHOLD = 5;
|
||||
auto delta = visual_viewport_position - *m_mousedown_visual_viewport_position;
|
||||
|
|
|
|||
|
|
@ -1519,6 +1519,9 @@ void Application::initialize_actions()
|
|||
|
||||
m_show_line_box_borders_action = Action::create_checkable("Show Line Box Borders"sv, ActionID::ShowLineBoxBorders, check(m_show_line_box_borders_action, "set-line-box-borders"sv));
|
||||
m_debug_menu->add_action(*m_show_line_box_borders_action);
|
||||
|
||||
m_show_caret_hit_test_debug_overlay_action = Action::create_checkable("Show Caret Hit Test Debug Overlay"sv, ActionID::ShowCaretHitTestDebugOverlay, check(m_show_caret_hit_test_debug_overlay_action, "set-caret-hit-test-debug-overlay"sv));
|
||||
m_debug_menu->add_action(*m_show_caret_hit_test_debug_overlay_action);
|
||||
m_debug_menu->add_separator();
|
||||
|
||||
m_debug_menu->add_action(Action::create("Collect Garbage"sv, ActionID::CollectGarbage, debug_request("collect-garbage"sv)));
|
||||
|
|
@ -1565,6 +1568,7 @@ void Application::apply_view_options(Badge<ViewImplementation>, ViewImplementati
|
|||
view.set_preferred_motion(m_motion);
|
||||
|
||||
view.debug_request("set-line-box-borders"sv, m_show_line_box_borders_action->checked() ? "on"sv : "off"sv);
|
||||
view.debug_request("set-caret-hit-test-debug-overlay"sv, m_show_caret_hit_test_debug_overlay_action->checked() ? "on"sv : "off"sv);
|
||||
view.debug_request("scripting"sv, m_enable_scripting_action->checked() ? "on"sv : "off"sv);
|
||||
view.debug_request("content-blocking"sv, m_enable_content_blocking_action->checked() ? "on"sv : "off"sv);
|
||||
if (m_content_blocker_list_buffer.has_value())
|
||||
|
|
|
|||
|
|
@ -375,6 +375,7 @@ private:
|
|||
|
||||
RefPtr<Menu> m_debug_menu;
|
||||
RefPtr<Action> m_show_line_box_borders_action;
|
||||
RefPtr<Action> m_show_caret_hit_test_debug_overlay_action;
|
||||
RefPtr<Action> m_enable_scripting_action;
|
||||
RefPtr<Action> m_enable_content_blocking_action;
|
||||
RefPtr<Action> m_block_pop_ups_action;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ enum class ActionID {
|
|||
DumpGCGraph,
|
||||
DumpWasmStats,
|
||||
ShowLineBoxBorders,
|
||||
ShowCaretHitTestDebugOverlay,
|
||||
CollectGarbage,
|
||||
CrashCurrentPage,
|
||||
SpoofUserAgent,
|
||||
|
|
|
|||
|
|
@ -463,6 +463,13 @@ void ConnectionFromClient::debug_request(u64 page_id, ByteString request, ByteSt
|
|||
return;
|
||||
}
|
||||
|
||||
if (request == "set-caret-hit-test-debug-overlay") {
|
||||
bool state = argument == "on";
|
||||
auto traversable = page->page().top_level_traversable();
|
||||
traversable->set_should_show_caret_hit_test_debug_overlay(state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request == "clear-cache") {
|
||||
Web::Fetch::Fetching::clear_http_memory_cache();
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue