|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2016 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the automatic scroller. |
|
8 """ |
|
9 |
|
10 # |
|
11 # This module is based on the Qupzilla auto scroller. |
|
12 # Copyright (C) 2014 David Rosca <nowrep@gmail.com> |
|
13 # |
|
14 |
|
15 from __future__ import unicode_literals |
|
16 |
|
17 from PyQt5.QtCore import Qt, QObject, QRect, QEvent, QPoint |
|
18 from PyQt5.QtWidgets import QApplication, QLabel |
|
19 |
|
20 from .FrameScroller import FrameScroller |
|
21 |
|
22 import Preferences |
|
23 import UI.PixmapCache |
|
24 |
|
25 |
|
26 class AutoScroller(QObject): |
|
27 """ |
|
28 Class implementing the automatic scroller. |
|
29 """ |
|
30 def __init__(self, parent=None): |
|
31 """ |
|
32 Constructor |
|
33 |
|
34 @param parent reference to the parent object |
|
35 @type QObject |
|
36 """ |
|
37 super(AutoScroller, self).__init__(parent) |
|
38 |
|
39 self.__view = None |
|
40 |
|
41 self.__indicator = QLabel() |
|
42 self.__indicator.resize(32, 32) |
|
43 self.__indicator.setContentsMargins(0, 0, 0, 0) |
|
44 self.__indicator.installEventFilter(self) |
|
45 |
|
46 self.__scroller = FrameScroller(self) |
|
47 self.__scroller.setScrollDivider( |
|
48 Preferences.getWebBrowser("AutoScrollDivider")) |
|
49 |
|
50 self.__enabled = Preferences.getWebBrowser("AutoScrollEnabled") |
|
51 |
|
52 def isEnabled(self): |
|
53 """ |
|
54 Public method to get the enabled state. |
|
55 |
|
56 @return enabled state |
|
57 @rtype bool |
|
58 """ |
|
59 return self.__enabled |
|
60 |
|
61 def mouseMove(self, evt): |
|
62 """ |
|
63 Public method to handle mouse move events. |
|
64 |
|
65 @param evt reference to the mouse move event |
|
66 @type QMouseEvent |
|
67 @return flag indicating, that the event was handled |
|
68 @rtype bool |
|
69 """ |
|
70 if self.__enabled and self.__indicator.isVisible(): |
|
71 rect = self.__indicatorGlobalRect() |
|
72 xlen = 0 |
|
73 ylen = 0 |
|
74 egp = evt.globalPos() |
|
75 |
|
76 if rect.left() > egp.x(): |
|
77 xlen = egp.x() - rect.left() |
|
78 elif rect.right() < egp.x(): |
|
79 xlen = egp.x() - rect.right() |
|
80 |
|
81 if rect.top() > egp.y(): |
|
82 ylen = egp.y() - rect.top() |
|
83 elif rect.bottom() < egp.y(): |
|
84 ylen = egp.y() - rect.bottom() |
|
85 |
|
86 self.__scroller.startScrolling(xlen, ylen) |
|
87 |
|
88 return False |
|
89 |
|
90 def mousePress(self, view, evt): |
|
91 """ |
|
92 Public method to handle mouse button presses. |
|
93 |
|
94 @param view reference to the web view the button was pressed on |
|
95 @type WebBrowserView |
|
96 @param evt reference to the mouse button press event |
|
97 @type QMouseEvent |
|
98 @return flag indicating, that the event was handled |
|
99 @rtype bool |
|
100 """ |
|
101 if self.__enabled: |
|
102 middleButton = evt.buttons() == Qt.MiddleButton |
|
103 |
|
104 if view: |
|
105 # test for start |
|
106 if self.__view != view and middleButton: |
|
107 return self.__showIndicator(view, evt.pos()) |
|
108 elif not self.__indicator.isVisible() and middleButton: |
|
109 return self.__showIndicator(view, evt.pos()) |
|
110 |
|
111 # test for stop |
|
112 if self.__indicator.isVisible(): |
|
113 self.__stopScrolling() |
|
114 return True |
|
115 |
|
116 return False |
|
117 |
|
118 def mouseRelease(self, evt): |
|
119 """ |
|
120 Public method to handle mouse button releases. |
|
121 |
|
122 @param evt reference to the mouse button release event |
|
123 @type QMouseEvent |
|
124 @return flag indicating, that the event was handled |
|
125 @rtype bool |
|
126 """ |
|
127 if self.__enabled and self.__indicator.isVisible(): |
|
128 if not self.__indicatorGlobalRect().contains( |
|
129 evt.globalPos()): |
|
130 self.__stopScrolling() |
|
131 return True |
|
132 |
|
133 return False |
|
134 |
|
135 def wheel(self): |
|
136 """ |
|
137 Public method to handle a mouse wheel event. |
|
138 |
|
139 @return flag indicating, that the event was handled |
|
140 @rtype bool |
|
141 """ |
|
142 if self.__enabled and self.__indicator.isVisible(): |
|
143 self.__stopScrolling() |
|
144 return True |
|
145 |
|
146 return False |
|
147 |
|
148 def preferencesChanged(self): |
|
149 """ |
|
150 Public method to handle a change of the settings. |
|
151 """ |
|
152 enabled = Preferences.getWebBrowser("AutoScrollEnabled") |
|
153 if enabled != self.__enabled: |
|
154 if self.__indicator.isVisible(): |
|
155 self.__stopScrolling() |
|
156 self.__enabled = enabled |
|
157 |
|
158 self.__scroller.setScrollDivider( |
|
159 Preferences.getWebBrowser("AutoScrollDivider")) |
|
160 |
|
161 def eventFilter(self, obj, evt): |
|
162 """ |
|
163 Public method to handle event for an object. |
|
164 |
|
165 @param obj refernce to the object sending the event |
|
166 @type QObject |
|
167 @param evt reference to the event to be handled |
|
168 @type QEvent |
|
169 @return flag indicating, that the event was handled |
|
170 @rtype bool |
|
171 """ |
|
172 if obj == self.__indicator: |
|
173 if evt.type() == QEvent.Enter: |
|
174 self.__scroller.stopScrolling() |
|
175 elif evt.type() in [QEvent.Wheel, QEvent.Hide, |
|
176 QEvent.MouseButtonPress]: |
|
177 self.__stopScrolling() |
|
178 |
|
179 return False |
|
180 |
|
181 def __showIndicator(self, view, pos): |
|
182 """ |
|
183 Private method to show the auto scroll indicator. |
|
184 |
|
185 @param view reference to the view to show the indicator on |
|
186 @type WebBrowserView |
|
187 @param pos position to show the indicator at |
|
188 @type QPoint |
|
189 @return flag indicating, that the indicator is shown |
|
190 @rtype bool |
|
191 """ |
|
192 hit = view.page().hitTestContent(pos) |
|
193 |
|
194 if hit.isContentEditable() or not hit.linkUrl().isEmpty(): |
|
195 return False |
|
196 |
|
197 jsSource = """ |
|
198 var out = { |
|
199 vertical: |
|
200 window.innerWidth > document.documentElement.clientWidth, |
|
201 horizontal: |
|
202 window.innerHeight > document.documentElement.clientHeight |
|
203 }; |
|
204 out;""" |
|
205 |
|
206 res = view.page().execJavaScript(jsSource) |
|
207 if res is None: |
|
208 return False |
|
209 |
|
210 vertical = res["vertical"] |
|
211 horizontal = res["horizontal"] |
|
212 if not vertical and not horizontal: |
|
213 return False |
|
214 |
|
215 if vertical and horizontal: |
|
216 self.__indicator.setPixmap( |
|
217 UI.PixmapCache.getPixmap("scrollAll.png")) |
|
218 elif vertical: |
|
219 self.__indicator.setPixmap( |
|
220 UI.PixmapCache.getPixmap("scrollVertical.png")) |
|
221 else: |
|
222 self.__indicator.setPixmap( |
|
223 UI.PixmapCache.getPixmap("scrollHorizontal.png")) |
|
224 |
|
225 self.__view = view |
|
226 p = QPoint( |
|
227 pos.x() - self.__indicator.pixmap().width() // 2, |
|
228 pos.y() - self.__indicator.pixmap().height() // 2 |
|
229 ) |
|
230 |
|
231 self.__indicator.setParent(self.__view) |
|
232 self.__indicator.move(p) |
|
233 self.__indicator.show() |
|
234 |
|
235 self.__scroller.setPage(view.page()) |
|
236 |
|
237 self.__view.inputWidget().grabMouse() |
|
238 QApplication.setOverrideCursor(Qt.ArrowCursor) |
|
239 |
|
240 return True |
|
241 |
|
242 def __stopScrolling(self): |
|
243 """ |
|
244 Private method to stop scrolling. |
|
245 """ |
|
246 self.__view.inputWidget().releaseMouse() |
|
247 QApplication.restoreOverrideCursor() |
|
248 |
|
249 self.__indicator.hide() |
|
250 self.__indicator.setParent(None) |
|
251 self.__scroller.stopScrolling() |
|
252 |
|
253 def __indicatorGlobalRect(self): |
|
254 """ |
|
255 Private method to calculate the global indicator parameters. |
|
256 |
|
257 @return global indicator parameters |
|
258 @rtype QRect |
|
259 """ |
|
260 pos = self.__indicator.parentWidget().mapToGlobal( |
|
261 self.__indicator.geometry().topLeft()) |
|
262 return QRect(pos.x(), pos.y(), |
|
263 self.__indicator.width(), self.__indicator.height()) |