diff --git a/Ladybird/Qt/InspectorWidget.cpp b/Ladybird/Qt/InspectorWidget.cpp index 5539892361..43e3b3f845 100644 --- a/Ladybird/Qt/InspectorWidget.cpp +++ b/Ladybird/Qt/InspectorWidget.cpp @@ -4,10 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include -#include -#include - #include "InspectorWidget.h" #include #include @@ -40,42 +36,40 @@ InspectorWidget::InspectorWidget() 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()) { - auto index = m_dom_model.to_gui(indexes.first()); - set_selection(index); - } - }); add_tab(top_tab_widget, m_dom_tree_view, "DOM"); - auto accessibility_tree_view = new QTreeView; - accessibility_tree_view->setHeaderHidden(true); - accessibility_tree_view->setModel(&m_accessibility_model); - add_tab(top_tab_widget, accessibility_tree_view, "Accessibility"); + m_accessibility_tree_view = new QTreeView; + m_accessibility_tree_view->setHeaderHidden(true); + add_tab(top_tab_widget, m_accessibility_tree_view, "Accessibility"); - auto add_table_tab = [&](auto* tab_widget, auto& model, auto name) { - auto table_view = new QTableView; - table_view->setModel(&model); + auto add_table_tab = [&](auto* tab_widget, auto name) { + auto* table_view = new QTableView; table_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table_view->verticalHeader()->setVisible(false); table_view->horizontalHeader()->setVisible(false); add_tab(tab_widget, table_view, name); + return table_view; }; - auto node_tabs = new QTabWidget; - add_table_tab(node_tabs, m_computed_style_model, "Computed"); - add_table_tab(node_tabs, m_resolved_style_model, "Resolved"); - add_table_tab(node_tabs, m_custom_properties_model, "Variables"); + auto* node_tabs = new QTabWidget; + m_computed_style_table = add_table_tab(node_tabs, "Computed"); + m_resolved_style_table = add_table_tab(node_tabs, "Resolved"); + m_custom_properties_table = add_table_tab(node_tabs, "Variables"); splitter->addWidget(node_tabs); } void InspectorWidget::set_dom_json(StringView dom_json) { - m_dom_model.set_underlying_model(WebView::DOMTreeModel::create(dom_json)); + m_dom_model = TreeModel::create(TreeModel::Type::DOMTree, dom_json).release_value_but_fixme_should_propagate_errors(); + m_dom_tree_view->setModel(m_dom_model); m_dom_loaded = true; + + QObject::connect(m_dom_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, + [this](QItemSelection const& selected, QItemSelection const&) { + if (auto indexes = selected.indexes(); !indexes.empty()) + set_selection(indexes.first()); + }); + if (m_pending_selection.has_value()) set_selection(m_pending_selection.release_value()); else @@ -84,14 +78,19 @@ void InspectorWidget::set_dom_json(StringView dom_json) void InspectorWidget::set_accessibility_json(StringView accessibility_json) { - m_accessibility_model.set_underlying_model(WebView::AccessibilityTreeModel::create(accessibility_json)); + m_accessibility_model = TreeModel::create(TreeModel::Type::AccessibilityTree, accessibility_json).release_value_but_fixme_should_propagate_errors(); + m_accessibility_tree_view->setModel(m_accessibility_model); } void InspectorWidget::clear_dom_json() { - m_dom_model.set_underlying_model(nullptr); + m_dom_tree_view->setModel(nullptr); + m_dom_model = nullptr; + // 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); + m_accessibility_tree_view->setModel(nullptr); + m_accessibility_model = nullptr; + clear_style_json(); clear_selection(); m_dom_loaded = false; @@ -99,16 +98,27 @@ void InspectorWidget::clear_dom_json() void InspectorWidget::load_style_json(StringView computed_style_json, StringView resolved_style_json, StringView custom_properties_json) { - m_computed_style_model.set_underlying_model(WebView::StylePropertiesModel::create(computed_style_json)); - m_resolved_style_model.set_underlying_model(WebView::StylePropertiesModel::create(resolved_style_json)); - m_custom_properties_model.set_underlying_model(WebView::StylePropertiesModel::create(custom_properties_json)); + m_computed_style_model = PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, computed_style_json).release_value_but_fixme_should_propagate_errors(); + m_computed_style_table->setModel(m_computed_style_model); + + m_resolved_style_model = PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, resolved_style_json).release_value_but_fixme_should_propagate_errors(); + m_resolved_style_table->setModel(m_resolved_style_model); + + m_custom_properties_model = PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, custom_properties_json).release_value_but_fixme_should_propagate_errors(); + m_custom_properties_table->setModel(m_custom_properties_model); } 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); + m_computed_style_table->setModel(nullptr); + m_computed_style_model = nullptr; + + m_resolved_style_table->setModel(nullptr); + m_resolved_style_model = nullptr; + + m_custom_properties_table->setModel(nullptr); + m_custom_properties_model = nullptr; + clear_selection(); } @@ -133,17 +143,14 @@ void InspectorWidget::set_selection(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()) { + auto index = m_dom_model->index_for_node(selection.dom_node_id, selection.pseudo_element); + if (!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); + m_dom_tree_view->scrollTo(index); + m_dom_tree_view->setCurrentIndex(index); } void InspectorWidget::select_default_node() @@ -153,12 +160,12 @@ void InspectorWidget::select_default_node() m_dom_tree_view->setCurrentIndex({}); } -void InspectorWidget::set_selection(GUI::ModelIndex index) +void InspectorWidget::set_selection(QModelIndex const& index) { - if (!index.is_valid()) + if (!index.isValid()) return; - auto* json = static_cast(index.internal_data()); + auto const* json = static_cast(index.constInternalPointer()); VERIFY(json); Selection selection {}; diff --git a/Ladybird/Qt/InspectorWidget.h b/Ladybird/Qt/InspectorWidget.h index 73cb8dcc04..8d3bb63677 100644 --- a/Ladybird/Qt/InspectorWidget.h +++ b/Ladybird/Qt/InspectorWidget.h @@ -6,7 +6,7 @@ #pragma once -#include "ModelTranslator.h" +#include "ModelAdapter.h" #include "WebContentView.h" #include #include @@ -49,18 +49,22 @@ public: Function on_close; private: - void set_selection(GUI::ModelIndex); + void set_selection(QModelIndex const&); void closeEvent(QCloseEvent*) override; Selection m_selection; - ModelTranslator m_dom_model {}; - ModelTranslator m_accessibility_model {}; - ModelTranslator m_computed_style_model {}; - ModelTranslator m_resolved_style_model {}; - ModelTranslator m_custom_properties_model {}; + OwnPtr m_dom_model; + OwnPtr m_accessibility_model; + OwnPtr m_computed_style_model; + OwnPtr m_resolved_style_model; + OwnPtr m_custom_properties_model; QTreeView* m_dom_tree_view { nullptr }; + QTreeView* m_accessibility_tree_view { nullptr }; + QTableView* m_computed_style_table { nullptr }; + QTableView* m_resolved_style_table { nullptr }; + QTableView* m_custom_properties_table { nullptr }; bool m_dom_loaded { false }; Optional m_pending_selection {}; diff --git a/Ladybird/Qt/ModelAdapter.h b/Ladybird/Qt/ModelAdapter.h new file mode 100644 index 0000000000..53fc04535e --- /dev/null +++ b/Ladybird/Qt/ModelAdapter.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "StringUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Ladybird { + +template +class ModelAdapter : public QAbstractItemModel { +public: + using Type = typename ModelType::Type; + + static ErrorOr> create(Type type, StringView model, QObject* parent = nullptr) + { + auto json_model = TRY(JsonValue::from_string(model)); + if (!json_model.is_object()) + return Error::from_string_literal("Expected model to be a JSON object"); + + return adopt_own(*new ModelAdapter(type, move(json_model), parent)); + } + + virtual int rowCount(QModelIndex const& parent) const override + { + return m_model.row_count(to_web_view_model_index(parent)); + } + + virtual int columnCount(QModelIndex const& parent) const override + { + return m_model.column_count(to_web_view_model_index(parent)); + } + + virtual QModelIndex index(int row, int column, QModelIndex const& parent) const override + { + auto index = m_model.index(row, column, to_web_view_model_index(parent)); + return to_qt_model_index(index); + } + + virtual QModelIndex parent(QModelIndex const& index) const override + { + if constexpr (requires { m_model.parent(declval()); }) { + auto parent = m_model.parent(to_web_view_model_index(index)); + return to_qt_model_index(parent); + } else { + return {}; + } + } + + virtual QVariant data(QModelIndex const& index, int role) const override + { + if (role == Qt::DisplayRole) { + auto text = m_model.text_for_display(to_web_view_model_index(index)); + return qstring_from_ak_string(text); + } + + return {}; + } + + QModelIndex index_for_node(i32 node_id, Optional const& pseudo_element) const + { + if constexpr (requires { m_model.index_for_node(node_id, pseudo_element); }) { + auto parent = m_model.index_for_node(node_id, pseudo_element); + return to_qt_model_index(parent); + } else { + return {}; + } + } + +private: + ModelAdapter(Type type, JsonValue model, QObject* parent) + : QAbstractItemModel(parent) + , m_model(type, move(model)) + { + } + + ALWAYS_INLINE QModelIndex to_qt_model_index(WebView::ModelIndex const& index) const + { + if (!index.is_valid()) + return {}; + return createIndex(index.row, index.column, index.internal_data); + } + + ALWAYS_INLINE WebView::ModelIndex to_web_view_model_index(QModelIndex const& index) const + { + if (!index.isValid()) + return {}; + return { index.row(), index.column(), index.constInternalPointer() }; + } + + ModelType m_model; +}; + +using TreeModel = ModelAdapter; +using PropertyTableModel = ModelAdapter; + +}