File indexing completed on 2024-05-12 04:58:30
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2016-2017 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 0019 #include "webscrollbarmanager.h" 0020 #include "webscrollbar.h" 0021 #include "webview.h" 0022 #include "webpage.h" 0023 #include "mainapplication.h" 0024 #include "scripts.h" 0025 #include "settings.h" 0026 0027 #include <QPointer> 0028 #include <QPaintEvent> 0029 #include <QPainter> 0030 #include <QWebEngineProfile> 0031 #include <QWebEngineScriptCollection> 0032 #include <QStyle> 0033 #include <QStyleOption> 0034 0035 Q_GLOBAL_STATIC(WebScrollBarManager, qz_web_scrollbar_manager) 0036 0037 class WebScrollBarCornerWidget : public QWidget 0038 { 0039 public: 0040 explicit WebScrollBarCornerWidget(WebView *view) 0041 : QWidget() 0042 , m_view(view) 0043 { 0044 setAutoFillBackground(true); 0045 } 0046 0047 void updateVisibility(bool visible, int thickness) 0048 { 0049 if (visible) { 0050 setParent(m_view->overlayWidget()); 0051 resize(thickness, thickness); 0052 move(m_view->width() - width(), m_view->height() - height()); 0053 show(); 0054 } else { 0055 hide(); 0056 } 0057 } 0058 0059 private: 0060 void paintEvent(QPaintEvent *ev) override 0061 { 0062 QStyleOption option; 0063 option.initFrom(this); 0064 option.rect = rect(); 0065 0066 QPainter p(this); 0067 if (mApp->styleName() == QL1S("breeze")) { 0068 p.fillRect(ev->rect(), option.palette.window()); 0069 } else { 0070 style()->drawPrimitive(QStyle::PE_PanelScrollAreaCorner, &option, &p, this); 0071 } 0072 } 0073 0074 WebView *m_view; 0075 }; 0076 0077 struct ScrollBarData { 0078 ~ScrollBarData() { 0079 delete vscrollbar; 0080 delete hscrollbar; 0081 delete corner; 0082 } 0083 0084 WebScrollBar *vscrollbar; 0085 WebScrollBar *hscrollbar; 0086 bool vscrollbarVisible = false; 0087 bool hscrollbarVisible = false; 0088 WebScrollBarCornerWidget *corner; 0089 }; 0090 0091 WebScrollBarManager::WebScrollBarManager(QObject *parent) 0092 : QObject(parent) 0093 { 0094 m_scrollbarJs = QL1S("(function() {" 0095 "var head = document.getElementsByTagName('head')[0];" 0096 "if (!head) return;" 0097 "var css = document.createElement('style');" 0098 "css.setAttribute('type', 'text/css');" 0099 "var size = %1 / window.devicePixelRatio + 'px';" 0100 "css.appendChild(document.createTextNode('" 0101 " body::-webkit-scrollbar{width:'+size+';height:'+size+';}" 0102 "'));" 0103 "head.appendChild(css);" 0104 "})()"); 0105 0106 loadSettings(); 0107 } 0108 0109 void WebScrollBarManager::loadSettings() 0110 { 0111 m_enabled = Settings().value(QSL("Web-Browser-Settings/UseNativeScrollbars"), false).toBool(); 0112 0113 if (!m_enabled) { 0114 for (WebView *view : m_scrollbars.keys()) { 0115 removeWebView(view); 0116 } 0117 } 0118 } 0119 0120 void WebScrollBarManager::addWebView(WebView *view) 0121 { 0122 if (!m_enabled) { 0123 return; 0124 } 0125 0126 delete m_scrollbars.value(view); 0127 0128 auto *data = new ScrollBarData; 0129 data->vscrollbar = new WebScrollBar(Qt::Vertical, view); 0130 data->hscrollbar = new WebScrollBar(Qt::Horizontal, view); 0131 data->corner = new WebScrollBarCornerWidget(view); 0132 m_scrollbars[view] = data; 0133 0134 const int thickness = data->vscrollbar->thickness(); 0135 0136 auto updateValues = [=]() { 0137 const QSize viewport = viewportSize(view, thickness); 0138 data->vscrollbar->updateValues(viewport); 0139 data->vscrollbar->setVisible(data->vscrollbarVisible); 0140 data->hscrollbar->updateValues(viewport); 0141 data->hscrollbar->setVisible(data->hscrollbarVisible); 0142 data->corner->updateVisibility(data->vscrollbarVisible && data->hscrollbarVisible, thickness); 0143 }; 0144 0145 connect(view, &WebView::viewportResized, data->vscrollbar, updateValues); 0146 connect(view->page(), &WebPage::scrollPositionChanged, data->vscrollbar, updateValues); 0147 0148 connect(view->page(), &WebPage::contentsSizeChanged, data->vscrollbar, [=]() { 0149 const QString source = QL1S("var out = {" 0150 "vertical: document.documentElement && window.innerWidth > document.documentElement.clientWidth," 0151 "horizontal: document.documentElement && window.innerHeight > document.documentElement.clientHeight" 0152 "};out;"); 0153 0154 QPointer<WebView> p(view); 0155 view->page()->runJavaScript(source, WebPage::SafeJsWorld, [=](const QVariant &res) { 0156 if (!p || !m_scrollbars.contains(view)) { 0157 return; 0158 } 0159 const QVariantMap map = res.toMap(); 0160 data->vscrollbarVisible = map.value(QSL("vertical")).toBool(); 0161 data->hscrollbarVisible = map.value(QSL("horizontal")).toBool(); 0162 updateValues(); 0163 }); 0164 }); 0165 0166 connect(view, &WebView::zoomLevelChanged, data->vscrollbar, [=]() { 0167 view->page()->runJavaScript(m_scrollbarJs.arg(thickness)); 0168 }); 0169 0170 if (m_scrollbars.size() == 1) { 0171 createUserScript(thickness); 0172 } 0173 } 0174 0175 void WebScrollBarManager::removeWebView(WebView *view) 0176 { 0177 if (!m_scrollbars.contains(view)) { 0178 return; 0179 } 0180 0181 if (m_scrollbars.size() == 1) { 0182 removeUserScript(); 0183 } 0184 0185 delete m_scrollbars.take(view); 0186 } 0187 0188 QScrollBar *WebScrollBarManager::scrollBar(Qt::Orientation orientation, WebView *view) const 0189 { 0190 ScrollBarData *d = m_scrollbars.value(view); 0191 if (!d) { 0192 return nullptr; 0193 } 0194 return orientation == Qt::Vertical ? d->vscrollbar : d->hscrollbar; 0195 } 0196 0197 WebScrollBarManager *WebScrollBarManager::instance() 0198 { 0199 return qz_web_scrollbar_manager(); 0200 } 0201 0202 void WebScrollBarManager::createUserScript(int thickness) 0203 { 0204 QWebEngineScript script; 0205 script.setName(QSL("_falkon_scrollbar")); 0206 script.setInjectionPoint(QWebEngineScript::DocumentReady); 0207 script.setWorldId(WebPage::SafeJsWorld); 0208 script.setSourceCode(m_scrollbarJs.arg(thickness)); 0209 mApp->webProfile()->scripts()->insert(script); 0210 } 0211 0212 void WebScrollBarManager::removeUserScript() 0213 { 0214 for (const QWebEngineScript &script : mApp->webProfile()->scripts()->find(QSL("_falkon_scrollbar"))) { 0215 mApp->webProfile()->scripts()->remove(script); 0216 } 0217 } 0218 0219 QSize WebScrollBarManager::viewportSize(WebView *view, int thickness) const 0220 { 0221 QSize viewport = view->size(); 0222 0223 thickness /= view->devicePixelRatioF(); 0224 0225 ScrollBarData *data = m_scrollbars.value(view); 0226 Q_ASSERT(data); 0227 0228 if (data->vscrollbarVisible) { 0229 viewport.setWidth(viewport.width() - thickness); 0230 } 0231 if (data->hscrollbarVisible) { 0232 viewport.setHeight(viewport.height() - thickness); 0233 } 0234 0235 #if 0 0236 const QSize content = view->page()->contentsSize().toSize(); 0237 0238 // Check both axis 0239 if (content.width() - viewport.width() > 0) { 0240 viewport.setHeight(viewport.height() - thickness); 0241 } 0242 0243 if (content.height() - viewport.height() > 0) { 0244 viewport.setWidth(viewport.width() - thickness); 0245 } 0246 0247 // Check again against adjusted size 0248 if (viewport.height() == view->height() && content.width() - viewport.width() > 0) { 0249 viewport.setHeight(viewport.height() - thickness); 0250 } 0251 0252 if (viewport.width() == view->width() && content.height() - viewport.height() > 0) { 0253 viewport.setWidth(viewport.width() - thickness); 0254 } 0255 #endif 0256 0257 return viewport; 0258 }