From 7cdfb9c6b4b1470a6d27ebb1817614df864aa782 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 2 Jan 2020 14:55:19 +0100 Subject: [PATCH] Browser+LibHTML: Add resolved element style to the DOM inspector When selecting an element in the browser's DOM inspector, we now also show the resolved CSS properties (and their values) for that element. Since the inspector was growing a bit more complex, I moved it out of the "show inspector" action callback and into its own class. In the future, we will probably want to migrate the inspector down to LibHTML to make it accessible to other clients of the library, but for now we can keep working on it inside Browser. :^) --- Applications/Browser/InspectorWidget.cpp | 39 +++++++++++++++++ Applications/Browser/InspectorWidget.h | 20 +++++++++ Applications/Browser/Makefile | 3 +- Applications/Browser/main.cpp | 17 +++----- Libraries/LibHTML/DOMElementStyleModel.cpp | 50 ++++++++++++++++++++++ Libraries/LibHTML/DOMElementStyleModel.h | 33 ++++++++++++++ Libraries/LibHTML/Makefile | 1 + 7 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 Applications/Browser/InspectorWidget.cpp create mode 100644 Applications/Browser/InspectorWidget.h create mode 100644 Libraries/LibHTML/DOMElementStyleModel.cpp create mode 100644 Libraries/LibHTML/DOMElementStyleModel.h diff --git a/Applications/Browser/InspectorWidget.cpp b/Applications/Browser/InspectorWidget.cpp new file mode 100644 index 0000000000..f7dab25ac3 --- /dev/null +++ b/Applications/Browser/InspectorWidget.cpp @@ -0,0 +1,39 @@ +#include "InspectorWidget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +InspectorWidget::InspectorWidget(GWidget* parent) + : GWidget(parent) +{ + set_layout(make(Orientation::Vertical)); + auto splitter = GSplitter::construct(Orientation::Vertical, this); + m_dom_tree_view = GTreeView::construct(splitter); + m_dom_tree_view->on_selection = [this](auto& index) { + auto* node = static_cast(index.internal_data()); + node->document().set_inspected_node(node); + if (node->is_element()) + m_style_table_view->set_model(DOMElementStyleModel::create(to(*node))); + else + m_style_table_view->set_model(nullptr); + }; + m_style_table_view = GTableView::construct(splitter); + m_style_table_view->set_size_columns_to_fit_content(true); +} + +InspectorWidget::~InspectorWidget() +{ +} + +void InspectorWidget::set_document(Document* document) +{ + if (m_document == document) + return; + m_document = document; + m_dom_tree_view->set_model(DOMTreeModel::create(*document)); +} diff --git a/Applications/Browser/InspectorWidget.h b/Applications/Browser/InspectorWidget.h new file mode 100644 index 0000000000..5a1e0de62a --- /dev/null +++ b/Applications/Browser/InspectorWidget.h @@ -0,0 +1,20 @@ +#include + +class Document; +class GTableView; +class GTreeView; + +class InspectorWidget final : public GWidget { + C_OBJECT(InspectorWidget) +public: + virtual ~InspectorWidget(); + + void set_document(Document*); + +private: + explicit InspectorWidget(GWidget* parent); + + RefPtr m_dom_tree_view; + RefPtr m_style_table_view; + RefPtr m_document; +}; diff --git a/Applications/Browser/Makefile b/Applications/Browser/Makefile index e4843ac8cb..9e9a6d9485 100755 --- a/Applications/Browser/Makefile +++ b/Applications/Browser/Makefile @@ -1,5 +1,6 @@ OBJS = \ - main.o + main.o \ + InspectorWidget.o PROGRAM = Browser diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index d627d55bc4..4189bea3bf 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -1,4 +1,5 @@ #include "History.h" +#include "InspectorWidget.h" #include #include #include @@ -9,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -134,7 +134,6 @@ int main(int argc, char** argv) menubar->add_menu(move(app_menu)); RefPtr dom_inspector_window; - RefPtr dom_tree_view; auto inspect_menu = GMenu::construct("Inspect"); inspect_menu->add_action(GAction::create("View source", { Mod_Ctrl, Key_U }, [&](auto&) { @@ -161,17 +160,11 @@ int main(int argc, char** argv) dom_inspector_window = GWindow::construct(); dom_inspector_window->set_rect(100, 100, 300, 500); dom_inspector_window->set_title("DOM inspector"); - dom_tree_view = GTreeView::construct(nullptr); - dom_tree_view->on_selection = [](auto& index) { - auto* node = static_cast(index.internal_data()); - node->document().set_inspected_node(node); - }; - dom_inspector_window->set_main_widget(dom_tree_view); + auto dom_inspector_widget = InspectorWidget::construct(nullptr); + dom_inspector_window->set_main_widget(dom_inspector_widget); } - if (html_widget->document()) - dom_tree_view->set_model(DOMTreeModel::create(*html_widget->document())); - else - dom_tree_view->set_model(nullptr); + auto* inspector_widget = static_cast(dom_inspector_window->main_widget()); + inspector_widget->set_document(html_widget->document()); dom_inspector_window->show(); dom_inspector_window->move_to_front(); })); diff --git a/Libraries/LibHTML/DOMElementStyleModel.cpp b/Libraries/LibHTML/DOMElementStyleModel.cpp new file mode 100644 index 0000000000..e217c2bde2 --- /dev/null +++ b/Libraries/LibHTML/DOMElementStyleModel.cpp @@ -0,0 +1,50 @@ +#include "DOMElementStyleModel.h" +#include +#include +#include + +DOMElementStyleModel::DOMElementStyleModel(const Element& element) + : m_element(element) +{ + if (element.resolved_style()) { + element.resolved_style()->for_each_property([&](auto property_id, auto& property_value) { + Value value; + value.name = CSS::string_from_property_id(property_id); + value.value = property_value.to_string(); + m_values.append(value); + }); + } +} + +int DOMElementStyleModel::row_count(const GModelIndex&) const +{ + return m_values.size(); +} + +String DOMElementStyleModel::column_name(int column_index) const +{ + switch (column_index) { + case Column::PropertyName: + return "Name"; + case Column::PropertyValue: + return "Value"; + default: + ASSERT_NOT_REACHED(); + } +} +GVariant DOMElementStyleModel::data(const GModelIndex& index, Role role) const +{ + auto& value = m_values[index.row()]; + if (role == Role::Display) { + if (index.column() == Column::PropertyName) + return value.name; + if (index.column() == Column::PropertyValue) + return value.value; + } + return {}; +} + +void DOMElementStyleModel::update() +{ + did_update(); +} diff --git a/Libraries/LibHTML/DOMElementStyleModel.h b/Libraries/LibHTML/DOMElementStyleModel.h new file mode 100644 index 0000000000..7d074eb9ef --- /dev/null +++ b/Libraries/LibHTML/DOMElementStyleModel.h @@ -0,0 +1,33 @@ +#include +#include + +class Element; + +class DOMElementStyleModel final : public GModel { +public: + enum Column { + PropertyName, + PropertyValue, + __Count + }; + + static NonnullRefPtr create(const Element& element) { return adopt(*new DOMElementStyleModel(element)); } + + virtual int row_count(const GModelIndex& = GModelIndex()) const override; + virtual int column_count(const GModelIndex& = GModelIndex()) const override { return Column::__Count; } + virtual String column_name(int) const override; + virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; + virtual void update() override; + +private: + explicit DOMElementStyleModel(const Element&); + const Element& element() const { return *m_element; } + + NonnullRefPtr m_element; + + struct Value { + String name; + String value; + }; + Vector m_values; +}; diff --git a/Libraries/LibHTML/Makefile b/Libraries/LibHTML/Makefile index 64f0d47324..a0518ad08a 100644 --- a/Libraries/LibHTML/Makefile +++ b/Libraries/LibHTML/Makefile @@ -34,6 +34,7 @@ LIBHTML_OBJS = \ DOM/Node.o \ DOM/ParentNode.o \ DOM/Text.o \ + DOMElementStyleModel.o \ DOMTreeModel.o \ Dump.o \ FontCache.o \