Falkon Develop
Cross-platform Qt-based web browser
autoscroller.cpp
Go to the documentation of this file.
1/* ============================================================
2* AutoScroll - Autoscroll for Falkon
3* Copyright (C) 2014-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#include "autoscroller.h"
19#include "framescroller.h"
20#include "webview.h"
21#include "webpage.h"
22#include "webhittestresult.h"
23
24#include <QApplication>
25#include <QMouseEvent>
26#include <QSettings>
27#include <QIcon>
28#include <QPainter>
29
31 : QLabel(parent)
32{
33 resize(33, 33);
34 setContentsMargins(0, 0, 0, 0);
35}
36
37Qt::Orientations ScrollIndicator::orientations() const
38{
39 return m_orientations;
40}
41
42void ScrollIndicator::setOrientations(Qt::Orientations orientations)
43{
44 m_orientations = orientations;
45
46 if (m_orientations == Qt::Vertical) {
47 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_vertical.png")).pixmap(33));
48 } else if (m_orientations == Qt::Horizontal) {
49 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_horizontal.png")).pixmap(33));
50 } else {
51 setPixmap(QIcon(QSL(":/autoscroll/data/scroll_all.png")).pixmap(33));
52 }
53
54 update();
55}
56
57void ScrollIndicator::paintEvent(QPaintEvent *event)
58{
59 QPainter p(this);
60 p.setRenderHint(QPainter::Antialiasing);
61
62 QRectF r(rect());
63 r.adjust(1, 1, -1, -1);
64
65 QColor c1(Qt::gray);
66 c1.setAlpha(190);
67
68 QColor c2(Qt::white);
69 c2.setAlpha(190);
70
71 QRadialGradient g(r.center(), r.height() / 2.0);
72 g.setColorAt(1, c1);
73 g.setColorAt(0.7, c2);
74
75 p.setPen(Qt::NoPen);
76 p.setBrush(g);
77 p.drawEllipse(r);
78
79 QLabel::paintEvent(event);
80}
81
82AutoScroller::AutoScroller(const QString &settingsFile, QObject* parent)
83 : QObject(parent)
84 , m_view(nullptr)
85 , m_settingsFile(settingsFile)
86{
87 m_indicator = new ScrollIndicator;
88 m_indicator->installEventFilter(this);
89
90 QSettings settings(m_settingsFile, QSettings::IniFormat);
91 settings.beginGroup("AutoScroll");
92
93 m_frameScroller = new FrameScroller(this);
94 m_frameScroller->setScrollDivider(settings.value("ScrollDivider", 8.0).toDouble());
95
96 settings.endGroup();
97}
98
100{
101 delete m_indicator;
102}
103
104bool AutoScroller::mouseMove(QObject* obj, QMouseEvent* event)
105{
106 Q_UNUSED(obj)
107
108 if (m_indicator->isVisible()) {
109 QRect rect = indicatorGlobalRect();
110 int xlength = 0;
111 int ylength = 0;
112
113 if (rect.left() > event->globalPosition().toPoint().x()) {
114 xlength = event->globalPosition().toPoint().x() - rect.left();
115 }
116 else if (rect.right() < event->globalPosition().toPoint().x()) {
117 xlength = event->globalPosition().toPoint().x() - rect.right();
118 }
119 if (rect.top() > event->globalPosition().toPoint().y()) {
120 ylength = event->globalPosition().toPoint().y() - rect.top();
121 }
122 else if (rect.bottom() < event->globalPosition().toPoint().y()) {
123 ylength = event->globalPosition().toPoint().y() - rect.bottom();
124 }
125
126 m_frameScroller->startScrolling(xlength, ylength);
127 }
128
129 return false;
130}
131
132bool AutoScroller::mousePress(QObject* obj, QMouseEvent* event)
133{
134 bool middleButton = event->buttons() == Qt::MiddleButton;
135 auto* view = qobject_cast<WebView*>(obj);
136 Q_ASSERT(view);
137
138 // Start?
139 if (m_view != view && middleButton) {
140 return showIndicator(view, event->position().toPoint());
141 }
142 else if (!m_indicator->isVisible() && middleButton) {
143 return showIndicator(view, event->position().toPoint());
144 }
145
146 // Stop
147 if (m_indicator->isVisible()) {
148 stopScrolling();
149 return true;
150 }
151
152 return false;
153}
154
155bool AutoScroller::mouseRelease(QObject* obj, QMouseEvent* event)
156{
157 Q_UNUSED(obj)
158
159 if (m_indicator->isVisible()) {
160 if (!indicatorGlobalRect().contains(event->globalPosition().toPoint())) {
161 stopScrolling();
162 }
163 return true;
164 }
165
166 return false;
167}
168
169bool AutoScroller::wheel(QObject *obj, QWheelEvent *event)
170{
171 Q_UNUSED(obj)
172 Q_UNUSED(event);
173
174 if (m_indicator->isVisible()) {
175 stopScrolling();
176 return true;
177 }
178
179 return false;
180}
181
183{
184 return m_frameScroller->scrollDivider();
185}
186
188{
189 QSettings settings(m_settingsFile, QSettings::IniFormat);
190 settings.beginGroup("AutoScroll");
191 settings.setValue("ScrollDivider", divider);
192 settings.endGroup();
193
194 m_frameScroller->setScrollDivider(divider);
195}
196
197bool AutoScroller::eventFilter(QObject* obj, QEvent* event)
198{
199 if (obj == m_indicator) {
200 switch (event->type()) {
201 case QEvent::Enter:
202 m_frameScroller->stopScrolling();
203 break;
204
205 case QEvent::Wheel:
206 case QEvent::Hide:
207 case QEvent::MouseButtonPress:
208 stopScrolling();
209 break;
210
211 default:
212 break;
213 }
214 }
215
216 return false;
217}
218
219bool AutoScroller::showIndicator(WebView* view, const QPoint &pos)
220{
221 const WebHitTestResult res = view->page()->hitTestContent(pos);
222
223 if (res.isContentEditable() || !res.linkUrl().isEmpty() || res.tagName().endsWith(QL1S("frame"))) {
224 return false;
225 }
226
227 QString source = QL1S("var out = {"
228 " vertical: window.innerWidth > document.documentElement.clientWidth,"
229 " horizontal: window.innerHeight > document.documentElement.clientHeight"
230 "};"
231 "out;");
232
233 const QVariantMap &map = view->page()->execJavaScript(source, WebPage::SafeJsWorld).toMap();
234
235 bool vertical = map.value(QSL("vertical")).toBool();
236 bool horizontal = map.value(QSL("horizontal")).toBool();
237
238 if (!vertical && !horizontal) {
239 return false;
240 }
241
242 Qt::Orientations orientations;
243 if (vertical) {
244 orientations |= Qt::Vertical;
245 }
246 if (horizontal) {
247 orientations |= Qt::Horizontal;
248 }
249 m_indicator->setOrientations(orientations);
250
251 m_view = view;
252
253 QPoint p;
254 p.setX(pos.x() - m_indicator->width() / 2);
255 p.setY(pos.y() - m_indicator->height() / 2);
256
257 m_indicator->setParent(m_view->overlayWidget());
258 m_indicator->move(m_view->mapTo(m_view->overlayWidget(), p));
259 m_indicator->show();
260
261 m_frameScroller->setPage(view->page());
262
263 m_view->inputWidget()->grabMouse();
264 QApplication::setOverrideCursor(Qt::ArrowCursor);
265
266 return true;
267}
268
269void AutoScroller::stopScrolling()
270{
271 m_view->inputWidget()->releaseMouse();
272 QApplication::restoreOverrideCursor();
273
274 m_indicator->hide();
275 m_indicator->setParent(nullptr);
276 m_frameScroller->stopScrolling();
277}
278
279QRect AutoScroller::indicatorGlobalRect() const
280{
281 QPoint pos = m_indicator->parentWidget()->mapToGlobal(m_indicator->geometry().topLeft());
282 return QRect(pos.x(), pos.y(), m_indicator->width(), m_indicator->height());
283}
~AutoScroller() override
bool mousePress(QObject *obj, QMouseEvent *event)
bool mouseMove(QObject *obj, QMouseEvent *event)
void setScrollDivider(double divider)
double scrollDivider() const
bool wheel(QObject *obj, QWheelEvent *event)
AutoScroller(const QString &settingsFile, QObject *parent=nullptr)
bool mouseRelease(QObject *obj, QMouseEvent *event)
void stopScrolling()
double scrollDivider() const
void setScrollDivider(double divider)
void setPage(WebPage *page)
void startScrolling(int lengthX, int lengthY)
Qt::Orientations orientations() const
ScrollIndicator(QWidget *parent=nullptr)
void setOrientations(Qt::Orientations orientations)
QString tagName() const
bool isContentEditable() const
QVariant execJavaScript(const QString &scriptSource, quint32 worldId=UnsafeJsWorld, int timeout=500)
Definition: webpage.cpp:165
@ SafeJsWorld
Definition: webpage.h:45
WebHitTestResult hitTestContent(const QPoint &pos) const
Definition: webpage.cpp:189
virtual QWidget * overlayWidget()=0
WebPage * page() const
Definition: webview.cpp:132
QWidget * inputWidget() const
Definition: webview.cpp:251
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40