Falkon Develop
Cross-platform Qt-based web browser
webscrollbarmanager.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2016-2017 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18
19#include "webscrollbarmanager.h"
20#include "webscrollbar.h"
21#include "webview.h"
22#include "webpage.h"
23#include "mainapplication.h"
24#include "scripts.h"
25#include "settings.h"
26
27#include <QPointer>
28#include <QPaintEvent>
29#include <QPainter>
30#include <QWebEngineProfile>
31#include <QWebEngineScriptCollection>
32#include <QStyle>
33#include <QStyleOption>
34
35Q_GLOBAL_STATIC(WebScrollBarManager, qz_web_scrollbar_manager)
36
37class WebScrollBarCornerWidget : public QWidget
38{
39public:
41 : QWidget()
42 , m_view(view)
43 {
44 setAutoFillBackground(true);
45 }
46
47 void updateVisibility(bool visible, int thickness)
48 {
49 if (visible) {
50 setParent(m_view->overlayWidget());
51 resize(thickness, thickness);
52 move(m_view->width() - width(), m_view->height() - height());
53 show();
54 } else {
55 hide();
56 }
57 }
58
59private:
60 void paintEvent(QPaintEvent *ev) override
61 {
62 QStyleOption option;
63 option.initFrom(this);
64 option.rect = rect();
65
66 QPainter p(this);
67 if (mApp->styleName() == QL1S("breeze")) {
68 p.fillRect(ev->rect(), option.palette.window());
69 } else {
70 style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this);
71 }
72 }
73
74 WebView *m_view;
75};
76
79 delete vscrollbar;
80 delete hscrollbar;
81 delete corner;
82 }
83
86 bool vscrollbarVisible = false;
87 bool hscrollbarVisible = false;
89};
90
92 : QObject(parent)
93{
94 m_scrollbarJs = QL1S("(function() {"
95 "var head = document.getElementsByTagName('head')[0];"
96 "if (!head) return;"
97 "var css = document.createElement('style');"
98 "css.setAttribute('type', 'text/css');"
99 "var size = %1 / window.devicePixelRatio + 'px';"
100 "css.appendChild(document.createTextNode('"
101 " body::-webkit-scrollbar{width:'+size+';height:'+size+';}"
102 "'));"
103 "head.appendChild(css);"
104 "})()");
105
106 loadSettings();
107}
108
110{
111 m_enabled = Settings().value(QSL("Web-Browser-Settings/UseNativeScrollbars"), false).toBool();
112
113 if (!m_enabled) {
114 for (WebView *view : m_scrollbars.keys()) {
115 removeWebView(view);
116 }
117 }
118}
119
121{
122 if (!m_enabled) {
123 return;
124 }
125
126 delete m_scrollbars.value(view);
127
128 auto *data = new ScrollBarData;
129 data->vscrollbar = new WebScrollBar(Qt::Vertical, view);
130 data->hscrollbar = new WebScrollBar(Qt::Horizontal, view);
131 data->corner = new WebScrollBarCornerWidget(view);
132 m_scrollbars[view] = data;
133
134 const int thickness = data->vscrollbar->thickness();
135
136 auto updateValues = [=]() {
137 const QSize viewport = viewportSize(view, thickness);
138 data->vscrollbar->updateValues(viewport);
139 data->vscrollbar->setVisible(data->vscrollbarVisible);
140 data->hscrollbar->updateValues(viewport);
141 data->hscrollbar->setVisible(data->hscrollbarVisible);
142 data->corner->updateVisibility(data->vscrollbarVisible && data->hscrollbarVisible, thickness);
143 };
144
145 connect(view, &WebView::viewportResized, data->vscrollbar, updateValues);
146 connect(view->page(), &WebPage::scrollPositionChanged, data->vscrollbar, updateValues);
147
148 connect(view->page(), &WebPage::contentsSizeChanged, data->vscrollbar, [=]() {
149 const QString source = QL1S("var out = {"
150 "vertical: document.documentElement && window.innerWidth > document.documentElement.clientWidth,"
151 "horizontal: document.documentElement && window.innerHeight > document.documentElement.clientHeight"
152 "};out;");
153
154 QPointer<WebView> p(view);
155 view->page()->runJavaScript(source, WebPage::SafeJsWorld, [=](const QVariant &res) {
156 if (!p || !m_scrollbars.contains(view)) {
157 return;
158 }
159 const QVariantMap map = res.toMap();
160 data->vscrollbarVisible = map.value(QSL("vertical")).toBool();
161 data->hscrollbarVisible = map.value(QSL("horizontal")).toBool();
162 updateValues();
163 });
164 });
165
166 connect(view, &WebView::zoomLevelChanged, data->vscrollbar, [=]() {
167 view->page()->runJavaScript(m_scrollbarJs.arg(thickness));
168 });
169
170 if (m_scrollbars.size() == 1) {
171 createUserScript(thickness);
172 }
173}
174
176{
177 if (!m_scrollbars.contains(view)) {
178 return;
179 }
180
181 if (m_scrollbars.size() == 1) {
182 removeUserScript();
183 }
184
185 delete m_scrollbars.take(view);
186}
187
188QScrollBar *WebScrollBarManager::scrollBar(Qt::Orientation orientation, WebView *view) const
189{
190 ScrollBarData *d = m_scrollbars.value(view);
191 if (!d) {
192 return nullptr;
193 }
194 return orientation == Qt::Vertical ? d->vscrollbar : d->hscrollbar;
195}
196
198{
199 return qz_web_scrollbar_manager();
200}
201
202void WebScrollBarManager::createUserScript(int thickness)
203{
204 QWebEngineScript script;
205 script.setName(QSL("_falkon_scrollbar"));
206 script.setInjectionPoint(QWebEngineScript::DocumentReady);
207 script.setWorldId(WebPage::SafeJsWorld);
208 script.setSourceCode(m_scrollbarJs.arg(thickness));
209 mApp->webProfile()->scripts()->insert(script);
210}
211
212void WebScrollBarManager::removeUserScript()
213{
214 for (const QWebEngineScript &script : mApp->webProfile()->scripts()->find(QSL("_falkon_scrollbar"))) {
215 mApp->webProfile()->scripts()->remove(script);
216 }
217}
218
219QSize WebScrollBarManager::viewportSize(WebView *view, int thickness) const
220{
221 QSize viewport = view->size();
222
223 thickness /= view->devicePixelRatioF();
224
225 ScrollBarData *data = m_scrollbars.value(view);
226 Q_ASSERT(data);
227
228 if (data->vscrollbarVisible) {
229 viewport.setWidth(viewport.width() - thickness);
230 }
231 if (data->hscrollbarVisible) {
232 viewport.setHeight(viewport.height() - thickness);
233 }
234
235#if 0
236 const QSize content = view->page()->contentsSize().toSize();
237
238 // Check both axis
239 if (content.width() - viewport.width() > 0) {
240 viewport.setHeight(viewport.height() - thickness);
241 }
242
243 if (content.height() - viewport.height() > 0) {
244 viewport.setWidth(viewport.width() - thickness);
245 }
246
247 // Check again against adjusted size
248 if (viewport.height() == view->height() && content.width() - viewport.width() > 0) {
249 viewport.setHeight(viewport.height() - thickness);
250 }
251
252 if (viewport.width() == view->width() && content.height() - viewport.height() > 0) {
253 viewport.setWidth(viewport.width() - thickness);
254 }
255#endif
256
257 return viewport;
258}
QVariant value(const QString &key, const QVariant &defaultValue=QVariant())
Definition: settings.cpp:74
@ SafeJsWorld
Definition: webpage.h:45
void updateVisibility(bool visible, int thickness)
WebScrollBarCornerWidget(WebView *view)
static WebScrollBarManager * instance()
void removeWebView(WebView *view)
QScrollBar * scrollBar(Qt::Orientation orientation, WebView *view) const
void addWebView(WebView *view)
WebScrollBarManager(QObject *parent=nullptr)
WebPage * page() const
Definition: webview.cpp:132
void zoomLevelChanged(int)
void viewportResized(QSize)
#define mApp
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40
WebScrollBar * hscrollbar
WebScrollBarCornerWidget * corner
WebScrollBar * vscrollbar