Ladybird/Qt: Properly listen for DPI changes on Wayland

Some Wayland compositors have support of fractional-scale-v1 protocol.
The protocol allows compositor to announce a preferred fractional scale
on a per-wl_surface basis. Qt forwards these Wayland events to an
application using a usual DevicePixelRatioChange event. However, in
contrast to the other platforms, this DevicePixelRatioChange event is
issued directly on widgets and not screens. Additionally, the exact
fractional scale is stored in QWindow object and not the current screen.

Note that in theory it is possible to obtain per-screen fractional
scaling on Wayland by interpolating data provided by wl_output and
xdg_output events but qtwayland does not do that.

If fractional-scale-v1 is not available, qtwayland will still fire
per-Widget DevicePixelRatioChange events, but, obviously, with the
per-screen possibly larger ceiled scaling.

This whole thing makes handling DPI changes on Wayland really simple.
All we need to do is to intercept DevicePixelRatioChange events firing
on QWindow objects and call the old device_pixel_ratio_changed handler
with the window's devicePixelRatio(). The only caveat here is not forget
to always set QWidget's parent before calling devicePixelRatio() on it.
This commit is contained in:
Dan Klishch 2024-01-26 23:14:41 -05:00 committed by Andrew Kaster
parent bf0b114bde
commit 38164411f0
Notes: sideshowbarker 2024-07-17 18:23:22 +09:00
7 changed files with 59 additions and 27 deletions

View file

@ -57,20 +57,22 @@ BrowserWindow::BrowserWindow(Vector<URL> const& initial_urls, WebView::CookieJar
m_tabs_container->setTabBarAutoHide(true);
// Listen for DPI changes
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_DontCreateNativeAncestors);
m_device_pixel_ratio = devicePixelRatio();
m_current_screen = screen();
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &BrowserWindow::device_pixel_ratio_changed);
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;
if (QT_VERSION < QT_VERSION_CHECK(6, 6, 0) || QGuiApplication::platformName() != "wayland") {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_DontCreateNativeAncestors);
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &BrowserWindow::device_pixel_ratio_changed);
});
QObject::connect(windowHandle(), &QWindow::screenChanged, this, [this](QScreen* screen) {
if (m_device_pixel_ratio != devicePixelRatio())
device_pixel_ratio_changed(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, &BrowserWindow::device_pixel_ratio_changed);
});
}
auto* menu = menuBar()->addMenu("&File");
@ -737,6 +739,18 @@ void BrowserWindow::copy_selected_text()
clipboard->setText(qstring_from_ak_string(text));
}
bool BrowserWindow::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 QMainWindow::event(event);
}
void BrowserWindow::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);

View file

@ -96,6 +96,7 @@ protected:
bool eventFilter(QObject* obj, QEvent* event) override;
private:
virtual bool event(QEvent*) override;
virtual void resizeEvent(QResizeEvent*) override;
virtual void moveEvent(QMoveEvent*) override;
virtual void wheelEvent(QWheelEvent*) override;

View file

@ -10,6 +10,7 @@
#include <LibWebView/InspectorClient.h>
#include <QAction>
#include <QCloseEvent>
#include <QGuiApplication>
#include <QMenu>
#include <QVBoxLayout>
#include <QWindow>
@ -21,7 +22,7 @@ extern bool is_using_dark_system_theme(QWidget&);
InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
: QWidget(tab, Qt::Window)
{
m_inspector_view = new WebContentView({}, {});
m_inspector_view = new WebContentView(this, {}, {});
if (is_using_dark_system_theme(*this))
m_inspector_view->update_palette(WebContentView::PaletteMode::Dark);
@ -126,20 +127,22 @@ InspectorWidget::InspectorWidget(QWidget* tab, WebContentView& content_view)
resize(875, 825);
// Listen for DPI changes
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_DontCreateNativeAncestors);
m_device_pixel_ratio = devicePixelRatio();
m_current_screen = screen();
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &InspectorWidget::device_pixel_ratio_changed);
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;
if (QT_VERSION < QT_VERSION_CHECK(6, 6, 0) || QGuiApplication::platformName() != "wayland") {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_DontCreateNativeAncestors);
QObject::connect(m_current_screen, &QScreen::logicalDotsPerInchChanged, this, &InspectorWidget::device_pixel_ratio_changed);
});
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);
});
}
}
InspectorWidget::~InspectorWidget() = default;
@ -170,6 +173,18 @@ void InspectorWidget::device_pixel_ratio_changed(qreal dpi)
m_inspector_view->set_device_pixel_ratio(m_device_pixel_ratio);
}
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);
}
void InspectorWidget::closeEvent(QCloseEvent* event)
{
event->accept();

View file

@ -35,6 +35,7 @@ public slots:
void device_pixel_ratio_changed(qreal dpi);
private:
bool event(QEvent*) override;
void closeEvent(QCloseEvent*) override;
QPoint to_widget_position(Gfx::IntPoint) const;

View file

@ -61,7 +61,7 @@ Tab::Tab(BrowserWindow* window, WebContentOptions const& web_content_options, St
m_layout->setSpacing(0);
m_layout->setContentsMargins(0, 0, 0, 0);
m_view = new WebContentView(web_content_options, webdriver_content_ipc_path, parent_client, page_index);
m_view = new WebContentView(this, web_content_options, webdriver_content_ipc_path, parent_client, page_index);
m_toolbar = new QToolBar(this);
m_location_edit = new LocationEdit(this);

View file

@ -54,8 +54,9 @@ namespace Ladybird {
bool is_using_dark_system_theme(QWidget&);
WebContentView::WebContentView(WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
: m_web_content_options(web_content_options)
WebContentView::WebContentView(QWidget* window, WebContentOptions const& web_content_options, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client, size_t page_index)
: QAbstractScrollArea(window)
, m_web_content_options(web_content_options)
, m_webdriver_content_ipc_path(webdriver_content_ipc_path)
{
m_client_state.client = parent_client;

View file

@ -42,7 +42,7 @@ class WebContentView final
, public WebView::ViewImplementation {
Q_OBJECT
public:
WebContentView(WebContentOptions const&, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
WebContentView(QWidget* window, WebContentOptions const&, StringView webdriver_content_ipc_path, RefPtr<WebView::WebContentClient> parent_client = nullptr, size_t page_index = 0);
virtual ~WebContentView() override;
Function<String(const AK::URL&, Web::HTML::ActivateTab)> on_tab_open_request;