mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-16 06:30:41 +00:00
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. :^)
This commit is contained in:
parent
0ff07980ae
commit
7cdfb9c6b4
39
Applications/Browser/InspectorWidget.cpp
Normal file
39
Applications/Browser/InspectorWidget.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "InspectorWidget.h"
|
||||
#include <LibGUI/GBoxLayout.h>
|
||||
#include <LibGUI/GSplitter.h>
|
||||
#include <LibGUI/GTableView.h>
|
||||
#include <LibGUI/GTreeView.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
#include <LibHTML/DOMElementStyleModel.h>
|
||||
#include <LibHTML/DOMTreeModel.h>
|
||||
|
||||
InspectorWidget::InspectorWidget(GWidget* parent)
|
||||
: GWidget(parent)
|
||||
{
|
||||
set_layout(make<GBoxLayout>(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<Node*>(index.internal_data());
|
||||
node->document().set_inspected_node(node);
|
||||
if (node->is_element())
|
||||
m_style_table_view->set_model(DOMElementStyleModel::create(to<Element>(*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));
|
||||
}
|
20
Applications/Browser/InspectorWidget.h
Normal file
20
Applications/Browser/InspectorWidget.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <LibGUI/GWidget.h>
|
||||
|
||||
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<GTreeView> m_dom_tree_view;
|
||||
RefPtr<GTableView> m_style_table_view;
|
||||
RefPtr<Document> m_document;
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
OBJS = \
|
||||
main.o
|
||||
main.o \
|
||||
InspectorWidget.o
|
||||
|
||||
PROGRAM = Browser
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "History.h"
|
||||
#include "InspectorWidget.h"
|
||||
#include <LibCore/CFile.h>
|
||||
#include <LibGUI/GAboutDialog.h>
|
||||
#include <LibGUI/GAction.h>
|
||||
|
@ -9,7 +10,6 @@
|
|||
#include <LibGUI/GStatusBar.h>
|
||||
#include <LibGUI/GTextBox.h>
|
||||
#include <LibGUI/GToolBar.h>
|
||||
#include <LibGUI/GTreeView.h>
|
||||
#include <LibGUI/GWindow.h>
|
||||
#include <LibHTML/CSS/StyleResolver.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
|
@ -134,7 +134,6 @@ int main(int argc, char** argv)
|
|||
menubar->add_menu(move(app_menu));
|
||||
|
||||
RefPtr<GWindow> dom_inspector_window;
|
||||
RefPtr<GTreeView> 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<Node*>(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<InspectorWidget*>(dom_inspector_window->main_widget());
|
||||
inspector_widget->set_document(html_widget->document());
|
||||
dom_inspector_window->show();
|
||||
dom_inspector_window->move_to_front();
|
||||
}));
|
||||
|
|
50
Libraries/LibHTML/DOMElementStyleModel.cpp
Normal file
50
Libraries/LibHTML/DOMElementStyleModel.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "DOMElementStyleModel.h"
|
||||
#include <LibHTML/CSS/PropertyID.h>
|
||||
#include <LibHTML/DOM/Document.h>
|
||||
#include <LibHTML/DOM/Element.h>
|
||||
|
||||
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();
|
||||
}
|
33
Libraries/LibHTML/DOMElementStyleModel.h
Normal file
33
Libraries/LibHTML/DOMElementStyleModel.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <LibGUI/GModel.h>
|
||||
|
||||
class Element;
|
||||
|
||||
class DOMElementStyleModel final : public GModel {
|
||||
public:
|
||||
enum Column {
|
||||
PropertyName,
|
||||
PropertyValue,
|
||||
__Count
|
||||
};
|
||||
|
||||
static NonnullRefPtr<DOMElementStyleModel> 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<Element> m_element;
|
||||
|
||||
struct Value {
|
||||
String name;
|
||||
String value;
|
||||
};
|
||||
Vector<Value> m_values;
|
||||
};
|
|
@ -34,6 +34,7 @@ LIBHTML_OBJS = \
|
|||
DOM/Node.o \
|
||||
DOM/ParentNode.o \
|
||||
DOM/Text.o \
|
||||
DOMElementStyleModel.o \
|
||||
DOMTreeModel.o \
|
||||
Dump.o \
|
||||
FontCache.o \
|
||||
|
|
Loading…
Reference in a new issue