From 09773048b6a15d19bb9843a018ce33b832525837 Mon Sep 17 00:00:00 2001 From: MacDue Date: Mon, 8 May 2023 21:15:35 +0100 Subject: [PATCH] Ladybird: Allow right clicking and inspecting elements This adds "Inspect Element" (currently the only entry) to the context menu for the page, which will do what you expect (most of the time), and bring up the Inspector with hovered element selected. --- Ladybird/BrowserWindow.cpp | 17 ++++++++++++ Ladybird/BrowserWindow.h | 1 + Ladybird/InspectorWidget.cpp | 52 ++++++++++++++++++++++++++++++++---- Ladybird/InspectorWidget.h | 12 +++++++++ Ladybird/ModelTranslator.h | 5 ++++ Ladybird/WebContentView.cpp | 16 ++++++++--- Ladybird/WebContentView.h | 7 ++++- 7 files changed, 101 insertions(+), 9 deletions(-) diff --git a/Ladybird/BrowserWindow.cpp b/Ladybird/BrowserWindow.cpp index 1d34d7b196..277c90c831 100644 --- a/Ladybird/BrowserWindow.cpp +++ b/Ladybird/BrowserWindow.cpp @@ -330,11 +330,28 @@ BrowserWindow::BrowserWindow(Browser::CookieJar& cookie_jar, StringView webdrive QObject::connect(m_tabs_container, &QTabWidget::tabCloseRequested, this, &BrowserWindow::close_tab); QObject::connect(close_current_tab_action, &QAction::triggered, this, &BrowserWindow::close_current_tab); + setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(this, &QWidget::customContextMenuRequested, this, &BrowserWindow::show_context_menu); + new_tab(s_settings->new_tab_page(), Web::HTML::ActivateTab::Yes); setCentralWidget(m_tabs_container); } +void BrowserWindow::show_context_menu(QPoint const& point) +{ + QMenu contextMenu("Context menu", this); + + QAction inspect_action("&Inspect Element", this); + connect(&inspect_action, &QAction::triggered, this, [this] { + if (!m_current_tab) + return; + m_current_tab->view().show_inspector(WebContentView::InspectorTarget::HoveredElement); + }); + contextMenu.addAction(&inspect_action); + contextMenu.exec(mapToGlobal(point)); +} + void BrowserWindow::set_current_tab(Tab* tab) { m_current_tab = tab; diff --git a/Ladybird/BrowserWindow.h b/Ladybird/BrowserWindow.h index fe3274e8ae..1c769020e6 100644 --- a/Ladybird/BrowserWindow.h +++ b/Ladybird/BrowserWindow.h @@ -49,6 +49,7 @@ public slots: void reset_zoom(); void select_all(); void copy_selected_text(); + void show_context_menu(QPoint const&); protected: bool eventFilter(QObject* obj, QEvent* event) override; diff --git a/Ladybird/InspectorWidget.cpp b/Ladybird/InspectorWidget.cpp index a0894c4038..ba567346ef 100644 --- a/Ladybird/InspectorWidget.cpp +++ b/Ladybird/InspectorWidget.cpp @@ -37,10 +37,10 @@ InspectorWidget::InspectorWidget() auto top_tap_widget = new QTabWidget; splitter->addWidget(top_tap_widget); - auto dom_tree_view = new QTreeView; - dom_tree_view->setHeaderHidden(true); - dom_tree_view->setModel(&m_dom_model); - QObject::connect(dom_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, + m_dom_tree_view = new QTreeView; + m_dom_tree_view->setHeaderHidden(true); + m_dom_tree_view->setModel(&m_dom_model); + QObject::connect(m_dom_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, [this](QItemSelection const& selected, QItemSelection const&) { auto indexes = selected.indexes(); if (indexes.size()) { @@ -48,7 +48,7 @@ InspectorWidget::InspectorWidget() set_selection(index); } }); - add_tab(top_tap_widget, dom_tree_view, "DOM"); + add_tab(top_tap_widget, m_dom_tree_view, "DOM"); auto accessibility_tree_view = new QTreeView; accessibility_tree_view->setHeaderHidden(true); @@ -74,6 +74,11 @@ InspectorWidget::InspectorWidget() void InspectorWidget::set_dom_json(StringView dom_json) { m_dom_model.set_underlying_model(WebView::DOMTreeModel::create(dom_json)); + m_dom_loaded = true; + if (m_pending_selection.has_value()) + set_selection(m_pending_selection.release_value()); + else + select_default_node(); } void InspectorWidget::set_accessibility_json(StringView accessibility_json) @@ -87,6 +92,8 @@ void InspectorWidget::clear_dom_json() // The accessibility tree is pretty much another form of the DOM tree, so should be cleared at the time time. m_accessibility_model.set_underlying_model(nullptr); clear_style_json(); + clear_selection(); + m_dom_loaded = false; } void InspectorWidget::load_style_json(StringView computed_style_json, StringView resolved_style_json, StringView custom_properties_json) @@ -101,6 +108,7 @@ void InspectorWidget::clear_style_json() m_computed_style_model.set_underlying_model(nullptr); m_resolved_style_model.set_underlying_model(nullptr); m_custom_properties_model.set_underlying_model(nullptr); + clear_selection(); } void InspectorWidget::closeEvent(QCloseEvent* event) @@ -108,6 +116,40 @@ void InspectorWidget::closeEvent(QCloseEvent* event) event->accept(); if (on_close) on_close(); + clear_selection(); +} + +void InspectorWidget::clear_selection() +{ + m_selection = {}; + m_dom_tree_view->clearSelection(); +} + +void InspectorWidget::set_selection(Selection selection) +{ + if (!m_dom_loaded) { + m_pending_selection = selection; + return; + } + + auto* model = verify_cast(m_dom_model.underlying_model().ptr()); + auto index = model->index_for_node(selection.dom_node_id, selection.pseudo_element); + auto qt_index = m_dom_model.to_qt(index); + + if (!qt_index.isValid()) { + dbgln("Failed to set DOM inspector selection! Could not find valid model index for node: {}", selection.dom_node_id); + return; + } + + m_dom_tree_view->scrollTo(qt_index); + m_dom_tree_view->setCurrentIndex(qt_index); +} + +void InspectorWidget::select_default_node() +{ + clear_style_json(); + m_dom_tree_view->collapseAll(); + m_dom_tree_view->setCurrentIndex({}); } void InspectorWidget::set_selection(GUI::ModelIndex index) diff --git a/Ladybird/InspectorWidget.h b/Ladybird/InspectorWidget.h index 1783dd546a..73cb8dcc04 100644 --- a/Ladybird/InspectorWidget.h +++ b/Ladybird/InspectorWidget.h @@ -30,6 +30,13 @@ public: bool operator==(Selection const& other) const = default; }; + bool dom_loaded() const { return m_dom_loaded; } + + void set_selection(Selection); + void clear_selection(); + + void select_default_node(); + void clear_dom_json(); void set_dom_json(StringView dom_json); @@ -52,6 +59,11 @@ private: ModelTranslator m_computed_style_model {}; ModelTranslator m_resolved_style_model {}; ModelTranslator m_custom_properties_model {}; + + QTreeView* m_dom_tree_view { nullptr }; + + bool m_dom_loaded { false }; + Optional m_pending_selection {}; }; } diff --git a/Ladybird/ModelTranslator.h b/Ladybird/ModelTranslator.h index a276636a1b..40f7c4cde0 100644 --- a/Ladybird/ModelTranslator.h +++ b/Ladybird/ModelTranslator.h @@ -23,6 +23,11 @@ public: endResetModel(); } + RefPtr underlying_model() + { + return m_model; + } + virtual int columnCount(QModelIndex const& parent) const override; virtual int rowCount(QModelIndex const& parent) const override; virtual QVariant data(QModelIndex const&, int role) const override; diff --git a/Ladybird/WebContentView.cpp b/Ladybird/WebContentView.cpp index f371dfa187..20f9781b8b 100644 --- a/Ladybird/WebContentView.cpp +++ b/Ladybird/WebContentView.cpp @@ -540,12 +540,22 @@ bool WebContentView::is_inspector_open() const return m_inspector_widget && m_inspector_widget->isVisible(); } -void WebContentView::show_inspector() +void WebContentView::show_inspector(InspectorTarget inspector_target) { + bool inspector_previously_loaded = m_inspector_widget; ensure_inspector_widget(); + if (!inspector_previously_loaded || !m_inspector_widget->dom_loaded()) { + inspect_dom_tree(); + inspect_accessibility_tree(); + } m_inspector_widget->show(); - inspect_dom_tree(); - inspect_accessibility_tree(); + + if (inspector_target == InspectorTarget::HoveredElement) { + auto hovered_node = get_hovered_node_id(); + m_inspector_widget->set_selection({ hovered_node }); + } else { + m_inspector_widget->select_default_node(); + } } void WebContentView::update_zoom() diff --git a/Ladybird/WebContentView.h b/Ladybird/WebContentView.h index 107db9195b..a51cfed348 100644 --- a/Ladybird/WebContentView.h +++ b/Ladybird/WebContentView.h @@ -93,7 +93,12 @@ public: void did_get_js_console_messages(i32 start_index, Vector message_types, Vector messages); void show_js_console(); - void show_inspector(); + + enum class InspectorTarget { + Document, + HoveredElement + }; + void show_inspector(InspectorTarget = InspectorTarget::Document); Ladybird::ConsoleWidget* console() { return m_console_widget; };