mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +00:00 
			
		
		
		
	Ladybird: Add the node properties tabs to the inspector
This now allows you to view the computed and resolved style values, along with the CSS variables of a node.
This commit is contained in:
		
							parent
							
								
									aa85a88158
								
							
						
					
					
						commit
						33249c727a
					
				
					 5 changed files with 103 additions and 19 deletions
				
			
		
							
								
								
									
										19
									
								
								Ladybird/DOMNodeProperties.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Ladybird/DOMNodeProperties.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2022, MacDue <macdue@dueutil.tech> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <AK/String.h> | ||||
| 
 | ||||
| namespace Ladybird { | ||||
| 
 | ||||
| struct DOMNodeProperties { | ||||
|     String computed_style_json; | ||||
|     String resolved_style_json; | ||||
|     String custom_properties_json; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -6,9 +6,16 @@ | |||
| 
 | ||||
| #define AK_DONT_REPLACE_STD | ||||
| 
 | ||||
| #include "InspectorWidget.h" | ||||
| #include <LibWebView/DOMTreeModel.h> | ||||
| #include <LibWebView/StylePropertiesModel.h> | ||||
| 
 | ||||
| #include "InspectorWidget.h" | ||||
| #include <QCloseEvent> | ||||
| #include <QHeaderView> | ||||
| #include <QSplitter> | ||||
| #include <QStringList> | ||||
| #include <QTabWidget> | ||||
| #include <QTableView> | ||||
| #include <QTreeView> | ||||
| #include <QVBoxLayout> | ||||
| 
 | ||||
|  | @ -17,12 +24,15 @@ namespace Ladybird { | |||
| InspectorWidget::InspectorWidget() | ||||
| { | ||||
|     setLayout(new QVBoxLayout); | ||||
|     m_tree_view = new QTreeView; | ||||
|     m_tree_view->setHeaderHidden(true); | ||||
|     m_tree_view->expandToDepth(3); | ||||
|     layout()->addWidget(m_tree_view); | ||||
|     m_tree_view->setModel(&m_dom_model); | ||||
|     QObject::connect(m_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, | ||||
|     auto splitter = new QSplitter(this); | ||||
|     layout()->addWidget(splitter); | ||||
|     splitter->setOrientation(Qt::Vertical); | ||||
|     auto tree_view = new QTreeView; | ||||
|     tree_view->setHeaderHidden(true); | ||||
|     tree_view->expandToDepth(3); | ||||
|     splitter->addWidget(tree_view); | ||||
|     tree_view->setModel(&m_dom_model); | ||||
|     QObject::connect(tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, | ||||
|         [this](QItemSelection const& selected, QItemSelection const&) { | ||||
|             auto indexes = selected.indexes(); | ||||
|             if (indexes.size()) { | ||||
|  | @ -30,16 +40,49 @@ InspectorWidget::InspectorWidget() | |||
|                 set_selection(index); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|     auto add_table_tab = [&](auto* tab_widget, auto& model, auto name) { | ||||
|         auto container = new QWidget; | ||||
|         auto table_view = new QTableView; | ||||
|         table_view->setModel(&model); | ||||
|         container->setLayout(new QVBoxLayout); | ||||
|         container->layout()->addWidget(table_view); | ||||
|         table_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); | ||||
|         table_view->verticalHeader()->setVisible(false); | ||||
|         table_view->horizontalHeader()->setVisible(false); | ||||
|         tab_widget->addTab(container, name); | ||||
|     }; | ||||
| 
 | ||||
|     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"); | ||||
|     splitter->addWidget(node_tabs); | ||||
| } | ||||
| 
 | ||||
| void InspectorWidget::set_dom_json(StringView dom_json) | ||||
| { | ||||
|     m_dom_model.set_underlying_model(WebView::DOMTreeModel::create(dom_json)); | ||||
| } | ||||
| 
 | ||||
| void InspectorWidget::clear_dom_json() | ||||
| { | ||||
|     m_dom_model.set_underlying_model(nullptr); | ||||
|     clear_style_json(); | ||||
| } | ||||
| 
 | ||||
| void InspectorWidget::set_dom_json(StringView dom_json) | ||||
| void InspectorWidget::load_style_json(StringView computed_style_json, StringView resolved_style_json, StringView custom_properties_json) | ||||
| { | ||||
|     m_dom_model.set_underlying_model(::WebView::DOMTreeModel::create(dom_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)); | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| void InspectorWidget::closeEvent(QCloseEvent* event) | ||||
|  | @ -70,7 +113,13 @@ void InspectorWidget::set_selection(GUI::ModelIndex index) | |||
|     m_selection = selection; | ||||
| 
 | ||||
|     VERIFY(on_dom_node_inspected); | ||||
|     on_dom_node_inspected(m_selection.dom_node_id, m_selection.pseudo_element); | ||||
|     auto maybe_inspected_node_properties = on_dom_node_inspected(m_selection.dom_node_id, m_selection.pseudo_element); | ||||
|     if (!maybe_inspected_node_properties.is_error()) { | ||||
|         auto properties = maybe_inspected_node_properties.release_value(); | ||||
|         load_style_json(properties.computed_style_json, properties.resolved_style_json, properties.custom_properties_json); | ||||
|     } else { | ||||
|         clear_style_json(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "DOMNodeProperties.h" | ||||
| #include "ModelTranslator.h" | ||||
| #include <AK/Optional.h> | ||||
| #include <AK/StringView.h> | ||||
|  | @ -13,6 +14,7 @@ | |||
| #include <QWidget> | ||||
| 
 | ||||
| class QTreeView; | ||||
| class QTableView; | ||||
| 
 | ||||
| namespace Ladybird { | ||||
| 
 | ||||
|  | @ -31,16 +33,22 @@ public: | |||
|     void clear_dom_json(); | ||||
|     void set_dom_json(StringView dom_json); | ||||
| 
 | ||||
|     Function<void(i32, Optional<Web::CSS::Selector::PseudoElement>)> on_dom_node_inspected; | ||||
|     void load_style_json(StringView computed_style_json, StringView resolved_style_json, StringView custom_properties_json); | ||||
|     void clear_style_json(); | ||||
| 
 | ||||
|     Function<ErrorOr<DOMNodeProperties>(i32, Optional<Web::CSS::Selector::PseudoElement>)> on_dom_node_inspected; | ||||
|     Function<void()> on_close; | ||||
| 
 | ||||
| private: | ||||
|     void set_selection(GUI::ModelIndex); | ||||
|     void closeEvent(QCloseEvent*) override; | ||||
| 
 | ||||
|     QTreeView* m_tree_view { nullptr }; | ||||
|     ModelTranslator m_dom_model {}; | ||||
|     Selection m_selection; | ||||
| 
 | ||||
|     ModelTranslator m_dom_model {}; | ||||
|     ModelTranslator m_computed_style_model {}; | ||||
|     ModelTranslator m_resolved_style_model {}; | ||||
|     ModelTranslator m_custom_properties_model {}; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -511,7 +511,7 @@ void WebContentView::ensure_inspector_widget() | |||
|     }; | ||||
| 
 | ||||
|     m_inspector_widget->on_dom_node_inspected = [&](auto id, auto pseudo_element) { | ||||
|         inspect_dom_node(id, pseudo_element); | ||||
|         return inspect_dom_node(id, pseudo_element); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  | @ -531,15 +531,21 @@ void WebContentView::inspect_dom_tree() | |||
|     client().async_inspect_dom_tree(); | ||||
| } | ||||
| 
 | ||||
| void WebContentView::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element) | ||||
| ErrorOr<Ladybird::DOMNodeProperties> WebContentView::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element) | ||||
| { | ||||
|     // TODO: Use and display response
 | ||||
|     (void)client().inspect_dom_node(node_id, pseudo_element); | ||||
|     auto response = client().inspect_dom_node(node_id, pseudo_element); | ||||
|     if (!response.has_style()) | ||||
|         return Error::from_string_view("Inspected node returned no style"sv); | ||||
|     return Ladybird::DOMNodeProperties { | ||||
|         .computed_style_json = TRY(String::from_deprecated_string(response.take_computed_style())), | ||||
|         .resolved_style_json = TRY(String::from_deprecated_string(response.take_resolved_style())), | ||||
|         .custom_properties_json = TRY(String::from_deprecated_string(response.take_custom_properties())), | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| void WebContentView::clear_inspected_dom_node() | ||||
| { | ||||
|     inspect_dom_node(0, {}); | ||||
|     (void)inspect_dom_node(0, {}); | ||||
| } | ||||
| 
 | ||||
| void WebContentView::show_inspector() | ||||
|  |  | |||
|  | @ -23,6 +23,8 @@ | |||
| #include <QAbstractScrollArea> | ||||
| #include <QPointer> | ||||
| 
 | ||||
| #include "DOMNodeProperties.h" | ||||
| 
 | ||||
| class QTextEdit; | ||||
| class QLineEdit; | ||||
| 
 | ||||
|  | @ -189,9 +191,9 @@ private: | |||
| 
 | ||||
|     bool is_inspector_open() const; | ||||
|     void inspect_dom_tree(); | ||||
|     void inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element); | ||||
|     void clear_inspected_dom_node(); | ||||
|     void clear_inspector_callbacks(); | ||||
|     ErrorOr<Ladybird::DOMNodeProperties> inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element); | ||||
| 
 | ||||
|     qreal m_inverse_pixel_scaling_ratio { 1.0 }; | ||||
|     bool m_should_show_line_box_borders { false }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 MacDue
						MacDue