mirror of
https://github.com/RGBCube/serenity
synced 2025-05-30 22:08:12 +00:00
Ladybird: Implement the JavaScript console using a WebContentView
This aligns the Ladybird console implementation with the Browser console a bit more, which uses OutOfProcessWebView for rendering console output. This allows us to style the console output to try and match the system theme. Using a WebContentView is simpler than trying to style the old QTextEdit widget, as the console output is HTML with built-in "-libweb-palette-*" colors. These will override any color we set on the QTextEdit widget.
This commit is contained in:
parent
5af715e394
commit
4aca24481e
5 changed files with 80 additions and 22 deletions
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "BrowserWindow.h"
|
#include "BrowserWindow.h"
|
||||||
|
#include "ConsoleWidget.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "SettingsDialog.h"
|
#include "SettingsDialog.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
@ -546,8 +547,13 @@ void BrowserWindow::reset_zoom()
|
||||||
|
|
||||||
void BrowserWindow::select_all()
|
void BrowserWindow::select_all()
|
||||||
{
|
{
|
||||||
if (auto* tab = m_current_tab)
|
if (!m_current_tab)
|
||||||
tab->view().select_all();
|
return;
|
||||||
|
|
||||||
|
if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow())
|
||||||
|
console->view().select_all();
|
||||||
|
else
|
||||||
|
m_current_tab->view().select_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::update_displayed_zoom_level()
|
void BrowserWindow::update_displayed_zoom_level()
|
||||||
|
@ -560,11 +566,18 @@ void BrowserWindow::update_displayed_zoom_level()
|
||||||
|
|
||||||
void BrowserWindow::copy_selected_text()
|
void BrowserWindow::copy_selected_text()
|
||||||
{
|
{
|
||||||
if (auto* tab = m_current_tab) {
|
if (!m_current_tab)
|
||||||
auto text = tab->view().selected_text();
|
return;
|
||||||
auto* clipboard = QGuiApplication::clipboard();
|
|
||||||
clipboard->setText(qstring_from_ak_deprecated_string(text));
|
DeprecatedString text;
|
||||||
}
|
|
||||||
|
if (auto* console = m_current_tab->view().console(); console && console->isActiveWindow())
|
||||||
|
text = console->view().selected_text();
|
||||||
|
else
|
||||||
|
text = m_current_tab->view().selected_text();
|
||||||
|
|
||||||
|
auto* clipboard = QGuiApplication::clipboard();
|
||||||
|
clipboard->setText(qstring_from_ak_deprecated_string(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::resizeEvent(QResizeEvent* event)
|
void BrowserWindow::resizeEvent(QResizeEvent* event)
|
||||||
|
|
|
@ -11,25 +11,46 @@
|
||||||
|
|
||||||
#include "ConsoleWidget.h"
|
#include "ConsoleWidget.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
#include "WebContentView.h"
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibJS/MarkupGenerator.h>
|
#include <LibJS/MarkupGenerator.h>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
#include <QPalette>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
|
||||||
|
static bool is_using_dark_system_theme(QWidget& widget)
|
||||||
|
{
|
||||||
|
// FIXME: Qt does not provide any method to query if the system is using a dark theme. We will have to implement
|
||||||
|
// platform-specific methods if we wish to have better detection. For now, this inspects if Qt is using a
|
||||||
|
// dark color for widget backgrounds using Rec. 709 luma coefficients.
|
||||||
|
// https://en.wikipedia.org/wiki/Rec._709#Luma_coefficients
|
||||||
|
|
||||||
|
auto color = widget.palette().color(widget.backgroundRole());
|
||||||
|
auto luma = 0.2126f * color.redF() + 0.7152f * color.greenF() + 0.0722f * color.blueF();
|
||||||
|
|
||||||
|
return luma <= 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleWidget::ConsoleWidget()
|
ConsoleWidget::ConsoleWidget()
|
||||||
{
|
{
|
||||||
setLayout(new QVBoxLayout);
|
setLayout(new QVBoxLayout);
|
||||||
|
|
||||||
m_output_view = new QTextEdit(this);
|
m_output_view = new WebContentView({}, WebView::EnableCallgrindProfiling::No);
|
||||||
m_output_view->setReadOnly(true);
|
if (is_using_dark_system_theme(*this))
|
||||||
layout()->addWidget(m_output_view);
|
m_output_view->update_palette(WebContentView::PaletteMode::Dark);
|
||||||
|
|
||||||
if (on_request_messages)
|
m_output_view->load("data:text/html,<html></html>"sv);
|
||||||
on_request_messages(0);
|
// Wait until our output WebView is loaded, and then request any messages that occurred before we existed
|
||||||
|
m_output_view->on_load_finish = [this](auto&) {
|
||||||
|
if (on_request_messages)
|
||||||
|
on_request_messages(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
layout()->addWidget(m_output_view);
|
||||||
|
|
||||||
auto* bottom_container = new QWidget(this);
|
auto* bottom_container = new QWidget(this);
|
||||||
bottom_container->setLayout(new QHBoxLayout);
|
bottom_container->setLayout(new QHBoxLayout);
|
||||||
|
@ -139,12 +160,27 @@ void ConsoleWidget::print_source_line(StringView source)
|
||||||
|
|
||||||
void ConsoleWidget::print_html(StringView line)
|
void ConsoleWidget::print_html(StringView line)
|
||||||
{
|
{
|
||||||
m_output_view->append(QString::fromUtf8(line.characters_without_null_termination(), line.length()));
|
StringBuilder builder;
|
||||||
|
|
||||||
|
builder.append(R"~~~(
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.innerHTML = ")~~~"sv);
|
||||||
|
builder.append_escaped_for_json(line);
|
||||||
|
builder.append(R"~~~("
|
||||||
|
document.body.appendChild(p);
|
||||||
|
)~~~"sv);
|
||||||
|
|
||||||
|
// FIXME: Make it scroll to the bottom, using `window.scrollTo()` in the JS above.
|
||||||
|
// We used to call `m_output_view->scroll_to_bottom();` here, but that does not work because
|
||||||
|
// it runs synchronously, meaning it happens before the HTML is output via IPC above.
|
||||||
|
m_output_view->run_javascript(builder.string_view());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleWidget::clear_output()
|
void ConsoleWidget::clear_output()
|
||||||
{
|
{
|
||||||
m_output_view->clear();
|
m_output_view->run_javascript(R"~~~(
|
||||||
|
document.body.innerHTML = "";
|
||||||
|
)~~~"sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleWidget::reset()
|
void ConsoleWidget::reset()
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
class QTextEdit;
|
class WebContentView;
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ public:
|
||||||
void print_html(StringView);
|
void print_html(StringView);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
WebContentView& view() { return *m_output_view; }
|
||||||
|
|
||||||
Function<void(DeprecatedString const&)> on_js_input;
|
Function<void(DeprecatedString const&)> on_js_input;
|
||||||
Function<void(i32)> on_request_messages;
|
Function<void(i32)> on_request_messages;
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ private:
|
||||||
void request_console_messages();
|
void request_console_messages();
|
||||||
void clear_output();
|
void clear_output();
|
||||||
|
|
||||||
QTextEdit* m_output_view { nullptr };
|
WebContentView* m_output_view { nullptr };
|
||||||
QLineEdit* m_input { nullptr };
|
QLineEdit* m_input { nullptr };
|
||||||
|
|
||||||
i32 m_highest_notified_message_index { -1 };
|
i32 m_highest_notified_message_index { -1 };
|
||||||
|
|
|
@ -567,11 +567,12 @@ void WebContentView::hideEvent(QHideEvent* event)
|
||||||
client().async_set_system_visibility_state(false);
|
client().async_set_system_visibility_state(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget)
|
static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget, WebContentView::PaletteMode mode)
|
||||||
{
|
{
|
||||||
auto qt_palette = widget.palette();
|
auto qt_palette = widget.palette();
|
||||||
|
|
||||||
auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/Default.ini", s_serenity_resource_root)).release_value_but_fixme_should_propagate_errors();
|
auto theme_file = mode == WebContentView::PaletteMode::Default ? "Default"sv : "Dark"sv;
|
||||||
|
auto theme = Gfx::load_system_theme(DeprecatedString::formatted("{}/res/themes/{}.ini", s_serenity_resource_root, theme_file)).release_value_but_fixme_should_propagate_errors();
|
||||||
auto palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme);
|
auto palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme);
|
||||||
auto palette = Gfx::Palette(move(palette_impl));
|
auto palette = Gfx::Palette(move(palette_impl));
|
||||||
|
|
||||||
|
@ -594,9 +595,9 @@ static Core::AnonymousBuffer make_system_theme_from_qt_palette(QWidget& widget)
|
||||||
return theme;
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::update_palette()
|
void WebContentView::update_palette(PaletteMode mode)
|
||||||
{
|
{
|
||||||
client().async_update_system_theme(make_system_theme_from_qt_palette(*this));
|
client().async_update_system_theme(make_system_theme_from_qt_palette(*this, mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentView::create_client(WebView::EnableCallgrindProfiling enable_callgrind_profiling)
|
void WebContentView::create_client(WebView::EnableCallgrindProfiling enable_callgrind_profiling)
|
||||||
|
|
|
@ -98,6 +98,8 @@ public:
|
||||||
void show_js_console();
|
void show_js_console();
|
||||||
void show_inspector();
|
void show_inspector();
|
||||||
|
|
||||||
|
Ladybird::ConsoleWidget* console() { return m_console_widget; };
|
||||||
|
|
||||||
ErrorOr<String> dump_layout_tree();
|
ErrorOr<String> dump_layout_tree();
|
||||||
|
|
||||||
void set_viewport_rect(Gfx::IntRect);
|
void set_viewport_rect(Gfx::IntRect);
|
||||||
|
@ -107,6 +109,12 @@ public:
|
||||||
Gfx::IntPoint to_content(Gfx::IntPoint) const;
|
Gfx::IntPoint to_content(Gfx::IntPoint) const;
|
||||||
Gfx::IntPoint to_widget(Gfx::IntPoint) const;
|
Gfx::IntPoint to_widget(Gfx::IntPoint) const;
|
||||||
|
|
||||||
|
enum class PaletteMode {
|
||||||
|
Default,
|
||||||
|
Dark,
|
||||||
|
};
|
||||||
|
void update_palette(PaletteMode = PaletteMode::Default);
|
||||||
|
|
||||||
virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) override;
|
virtual void notify_server_did_layout(Badge<WebContentClient>, Gfx::IntSize content_size) override;
|
||||||
virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id) override;
|
virtual void notify_server_did_paint(Badge<WebContentClient>, i32 bitmap_id) override;
|
||||||
virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) override;
|
virtual void notify_server_did_invalidate_content_rect(Badge<WebContentClient>, Gfx::IntRect const&) override;
|
||||||
|
@ -198,8 +206,6 @@ private:
|
||||||
bool is_inspector_open() const;
|
bool is_inspector_open() const;
|
||||||
void close_sub_widgets();
|
void close_sub_widgets();
|
||||||
|
|
||||||
void update_palette();
|
|
||||||
|
|
||||||
qreal m_inverse_pixel_scaling_ratio { 1.0 };
|
qreal m_inverse_pixel_scaling_ratio { 1.0 };
|
||||||
bool m_should_show_line_box_borders { false };
|
bool m_should_show_line_box_borders { false };
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue