2022-12-18 00:50:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
2022-12-18 21:12:14 +00:00
|
|
|
#include "InspectorWidget.h"
|
2023-12-05 21:58:26 +00:00
|
|
|
#include <Ladybird/Qt/StringUtils.h>
|
2023-12-06 14:34:53 +00:00
|
|
|
#include <LibWebView/Attribute.h>
|
2023-11-24 01:04:54 +00:00
|
|
|
#include <LibWebView/InspectorClient.h>
|
2023-12-05 21:58:26 +00:00
|
|
|
#include <QAction>
|
2022-12-18 00:50:53 +00:00
|
|
|
#include <QCloseEvent>
|
2024-01-27 04:14:41 +00:00
|
|
|
#include <QGuiApplication>
|
2023-12-05 21:58:26 +00:00
|
|
|
#include <QMenu>
|
2022-12-18 00:50:53 +00:00
|
|
|
#include <QVBoxLayout>
|
2023-12-13 18:10:05 +00:00
|
|
|
#include <QWindow>
|
2022-12-18 00:50:53 +00:00
|
|
|
|
|
|
|
namespace Ladybird {
|
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
extern bool is_using_dark_system_theme(QWidget&);
|
|
|
|
|
2023-12-04 16:03:40 +00:00
|
|
|
InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
|
|
|
|
: QWidget(tab, Qt::Window)
|
2022-12-18 00:50:53 +00:00
|
|
|
{
|
2024-07-23 17:05:50 +00:00
|
|
|
m_inspector_view = new WebContentView(this, content_view.web_content_options(), {});
|
2023-02-21 19:49:00 +00:00
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
if (is_using_dark_system_theme(*this))
|
|
|
|
m_inspector_view->update_palette(WebContentView::PaletteMode::Dark);
|
|
|
|
|
|
|
|
m_inspector_client = make<WebView::InspectorClient>(content_view, *m_inspector_view);
|
|
|
|
|
2023-12-05 21:58:26 +00:00
|
|
|
m_edit_node_action = new QAction("&Edit node", this);
|
|
|
|
connect(m_edit_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_edit_dom_node(); });
|
|
|
|
|
2023-12-06 16:16:28 +00:00
|
|
|
m_copy_node_action = new QAction("&Copy HTML", this);
|
|
|
|
connect(m_copy_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_copy_dom_node(); });
|
|
|
|
|
2023-12-06 16:56:16 +00:00
|
|
|
m_screenshot_node_action = new QAction("Take node &screenshot", this);
|
|
|
|
connect(m_screenshot_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_screenshot_dom_node(); });
|
|
|
|
|
2023-12-07 16:05:34 +00:00
|
|
|
m_create_child_element_action = new QAction("Create child &element", this);
|
|
|
|
connect(m_create_child_element_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_create_child_element(); });
|
|
|
|
|
|
|
|
m_create_child_text_node_action = new QAction("Create child &text node", this);
|
|
|
|
connect(m_create_child_text_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_create_child_text_node(); });
|
|
|
|
|
2023-12-07 16:13:15 +00:00
|
|
|
m_clone_node_action = new QAction("C&lone node", this);
|
|
|
|
connect(m_clone_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_clone_dom_node(); });
|
|
|
|
|
2023-12-05 21:58:26 +00:00
|
|
|
m_delete_node_action = new QAction("&Delete node", this);
|
|
|
|
connect(m_delete_node_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_remove_dom_node(); });
|
|
|
|
|
|
|
|
m_add_attribute_action = new QAction("&Add attribute", this);
|
|
|
|
connect(m_add_attribute_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_add_dom_node_attribute(); });
|
|
|
|
|
|
|
|
m_remove_attribute_action = new QAction("&Remove attribute", this);
|
|
|
|
connect(m_remove_attribute_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_remove_dom_node_attribute(); });
|
|
|
|
|
2023-12-06 15:12:31 +00:00
|
|
|
m_copy_attribute_value_action = new QAction("Copy attribute &value", this);
|
|
|
|
connect(m_copy_attribute_value_action, &QAction::triggered, [this]() { m_inspector_client->context_menu_copy_dom_node_attribute_value(); });
|
|
|
|
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_text_context_menu = new QMenu("DOM text context menu", this);
|
|
|
|
m_dom_node_text_context_menu->addAction(m_edit_node_action);
|
2023-12-06 16:16:28 +00:00
|
|
|
m_dom_node_text_context_menu->addAction(m_copy_node_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_text_context_menu->addSeparator();
|
|
|
|
m_dom_node_text_context_menu->addAction(m_delete_node_action);
|
|
|
|
|
2023-12-07 16:05:34 +00:00
|
|
|
auto* create_child_menu = new QMenu("Create child", this);
|
|
|
|
create_child_menu->addAction(m_create_child_element_action);
|
|
|
|
create_child_menu->addAction(m_create_child_text_node_action);
|
|
|
|
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_tag_context_menu = new QMenu("DOM tag context menu", this);
|
|
|
|
m_dom_node_tag_context_menu->addAction(m_edit_node_action);
|
|
|
|
m_dom_node_tag_context_menu->addSeparator();
|
|
|
|
m_dom_node_tag_context_menu->addAction(m_add_attribute_action);
|
2023-12-07 16:05:34 +00:00
|
|
|
m_dom_node_tag_context_menu->addMenu(create_child_menu);
|
2023-12-07 16:13:15 +00:00
|
|
|
m_dom_node_tag_context_menu->addAction(m_clone_node_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_tag_context_menu->addAction(m_delete_node_action);
|
2023-12-06 16:16:28 +00:00
|
|
|
m_dom_node_tag_context_menu->addSeparator();
|
|
|
|
m_dom_node_tag_context_menu->addAction(m_copy_node_action);
|
2023-12-06 16:56:16 +00:00
|
|
|
m_dom_node_tag_context_menu->addAction(m_screenshot_node_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
|
|
|
|
m_dom_node_attribute_context_menu = new QMenu("DOM attribute context menu", this);
|
|
|
|
m_dom_node_attribute_context_menu->addAction(m_edit_node_action);
|
2023-12-06 15:12:31 +00:00
|
|
|
m_dom_node_attribute_context_menu->addAction(m_copy_attribute_value_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_attribute_context_menu->addAction(m_remove_attribute_action);
|
|
|
|
m_dom_node_attribute_context_menu->addSeparator();
|
|
|
|
m_dom_node_attribute_context_menu->addAction(m_add_attribute_action);
|
2023-12-07 16:05:34 +00:00
|
|
|
m_dom_node_attribute_context_menu->addMenu(create_child_menu);
|
2023-12-07 16:13:15 +00:00
|
|
|
m_dom_node_attribute_context_menu->addAction(m_clone_node_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
m_dom_node_attribute_context_menu->addAction(m_delete_node_action);
|
2023-12-06 16:16:28 +00:00
|
|
|
m_dom_node_attribute_context_menu->addSeparator();
|
|
|
|
m_dom_node_attribute_context_menu->addAction(m_copy_node_action);
|
2023-12-06 16:56:16 +00:00
|
|
|
m_dom_node_attribute_context_menu->addAction(m_screenshot_node_action);
|
2023-12-05 21:58:26 +00:00
|
|
|
|
|
|
|
m_inspector_client->on_requested_dom_node_text_context_menu = [this](auto position) {
|
|
|
|
m_edit_node_action->setText("&Edit text");
|
2023-12-06 16:16:28 +00:00
|
|
|
m_copy_node_action->setText("&Copy text");
|
2023-12-05 21:58:26 +00:00
|
|
|
|
2024-03-11 02:17:35 +00:00
|
|
|
m_dom_node_text_context_menu->exec(m_inspector_view->map_point_to_global_position(position));
|
2023-12-05 21:58:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
m_inspector_client->on_requested_dom_node_tag_context_menu = [this](auto position, auto const& tag) {
|
|
|
|
m_edit_node_action->setText(qstring_from_ak_string(MUST(String::formatted("&Edit \"{}\"", tag))));
|
2023-12-06 16:16:28 +00:00
|
|
|
m_copy_node_action->setText("&Copy HTML");
|
2023-12-05 21:58:26 +00:00
|
|
|
|
2024-03-11 02:17:35 +00:00
|
|
|
m_dom_node_tag_context_menu->exec(m_inspector_view->map_point_to_global_position(position));
|
2023-12-05 21:58:26 +00:00
|
|
|
};
|
|
|
|
|
2023-12-06 15:12:31 +00:00
|
|
|
m_inspector_client->on_requested_dom_node_attribute_context_menu = [this](auto position, auto const&, WebView::Attribute const& attribute) {
|
|
|
|
static constexpr size_t MAX_ATTRIBUTE_VALUE_LENGTH = 32;
|
|
|
|
|
2023-12-06 16:16:28 +00:00
|
|
|
m_copy_node_action->setText("&Copy HTML");
|
2023-12-06 14:34:53 +00:00
|
|
|
m_edit_node_action->setText(qstring_from_ak_string(MUST(String::formatted("&Edit attribute \"{}\"", attribute.name))));
|
|
|
|
m_remove_attribute_action->setText(qstring_from_ak_string(MUST(String::formatted("&Remove attribute \"{}\"", attribute.name))));
|
2023-12-06 15:12:31 +00:00
|
|
|
m_copy_attribute_value_action->setText(qstring_from_ak_string(MUST(String::formatted("Copy attribute &value \"{:.{}}{}\"",
|
|
|
|
attribute.value, MAX_ATTRIBUTE_VALUE_LENGTH,
|
|
|
|
attribute.value.bytes_as_string_view().length() > MAX_ATTRIBUTE_VALUE_LENGTH ? "..."sv : ""sv))));
|
2023-12-05 21:58:26 +00:00
|
|
|
|
2024-03-11 02:17:35 +00:00
|
|
|
m_dom_node_attribute_context_menu->exec(m_inspector_view->map_point_to_global_position(position));
|
2023-12-05 21:58:26 +00:00
|
|
|
};
|
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
setLayout(new QVBoxLayout);
|
2023-12-04 14:39:22 +00:00
|
|
|
layout()->addWidget(m_inspector_view);
|
2023-11-24 01:04:54 +00:00
|
|
|
|
|
|
|
setWindowTitle("Inspector");
|
|
|
|
resize(875, 825);
|
2023-12-13 18:10:05 +00:00
|
|
|
|
|
|
|
// Listen for DPI changes
|
|
|
|
m_device_pixel_ratio = devicePixelRatio();
|
|
|
|
m_current_screen = screen();
|
2024-01-27 04:14:41 +00:00
|
|
|
if (QT_VERSION < QT_VERSION_CHECK(6, 6, 0) || QGuiApplication::platformName() != "wayland") {
|
|
|
|
setAttribute(Qt::WA_NativeWindow);
|
|
|
|
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
2023-12-13 18:10:05 +00:00
|
|
|
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &InspectorWidget::device_pixel_ratio_changed);
|
2024-01-27 04:14:41 +00:00
|
|
|
QObject::connect(windowHandle(), &QWindow::screenChanged, this, [this](QScreen* screen) {
|
|
|
|
if (m_device_pixel_ratio != screen->devicePixelRatio())
|
|
|
|
device_pixel_ratio_changed(screen->devicePixelRatio());
|
|
|
|
|
|
|
|
// Listen for logicalDotsPerInchChanged signals on new screen
|
|
|
|
QObject::disconnect(m_current_screen, &QScreen::logicalDotsPerInchChanged, nullptr, nullptr);
|
|
|
|
m_current_screen = screen;
|
|
|
|
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &InspectorWidget::device_pixel_ratio_changed);
|
|
|
|
});
|
|
|
|
}
|
2022-12-18 21:12:14 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
InspectorWidget::~InspectorWidget() = default;
|
|
|
|
|
|
|
|
void InspectorWidget::inspect()
|
2022-12-18 21:12:14 +00:00
|
|
|
{
|
2023-11-24 01:04:54 +00:00
|
|
|
m_inspector_client->inspect();
|
2022-12-18 00:50:53 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
void InspectorWidget::reset()
|
2023-02-21 19:49:00 +00:00
|
|
|
{
|
2023-11-24 01:04:54 +00:00
|
|
|
m_inspector_client->reset();
|
2023-02-21 19:49:00 +00:00
|
|
|
}
|
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
void InspectorWidget::select_hovered_node()
|
2022-12-18 00:50:53 +00:00
|
|
|
{
|
2023-11-24 01:04:54 +00:00
|
|
|
m_inspector_client->select_hovered_node();
|
|
|
|
}
|
2023-11-04 15:55:09 +00:00
|
|
|
|
2023-11-24 01:04:54 +00:00
|
|
|
void InspectorWidget::select_default_node()
|
|
|
|
{
|
|
|
|
m_inspector_client->select_default_node();
|
2022-12-18 00:50:53 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 18:10:05 +00:00
|
|
|
void InspectorWidget::device_pixel_ratio_changed(qreal dpi)
|
|
|
|
{
|
|
|
|
m_device_pixel_ratio = dpi;
|
|
|
|
m_inspector_view->set_device_pixel_ratio(m_device_pixel_ratio);
|
|
|
|
}
|
|
|
|
|
2024-01-27 04:14:41 +00:00
|
|
|
bool InspectorWidget::event(QEvent* event)
|
|
|
|
{
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
|
|
|
|
if (event->type() == QEvent::DevicePixelRatioChange) {
|
|
|
|
if (m_device_pixel_ratio != devicePixelRatio())
|
|
|
|
device_pixel_ratio_changed(devicePixelRatio());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return QWidget::event(event);
|
|
|
|
}
|
|
|
|
|
2022-12-18 00:50:53 +00:00
|
|
|
void InspectorWidget::closeEvent(QCloseEvent* event)
|
|
|
|
{
|
|
|
|
event->accept();
|
2023-11-24 01:04:54 +00:00
|
|
|
m_inspector_client->clear_selection();
|
2022-12-18 00:50:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|