From 64b48f689924aed490d231e939ef76ed3d2be3f6 Mon Sep 17 00:00:00 2001 From: David Rosca Date: Mon, 26 Dec 2016 12:38:51 +0100 Subject: [PATCH] Use native scrollbars for main scrollbars Use native widgets to render main scrollbars (scrollbars of the main webview widget, not iframes etc). It makes the scrollbars on body element invisible and instead draws native widgets on the space for scrollbars. Comes with possibility to disable it, enabled by default. --- src/lib/lib.pro | 4 + src/lib/preferences/preferences.cpp | 5 + src/lib/preferences/preferences.ui | 11 +- src/lib/webengine/webpage.cpp | 5 + src/lib/webengine/webpage.h | 1 + src/lib/webengine/webscrollbar.cpp | 89 +++++++++++ src/lib/webengine/webscrollbar.h | 46 ++++++ src/lib/webengine/webscrollbarmanager.cpp | 186 ++++++++++++++++++++++ src/lib/webengine/webscrollbarmanager.h | 50 ++++++ src/lib/webengine/webview.cpp | 23 ++- src/lib/webengine/webview.h | 1 + 11 files changed, 417 insertions(+), 4 deletions(-) create mode 100644 src/lib/webengine/webscrollbar.cpp create mode 100644 src/lib/webengine/webscrollbar.h create mode 100644 src/lib/webengine/webscrollbarmanager.cpp create mode 100644 src/lib/webengine/webscrollbarmanager.h diff --git a/src/lib/lib.pro b/src/lib/lib.pro index 7f1a7f6db..922199993 100644 --- a/src/lib/lib.pro +++ b/src/lib/lib.pro @@ -215,6 +215,8 @@ SOURCES += \ webengine/webinspector.cpp \ webengine/webpage.cpp \ webengine/webview.cpp \ + webengine/webscrollbar.cpp \ + webengine/webscrollbarmanager.cpp \ webtab/searchtoolbar.cpp \ webtab/tabbedwebview.cpp \ webtab/webtab.cpp \ @@ -397,6 +399,8 @@ HEADERS += \ webengine/webinspector.h \ webengine/webpage.h \ webengine/webview.h \ + webengine/webscrollbar.h \ + webengine/webscrollbarmanager.h \ webtab/searchtoolbar.h \ webtab/tabbedwebview.h \ webtab/webtab.h \ diff --git a/src/lib/preferences/preferences.cpp b/src/lib/preferences/preferences.cpp index 41e928cf2..b96e75e6d 100644 --- a/src/lib/preferences/preferences.cpp +++ b/src/lib/preferences/preferences.cpp @@ -48,6 +48,7 @@ #include "profilemanager.h" #include "html5permissions/html5permissionsdialog.h" #include "searchenginesdialog.h" +#include "webscrollbarmanager.h" #include #include @@ -277,6 +278,7 @@ Preferences::Preferences(BrowserWindow* window) ui->animateScrolling->setChecked(settings.value("AnimateScrolling", true).toBool()); ui->wheelScroll->setValue(settings.value("wheelScrollLines", qApp->wheelScrollLines()).toInt()); ui->xssAuditing->setChecked(settings.value("XSSAuditing", false).toBool()); + ui->useNativeScrollbars->setChecked(settings.value("UseNativeScrollbars", true).toBool()); foreach (int level, WebView::zoomLevels()) { ui->defaultZoomLevel->addItem(QString("%1%").arg(level)); @@ -973,6 +975,7 @@ void Preferences::saveSettings() settings.setValue("DefaultZoomLevel", ui->defaultZoomLevel->currentIndex()); settings.setValue("XSSAuditing", ui->xssAuditing->isChecked()); settings.setValue("closeAppWithCtrlQ", ui->closeAppWithCtrlQ->isChecked()); + settings.setValue("UseNativeScrollbars", ui->useNativeScrollbars->isChecked()); #ifdef Q_OS_WIN settings.setValue("CheckDefaultBrowser", ui->checkDefaultBrowser->isChecked()); #endif @@ -1070,6 +1073,8 @@ void Preferences::saveSettings() mApp->desktopNotifications()->loadSettings(); mApp->autoFill()->loadSettings(); mApp->networkManager()->loadSettings(); + + WebScrollBarManager::instance()->loadSettings(); } Preferences::~Preferences() diff --git a/src/lib/preferences/preferences.ui b/src/lib/preferences/preferences.ui index 6c3adb3dc..7ae08c4b2 100644 --- a/src/lib/preferences/preferences.ui +++ b/src/lib/preferences/preferences.ui @@ -1117,6 +1117,13 @@ + + + + Use native scrollbars + + + @@ -2374,8 +2381,8 @@ 0 0 - 560 - 70 + 96 + 28 diff --git a/src/lib/webengine/webpage.cpp b/src/lib/webengine/webpage.cpp index 4c817292c..3b94e9c06 100644 --- a/src/lib/webengine/webpage.cpp +++ b/src/lib/webengine/webpage.cpp @@ -161,6 +161,11 @@ void WebPage::scroll(int x, int y) runJavaScript(QSL("window.scrollTo(window.scrollX + %1, window.scrollY + %2)").arg(x).arg(y), WebPage::SafeJsWorld); } +void WebPage::setScrollPosition(const QPointF &pos) +{ + runJavaScript(QSL("window.scrollTo(%1, %2)").arg(pos.x()).arg(pos.y()), WebPage::SafeJsWorld); +} + void WebPage::scheduleAdjustPage() { if (view()->isLoading()) { diff --git a/src/lib/webengine/webpage.h b/src/lib/webengine/webpage.h index 0755610fa..c7aadf3b5 100644 --- a/src/lib/webengine/webpage.h +++ b/src/lib/webengine/webpage.h @@ -59,6 +59,7 @@ public: WebHitTestResult hitTestContent(const QPoint &pos) const; void scroll(int x, int y); + void setScrollPosition(const QPointF &pos); bool javaScriptPrompt(const QUrl &securityOrigin, const QString &msg, const QString &defaultValue, QString* result) Q_DECL_OVERRIDE; bool javaScriptConfirm(const QUrl &securityOrigin, const QString &msg) Q_DECL_OVERRIDE; diff --git a/src/lib/webengine/webscrollbar.cpp b/src/lib/webengine/webscrollbar.cpp new file mode 100644 index 000000000..426db2aa8 --- /dev/null +++ b/src/lib/webengine/webscrollbar.cpp @@ -0,0 +1,89 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2016 David Rosca +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ + +#include "webscrollbar.h" +#include "webview.h" +#include "webpage.h" + +#include + +WebScrollBar::WebScrollBar(Qt::Orientation orientation, WebView *view) + : QScrollBar(orientation) + , m_view(view) +{ + setFocusProxy(m_view); + resize(sizeHint()); + + connect(this, &QScrollBar::valueChanged, this, &WebScrollBar::performScroll); + connect(view, &WebView::focusChanged, this, [this]() { update(); }); +} + +int WebScrollBar::thickness() const +{ + return orientation() == Qt::Vertical ? width() : height(); +} + +void WebScrollBar::updateValues(const QSize &viewport) +{ + setMinimum(0); + setParent(m_view->overlayWidget()); + + m_blockScrolling = true; + + if (orientation() == Qt::Vertical) { + setFixedHeight(viewport.height()); + move(m_view->width() - width(), 0); + setPageStep(viewport.height()); + setMaximum(std::max(0, m_view->page()->contentsSize().toSize().height() - viewport.height())); + setValue(m_view->page()->scrollPosition().toPoint().y()); + } else { + setFixedWidth(viewport.width()); + move(0, m_view->height() - height()); + setPageStep(viewport.width()); + setMaximum(std::max(0, m_view->page()->contentsSize().toSize().width() - viewport.width())); + setValue(m_view->page()->scrollPosition().toPoint().x()); + } + + m_blockScrolling = false; + + setVisible(maximum() > minimum()); +} + +void WebScrollBar::performScroll() +{ + if (m_blockScrolling) { + return; + } + + QPointF pos = m_view->page()->scrollPosition(); + + if (orientation() == Qt::Vertical) { + pos.setY(value()); + } else { + pos.setX(value()); + } + + m_view->page()->setScrollPosition(pos); +} + +void WebScrollBar::paintEvent(QPaintEvent *ev) +{ + QPainter painter(this); + painter.fillRect(ev->rect(), m_view->page()->backgroundColor()); + QScrollBar::paintEvent(ev); +} diff --git a/src/lib/webengine/webscrollbar.h b/src/lib/webengine/webscrollbar.h new file mode 100644 index 000000000..d18b0c020 --- /dev/null +++ b/src/lib/webengine/webscrollbar.h @@ -0,0 +1,46 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2016 David Rosca +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ + +#ifndef WEBSCROLLBAR_H +#define WEBSCROLLBAR_H + +#include + +#include "qzcommon.h" + +class WebView; + +class WebScrollBar : public QScrollBar +{ + Q_OBJECT + +public: + explicit WebScrollBar(Qt::Orientation orientation, WebView *view); + + int thickness() const; + void updateValues(const QSize &viewport); + +private: + void performScroll(); + void paintEvent(QPaintEvent *ev) override; + + WebView *m_view; + bool m_blockScrolling = false; +}; + +#endif // WEBSCROLLBAR_H diff --git a/src/lib/webengine/webscrollbarmanager.cpp b/src/lib/webengine/webscrollbarmanager.cpp new file mode 100644 index 000000000..a63c0b9cb --- /dev/null +++ b/src/lib/webengine/webscrollbarmanager.cpp @@ -0,0 +1,186 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2016 David Rosca +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ + +#include "webscrollbarmanager.h" +#include "webscrollbar.h" +#include "webview.h" +#include "webpage.h" +#include "mainapplication.h" +#include "scripts.h" +#include "settings.h" + +#include +#include +#include + +Q_GLOBAL_STATIC(WebScrollBarManager, qz_web_scrollbar_manager) + +class WebScrollBarCornerWidget : QWidget +{ +public: + explicit WebScrollBarCornerWidget(WebView *view) + : QWidget() + , m_view(view) + { + } + + void updateVisibility(bool visible, int thickness) + { + if (visible) { + setParent(m_view->overlayWidget()); + resize(thickness, thickness); + move(m_view->width() - width(), m_view->height() - height()); + show(); + } else { + hide(); + } + } + +private: + void paintEvent(QPaintEvent *ev) override + { + QPainter painter(this); + painter.fillRect(ev->rect(), m_view->page()->backgroundColor()); + QWidget::paintEvent(ev); + } + + WebView *m_view; +}; + +struct ScrollBarData { + ~ScrollBarData() { + delete vscrollbar; + delete hscrollbar; + delete corner; + } + + WebScrollBar *vscrollbar; + WebScrollBar *hscrollbar; + WebScrollBarCornerWidget *corner; +}; + +WebScrollBarManager::WebScrollBarManager(QObject *parent) + : QObject(parent) +{ + loadSettings(); +} + +void WebScrollBarManager::loadSettings() +{ + m_enabled = Settings().value(QSL("Web-Browser-Settings/UseNativeScrollbars"), true).toBool(); + + if (!m_enabled) { + for (WebView *view : m_scrollbars.keys()) { + removeWebView(view); + } + } +} + +void WebScrollBarManager::addWebView(WebView *view) +{ + if (!m_enabled) { + return; + } + + delete m_scrollbars.value(view); + + ScrollBarData *data = new ScrollBarData; + data->vscrollbar = new WebScrollBar(Qt::Vertical, view); + data->hscrollbar = new WebScrollBar(Qt::Horizontal, view); + data->corner = new WebScrollBarCornerWidget(view); + m_scrollbars[view] = data; + + auto updateValues = [=]() { + const int thickness = data->vscrollbar->thickness(); + const QSize viewport = viewportSize(view, thickness); + data->vscrollbar->updateValues(viewport); + data->hscrollbar->updateValues(viewport); + data->corner->updateVisibility(data->vscrollbar->isVisible() && data->hscrollbar->isVisible(), thickness); + }; + + connect(view, &WebView::viewportResized, this, updateValues); + connect(view->page(), &WebPage::contentsSizeChanged, this, updateValues); + connect(view->page(), &WebPage::scrollPositionChanged, this, updateValues); + + if (m_scrollbars.size() == 1) { + createUserScript(); + } +} + +void WebScrollBarManager::removeWebView(WebView *view) +{ + if (m_scrollbars.size() == 1) { + removeUserScript(); + } + + disconnect(view, 0, this, 0); + disconnect(view->page(), 0, this, 0); + + delete m_scrollbars.take(view); +} + +WebScrollBarManager *WebScrollBarManager::instance() +{ + return qz_web_scrollbar_manager(); +} + +void WebScrollBarManager::createUserScript() +{ + Q_ASSERT(!m_scrollbars.isEmpty()); + + const int thickness = (*m_scrollbars.begin())->vscrollbar->thickness(); + + QWebEngineScript script; + script.setName(QSL("_qupzilla_scrollbar")); + script.setInjectionPoint(QWebEngineScript::DocumentReady); + script.setWorldId(WebPage::SafeJsWorld); + script.setSourceCode(Scripts::setCss(QSL("body::-webkit-scrollbar{width:%1px;height:%1px;}").arg(thickness))); + mApp->webProfile()->scripts()->insert(script); +} + +void WebScrollBarManager::removeUserScript() +{ + QWebEngineScript script = mApp->webProfile()->scripts()->findScript(QSL("_qupzilla_scrollbar")); + mApp->webProfile()->scripts()->remove(script); +} + +QSize WebScrollBarManager::viewportSize(WebView *view, int thickness) const +{ + QSize viewport = view->size(); + const QSize content = view->page()->contentsSize().toSize(); + + // Check both axis + if (content.width() - viewport.width() > 0) { + viewport.setHeight(viewport.height() - thickness); + } + + if (content.height() - viewport.height() > 0) { + viewport.setWidth(viewport.width() - thickness); + } + + // Check again against adjusted size + if (viewport.height() == view->height() && content.width() - viewport.width() > 0) { + viewport.setHeight(viewport.height() - thickness); + } + + if (viewport.width() == view->width() && content.height() - viewport.height() > 0) { + viewport.setWidth(viewport.width() - thickness); + } + + return viewport; +} diff --git a/src/lib/webengine/webscrollbarmanager.h b/src/lib/webengine/webscrollbarmanager.h new file mode 100644 index 000000000..9506e2a07 --- /dev/null +++ b/src/lib/webengine/webscrollbarmanager.h @@ -0,0 +1,50 @@ +/* ============================================================ +* QupZilla - Qt web browser +* Copyright (C) 2016 David Rosca +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ + +#ifndef WEBSCROLLBARMANAGER_H +#define WEBSCROLLBARMANAGER_H + +#include +#include + +class WebView; + +class WebScrollBarManager : public QObject +{ + Q_OBJECT + +public: + explicit WebScrollBarManager(QObject *parent = 0); + + void loadSettings(); + + void addWebView(WebView *view); + void removeWebView(WebView *view); + + static WebScrollBarManager *instance(); + +private: + void createUserScript(); + void removeUserScript(); + QSize viewportSize(WebView *view, int thickness) const; + + bool m_enabled = true; + QHash m_scrollbars; +}; + +#endif // WEBSCROLLBARMANAGER_H diff --git a/src/lib/webengine/webview.cpp b/src/lib/webengine/webview.cpp index 3a3bb1a8c..99a87ceef 100644 --- a/src/lib/webengine/webview.cpp +++ b/src/lib/webengine/webview.cpp @@ -34,6 +34,7 @@ #include "webinspector.h" #include "scripts.h" #include "webhittestresult.h" +#include "webscrollbarmanager.h" #include @@ -88,6 +89,7 @@ WebView::WebView(QWidget* parent) WebView::~WebView() { WebInspector::unregisterView(this); + WebScrollBarManager::instance()->removeWebView(this); } QIcon WebView::icon() const @@ -147,9 +149,12 @@ void WebView::setPage(WebPage *page) // Set default zoom level zoomReset(); - // Actions needs to be initialized for every QWebPage change + // Actions needs to be initialized for every QWebEnginePage change initializeActions(); + // Scrollbars must be added only after QWebEnginePage is set + WebScrollBarManager::instance()->addWebView(this); + mApp->plugins()->emitWebPageCreated(m_page); } @@ -1235,5 +1240,19 @@ bool WebView::eventFilter(QObject *obj, QEvent *event) } } - return QWebEngineView::eventFilter(obj, event); + const bool res = QWebEngineView::eventFilter(obj, event); + + if (obj == m_rwhvqt) { + switch (event->type()) { + case QEvent::FocusIn: + case QEvent::FocusOut: + emit focusChanged(hasFocus()); + break; + + default: + break; + } + } + + return res; } diff --git a/src/lib/webengine/webview.h b/src/lib/webengine/webview.h index 3e098879f..efdd161f5 100644 --- a/src/lib/webengine/webview.h +++ b/src/lib/webengine/webview.h @@ -74,6 +74,7 @@ public: static void setForceContextMenuOnMouseRelease(bool force); signals: + void focusChanged(bool); void viewportResized(QSize); void showNotification(QWidget*); void privacyChanged(bool);