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