24 |
24 |
25 class HelpViewerImplQWE(HelpViewerImpl, QWebEngineView): |
25 class HelpViewerImplQWE(HelpViewerImpl, QWebEngineView): |
26 """ |
26 """ |
27 Class implementing the QTextBrowser based help viewer class. |
27 Class implementing the QTextBrowser based help viewer class. |
28 """ |
28 """ |
|
29 |
29 ZoomLevels = [ |
30 ZoomLevels = [ |
30 30, 40, 50, 67, 80, 90, |
31 30, |
|
32 40, |
|
33 50, |
|
34 67, |
|
35 80, |
|
36 90, |
31 100, |
37 100, |
32 110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300, |
38 110, |
|
39 120, |
|
40 133, |
|
41 150, |
|
42 170, |
|
43 200, |
|
44 220, |
|
45 233, |
|
46 250, |
|
47 270, |
|
48 285, |
|
49 300, |
33 ] |
50 ] |
34 ZoomLevelDefault = 100 |
51 ZoomLevelDefault = 100 |
35 |
52 |
36 def __init__(self, engine, parent=None): |
53 def __init__(self, engine, parent=None): |
37 """ |
54 """ |
38 Constructor |
55 Constructor |
39 |
56 |
40 @param engine reference to the help engine |
57 @param engine reference to the help engine |
41 @type QHelpEngine |
58 @type QHelpEngine |
42 @param parent reference to the parent widget |
59 @param parent reference to the parent widget |
43 @type QWidget |
60 @type QWidget |
44 """ |
61 """ |
45 QWebEngineView.__init__(self, parent=parent) |
62 QWebEngineView.__init__(self, parent=parent) |
46 HelpViewerImpl.__init__(self, engine) |
63 HelpViewerImpl.__init__(self, engine) |
47 |
64 |
48 self.__helpViewerWidget = parent |
65 self.__helpViewerWidget = parent |
49 |
66 |
50 self.__rwhvqt = None |
67 self.__rwhvqt = None |
51 self.installEventFilter(self) |
68 self.installEventFilter(self) |
52 |
69 |
53 self.__page = None |
70 self.__page = None |
54 self.__createNewPage() |
71 self.__createNewPage() |
55 |
72 |
56 self.__currentScale = 100 |
73 self.__currentScale = 100 |
57 |
74 |
58 self.__menu = QMenu(self) |
75 self.__menu = QMenu(self) |
59 |
76 |
60 def __createNewPage(self): |
77 def __createNewPage(self): |
61 """ |
78 """ |
62 Private method to create a new page object. |
79 Private method to create a new page object. |
63 """ |
80 """ |
64 self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile()) |
81 self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile()) |
65 self.setPage(self.__page) |
82 self.setPage(self.__page) |
66 |
83 |
67 self.__page.titleChanged.connect(self.__titleChanged) |
84 self.__page.titleChanged.connect(self.__titleChanged) |
68 self.__page.urlChanged.connect(self.__titleChanged) |
85 self.__page.urlChanged.connect(self.__titleChanged) |
69 self.__page.newWindowRequested.connect(self.__newWindowRequested) |
86 self.__page.newWindowRequested.connect(self.__newWindowRequested) |
70 |
87 |
71 def __newWindowRequested(self, request): |
88 def __newWindowRequested(self, request): |
72 """ |
89 """ |
73 Private slot handling new window requests of the web page. |
90 Private slot handling new window requests of the web page. |
74 |
91 |
75 @param request reference to the new window request |
92 @param request reference to the new window request |
76 @type QWebEngineNewWindowRequest |
93 @type QWebEngineNewWindowRequest |
77 """ |
94 """ |
78 background = ( |
95 background = ( |
79 request.destination() == |
96 request.destination() |
80 QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab |
97 == QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab |
81 ) |
98 ) |
82 newViewer = self.__helpViewerWidget.addPage(background=background) |
99 newViewer = self.__helpViewerWidget.addPage(background=background) |
83 request.openIn(newViewer.page()) |
100 request.openIn(newViewer.page()) |
84 |
101 |
85 def __setRwhvqt(self): |
102 def __setRwhvqt(self): |
86 """ |
103 """ |
87 Private slot to set widget that receives input events. |
104 Private slot to set widget that receives input events. |
88 """ |
105 """ |
89 self.grabGesture(Qt.GestureType.PinchGesture) |
106 self.grabGesture(Qt.GestureType.PinchGesture) |
90 self.__rwhvqt = self.focusProxy() |
107 self.__rwhvqt = self.focusProxy() |
91 if self.__rwhvqt: |
108 if self.__rwhvqt: |
92 self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture) |
109 self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture) |
93 self.__rwhvqt.installEventFilter(self) |
110 self.__rwhvqt.installEventFilter(self) |
94 else: |
111 else: |
95 print("Focus proxy is null!") # __IGNORE_WARNING_M801__ |
112 print("Focus proxy is null!") # __IGNORE_WARNING_M801__ |
96 |
113 |
97 def setLink(self, url): |
114 def setLink(self, url): |
98 """ |
115 """ |
99 Public method to set the URL of the document to be shown. |
116 Public method to set the URL of the document to be shown. |
100 |
117 |
101 @param url URL of the document |
118 @param url URL of the document |
102 @type QUrl |
119 @type QUrl |
103 """ |
120 """ |
104 if url.toString() == "about:blank": |
121 if url.toString() == "about:blank": |
105 self.setHtml(self.__helpViewerWidget.emptyDocument()) |
122 self.setHtml(self.__helpViewerWidget.emptyDocument()) |
106 else: |
123 else: |
107 super().setUrl(url) |
124 super().setUrl(url) |
108 |
125 |
109 def link(self): |
126 def link(self): |
110 """ |
127 """ |
111 Public method to get the URL of the shown document. |
128 Public method to get the URL of the shown document. |
112 |
129 |
113 @return url URL of the document |
130 @return url URL of the document |
114 @rtype QUrl |
131 @rtype QUrl |
115 """ |
132 """ |
116 return super().url() |
133 return super().url() |
117 |
134 |
118 @pyqtSlot() |
135 @pyqtSlot() |
119 def __titleChanged(self): |
136 def __titleChanged(self): |
120 """ |
137 """ |
121 Private method to handle a change of the web page title. |
138 Private method to handle a change of the web page title. |
122 """ |
139 """ |
123 super().titleChanged.emit() |
140 super().titleChanged.emit() |
124 |
141 |
125 def pageTitle(self): |
142 def pageTitle(self): |
126 """ |
143 """ |
127 Public method get the page title. |
144 Public method get the page title. |
128 |
145 |
129 @return page title |
146 @return page title |
130 @rtype str |
147 @rtype str |
131 """ |
148 """ |
132 titleStr = super().title() |
149 titleStr = super().title() |
133 if not titleStr: |
150 if not titleStr: |
134 if self.link().isEmpty(): |
151 if self.link().isEmpty(): |
135 url = self.__page.requestedUrl() |
152 url = self.__page.requestedUrl() |
136 else: |
153 else: |
137 url = self.link() |
154 url = self.link() |
138 |
155 |
139 titleStr = url.host() |
156 titleStr = url.host() |
140 if not titleStr: |
157 if not titleStr: |
141 titleStr = url.toString( |
158 titleStr = url.toString(QUrl.UrlFormattingOption.RemoveFragment) |
142 QUrl.UrlFormattingOption.RemoveFragment) |
159 |
143 |
|
144 if not titleStr or titleStr == "about:blank": |
160 if not titleStr or titleStr == "about:blank": |
145 titleStr = self.tr("Empty Page") |
161 titleStr = self.tr("Empty Page") |
146 |
162 |
147 return titleStr |
163 return titleStr |
148 |
164 |
149 def isEmptyPage(self): |
165 def isEmptyPage(self): |
150 """ |
166 """ |
151 Public method to check, if the current page is the empty page. |
167 Public method to check, if the current page is the empty page. |
152 |
168 |
153 @return flag indicating an empty page is loaded |
169 @return flag indicating an empty page is loaded |
154 @rtype bool |
170 @rtype bool |
155 """ |
171 """ |
156 return self.pageTitle() == self.tr("Empty Page") |
172 return self.pageTitle() == self.tr("Empty Page") |
157 |
173 |
158 ####################################################################### |
174 ####################################################################### |
159 ## History related methods below |
175 ## History related methods below |
160 ####################################################################### |
176 ####################################################################### |
161 |
177 |
162 def isBackwardAvailable(self): |
178 def isBackwardAvailable(self): |
163 """ |
179 """ |
164 Public method to check, if stepping backward through the history is |
180 Public method to check, if stepping backward through the history is |
165 available. |
181 available. |
166 |
182 |
167 @return flag indicating backward stepping is available |
183 @return flag indicating backward stepping is available |
168 @rtype bool |
184 @rtype bool |
169 """ |
185 """ |
170 return self.history().canGoBack() |
186 return self.history().canGoBack() |
171 |
187 |
172 def isForwardAvailable(self): |
188 def isForwardAvailable(self): |
173 """ |
189 """ |
174 Public method to check, if stepping forward through the history is |
190 Public method to check, if stepping forward through the history is |
175 available. |
191 available. |
176 |
192 |
177 @return flag indicating forward stepping is available |
193 @return flag indicating forward stepping is available |
178 @rtype bool |
194 @rtype bool |
179 """ |
195 """ |
180 return self.history().canGoForward() |
196 return self.history().canGoForward() |
181 |
197 |
182 def backward(self): |
198 def backward(self): |
183 """ |
199 """ |
184 Public slot to move backwards in history. |
200 Public slot to move backwards in history. |
185 """ |
201 """ |
186 self.triggerPageAction(QWebEnginePage.WebAction.Back) |
202 self.triggerPageAction(QWebEnginePage.WebAction.Back) |
187 |
203 |
188 def forward(self): |
204 def forward(self): |
189 """ |
205 """ |
190 Public slot to move forward in history. |
206 Public slot to move forward in history. |
191 """ |
207 """ |
192 self.triggerPageAction(QWebEnginePage.WebAction.Forward) |
208 self.triggerPageAction(QWebEnginePage.WebAction.Forward) |
193 |
209 |
194 def reload(self): |
210 def reload(self): |
195 """ |
211 """ |
196 Public slot to reload the current page. |
212 Public slot to reload the current page. |
197 """ |
213 """ |
198 self.triggerPageAction(QWebEnginePage.WebAction.Reload) |
214 self.triggerPageAction(QWebEnginePage.WebAction.Reload) |
199 |
215 |
200 def backwardHistoryCount(self): |
216 def backwardHistoryCount(self): |
201 """ |
217 """ |
202 Public method to get the number of available back history items. |
218 Public method to get the number of available back history items. |
203 |
219 |
204 Note: For performance reasons this is limited to the maximum number of |
220 Note: For performance reasons this is limited to the maximum number of |
205 history items the help viewer is interested in. |
221 history items the help viewer is interested in. |
206 |
222 |
207 @return count of available back history items |
223 @return count of available back history items |
208 @rtype int |
224 @rtype int |
209 """ |
225 """ |
210 history = self.history() |
226 history = self.history() |
211 return len(history.backItems(HelpViewerWidget.MaxHistoryItems)) |
227 return len(history.backItems(HelpViewerWidget.MaxHistoryItems)) |
212 |
228 |
213 def forwardHistoryCount(self): |
229 def forwardHistoryCount(self): |
214 """ |
230 """ |
215 Public method to get the number of available forward history items. |
231 Public method to get the number of available forward history items. |
216 |
232 |
217 Note: For performance reasons this is limited to the maximum number of |
233 Note: For performance reasons this is limited to the maximum number of |
218 history items the help viewer is interested in. |
234 history items the help viewer is interested in. |
219 |
235 |
220 @return count of available forward history items |
236 @return count of available forward history items |
221 @rtype int |
237 @rtype int |
222 """ |
238 """ |
223 history = self.history() |
239 history = self.history() |
224 return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems)) |
240 return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems)) |
225 |
241 |
226 def historyTitle(self, offset): |
242 def historyTitle(self, offset): |
227 """ |
243 """ |
228 Public method to get the title of a history item. |
244 Public method to get the title of a history item. |
229 |
245 |
230 @param offset offset of the item with respect to the current page |
246 @param offset offset of the item with respect to the current page |
231 @type int |
247 @type int |
232 @return title of the requeted item in history |
248 @return title of the requeted item in history |
233 @rtype str |
249 @rtype str |
234 """ |
250 """ |
235 history = self.history() |
251 history = self.history() |
236 currentIndex = history.currentItemIndex() |
252 currentIndex = history.currentItemIndex() |
237 itm = self.history().itemAt(currentIndex + offset) |
253 itm = self.history().itemAt(currentIndex + offset) |
238 return itm.title() |
254 return itm.title() |
239 |
255 |
240 def gotoHistory(self, offset): |
256 def gotoHistory(self, offset): |
241 """ |
257 """ |
242 Public method to go to a history item. |
258 Public method to go to a history item. |
243 |
259 |
244 @param offset offset of the item with respect to the current page |
260 @param offset offset of the item with respect to the current page |
245 @type int |
261 @type int |
246 """ |
262 """ |
247 history = self.history() |
263 history = self.history() |
248 currentIndex = history.currentItemIndex() |
264 currentIndex = history.currentItemIndex() |
249 itm = self.history().itemAt(currentIndex + offset) |
265 itm = self.history().itemAt(currentIndex + offset) |
250 history.goToItem(itm) |
266 history.goToItem(itm) |
251 |
267 |
252 def clearHistory(self): |
268 def clearHistory(self): |
253 """ |
269 """ |
254 Public method to clear the history. |
270 Public method to clear the history. |
255 """ |
271 """ |
256 self.history().clear() |
272 self.history().clear() |
257 |
273 |
258 ####################################################################### |
274 ####################################################################### |
259 ## Zoom related methods below |
275 ## Zoom related methods below |
260 ####################################################################### |
276 ####################################################################### |
261 |
277 |
262 def __levelForScale(self, scale): |
278 def __levelForScale(self, scale): |
263 """ |
279 """ |
264 Private method determining the zoom level index given a zoom factor. |
280 Private method determining the zoom level index given a zoom factor. |
265 |
281 |
266 @param scale zoom factor |
282 @param scale zoom factor |
267 @type int |
283 @type int |
268 @return index of zoom factor |
284 @return index of zoom factor |
269 @rtype int |
285 @rtype int |
270 """ |
286 """ |
273 except ValueError: |
289 except ValueError: |
274 for _index in range(len(self.ZoomLevels)): |
290 for _index in range(len(self.ZoomLevels)): |
275 if scale <= self.ZoomLevels[scale]: |
291 if scale <= self.ZoomLevels[scale]: |
276 break |
292 break |
277 return index |
293 return index |
278 |
294 |
279 def scaleUp(self): |
295 def scaleUp(self): |
280 """ |
296 """ |
281 Public method to zoom in. |
297 Public method to zoom in. |
282 """ |
298 """ |
283 index = self.__levelForScale(self.__currentScale) |
299 index = self.__levelForScale(self.__currentScale) |
284 if index < len(self.ZoomLevels) - 1: |
300 if index < len(self.ZoomLevels) - 1: |
285 self.setScale(self.ZoomLevels[index + 1]) |
301 self.setScale(self.ZoomLevels[index + 1]) |
286 |
302 |
287 def scaleDown(self): |
303 def scaleDown(self): |
288 """ |
304 """ |
289 Public method to zoom out. |
305 Public method to zoom out. |
290 """ |
306 """ |
291 index = self.__levelForScale(self.__currentScale) |
307 index = self.__levelForScale(self.__currentScale) |
292 if index > 0: |
308 if index > 0: |
293 self.setScale(self.ZoomLevels[index - 1]) |
309 self.setScale(self.ZoomLevels[index - 1]) |
294 |
310 |
295 def setScale(self, scale): |
311 def setScale(self, scale): |
296 """ |
312 """ |
297 Public method to set the zoom level. |
313 Public method to set the zoom level. |
298 |
314 |
299 @param scale zoom level to set |
315 @param scale zoom level to set |
300 @type int |
316 @type int |
301 """ |
317 """ |
302 if scale != self.__currentScale: |
318 if scale != self.__currentScale: |
303 self.setZoomFactor(scale / 100.0) |
319 self.setZoomFactor(scale / 100.0) |
304 self.__currentScale = scale |
320 self.__currentScale = scale |
305 self.zoomChanged.emit() |
321 self.zoomChanged.emit() |
306 |
322 |
307 def resetScale(self): |
323 def resetScale(self): |
308 """ |
324 """ |
309 Public method to reset the zoom level. |
325 Public method to reset the zoom level. |
310 """ |
326 """ |
311 index = self.__levelForScale(self.ZoomLevelDefault) |
327 index = self.__levelForScale(self.ZoomLevelDefault) |
312 self.setScale(self.ZoomLevels[index]) |
328 self.setScale(self.ZoomLevels[index]) |
313 |
329 |
314 def scale(self): |
330 def scale(self): |
315 """ |
331 """ |
316 Public method to get the zoom level. |
332 Public method to get the zoom level. |
317 |
333 |
318 @return current zoom level |
334 @return current zoom level |
319 @rtype int |
335 @rtype int |
320 """ |
336 """ |
321 return self.__currentScale |
337 return self.__currentScale |
322 |
338 |
323 def isScaleUpAvailable(self): |
339 def isScaleUpAvailable(self): |
324 """ |
340 """ |
325 Public method to check, if the max. zoom level is reached. |
341 Public method to check, if the max. zoom level is reached. |
326 |
342 |
327 @return flag indicating scale up is available |
343 @return flag indicating scale up is available |
328 @rtype bool |
344 @rtype bool |
329 """ |
345 """ |
330 index = self.__levelForScale(self.__currentScale) |
346 index = self.__levelForScale(self.__currentScale) |
331 return index < len(self.ZoomLevels) - 1 |
347 return index < len(self.ZoomLevels) - 1 |
332 |
348 |
333 def isScaleDownAvailable(self): |
349 def isScaleDownAvailable(self): |
334 """ |
350 """ |
335 Public method to check, if the min. zoom level is reached. |
351 Public method to check, if the min. zoom level is reached. |
336 |
352 |
337 @return flag indicating scale down is available |
353 @return flag indicating scale down is available |
338 @rtype bool |
354 @rtype bool |
339 """ |
355 """ |
340 index = self.__levelForScale(self.__currentScale) |
356 index = self.__levelForScale(self.__currentScale) |
341 return index > 0 |
357 return index > 0 |
342 |
358 |
343 ####################################################################### |
359 ####################################################################### |
344 ## Event handlers below |
360 ## Event handlers below |
345 ####################################################################### |
361 ####################################################################### |
346 |
362 |
347 def eventFilter(self, obj, evt): |
363 def eventFilter(self, obj, evt): |
348 """ |
364 """ |
349 Public method to process event for other objects. |
365 Public method to process event for other objects. |
350 |
366 |
351 @param obj reference to object to process events for |
367 @param obj reference to object to process events for |
352 @type QObject |
368 @type QObject |
353 @param evt reference to event to be processed |
369 @param evt reference to event to be processed |
354 @type QEvent |
370 @type QEvent |
355 @return flag indicating that the event should be filtered out |
371 @return flag indicating that the event should be filtered out |
356 @rtype bool |
372 @rtype bool |
357 """ |
373 """ |
358 if ( |
374 if ( |
359 obj is self and |
375 obj is self |
360 evt.type() == QEvent.Type.ParentChange and |
376 and evt.type() == QEvent.Type.ParentChange |
361 self.parentWidget() is not None |
377 and self.parentWidget() is not None |
362 ): |
378 ): |
363 self.parentWidget().installEventFilter(self) |
379 self.parentWidget().installEventFilter(self) |
364 |
380 |
365 # find the render widget receiving events for the web page |
381 # find the render widget receiving events for the web page |
366 if obj is self and evt.type() == QEvent.Type.ChildAdded: |
382 if obj is self and evt.type() == QEvent.Type.ChildAdded: |
367 QTimer.singleShot(0, self.__setRwhvqt) |
383 QTimer.singleShot(0, self.__setRwhvqt) |
368 |
384 |
369 # forward events to WebBrowserView |
385 # forward events to WebBrowserView |
370 if ( |
386 if obj is self.__rwhvqt and evt.type() in [ |
371 obj is self.__rwhvqt and |
387 QEvent.Type.KeyPress, |
372 evt.type() in [QEvent.Type.KeyPress, |
388 QEvent.Type.MouseButtonRelease, |
373 QEvent.Type.MouseButtonRelease, |
389 QEvent.Type.Wheel, |
374 QEvent.Type.Wheel, |
390 QEvent.Type.Gesture, |
375 QEvent.Type.Gesture] |
391 ]: |
376 ): |
|
377 wasAccepted = evt.isAccepted() |
392 wasAccepted = evt.isAccepted() |
378 evt.setAccepted(False) |
393 evt.setAccepted(False) |
379 if evt.type() == QEvent.Type.KeyPress: |
394 if evt.type() == QEvent.Type.KeyPress: |
380 self._keyPressEvent(evt) |
395 self._keyPressEvent(evt) |
381 elif evt.type() == QEvent.Type.MouseButtonRelease: |
396 elif evt.type() == QEvent.Type.MouseButtonRelease: |
385 elif evt.type() == QEvent.Type.Gesture: |
400 elif evt.type() == QEvent.Type.Gesture: |
386 self._gestureEvent(evt) |
401 self._gestureEvent(evt) |
387 ret = evt.isAccepted() |
402 ret = evt.isAccepted() |
388 evt.setAccepted(wasAccepted) |
403 evt.setAccepted(wasAccepted) |
389 return ret |
404 return ret |
390 |
405 |
391 if ( |
406 if obj is self.parentWidget() and evt.type() in [ |
392 obj is self.parentWidget() and |
407 QEvent.Type.KeyPress, |
393 evt.type() in [QEvent.Type.KeyPress, QEvent.Type.KeyRelease] |
408 QEvent.Type.KeyRelease, |
394 ): |
409 ]: |
395 wasAccepted = evt.isAccepted() |
410 wasAccepted = evt.isAccepted() |
396 evt.setAccepted(False) |
411 evt.setAccepted(False) |
397 if evt.type() == QEvent.Type.KeyPress: |
412 if evt.type() == QEvent.Type.KeyPress: |
398 self._keyPressEvent(evt) |
413 self._keyPressEvent(evt) |
399 ret = evt.isAccepted() |
414 ret = evt.isAccepted() |
400 evt.setAccepted(wasAccepted) |
415 evt.setAccepted(wasAccepted) |
401 return ret |
416 return ret |
402 |
417 |
403 # block already handled events |
418 # block already handled events |
404 if ( |
419 if obj is self and evt.type() in [ |
405 obj is self and |
420 QEvent.Type.KeyPress, |
406 evt.type() in [QEvent.Type.KeyPress, |
421 QEvent.Type.MouseButtonRelease, |
407 QEvent.Type.MouseButtonRelease, |
422 QEvent.Type.Wheel, |
408 QEvent.Type.Wheel, |
423 QEvent.Type.Gesture, |
409 QEvent.Type.Gesture] |
424 ]: |
410 ): |
|
411 return True |
425 return True |
412 |
426 |
413 return super().eventFilter(obj, evt) |
427 return super().eventFilter(obj, evt) |
414 |
428 |
415 def _keyPressEvent(self, evt): |
429 def _keyPressEvent(self, evt): |
416 """ |
430 """ |
417 Protected method called by a key press. |
431 Protected method called by a key press. |
418 |
432 |
419 @param evt reference to the key event |
433 @param evt reference to the key event |
420 @type QKeyEvent |
434 @type QKeyEvent |
421 """ |
435 """ |
422 key = evt.key() |
436 key = evt.key() |
423 isControlModifier = ( |
437 isControlModifier = evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
424 evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
438 |
425 ) |
439 if key == Qt.Key.Key_ZoomIn or (key == Qt.Key.Key_Plus and isControlModifier): |
426 |
|
427 if ( |
|
428 key == Qt.Key.Key_ZoomIn or |
|
429 (key == Qt.Key.Key_Plus and isControlModifier) |
|
430 ): |
|
431 self.scaleUp() |
440 self.scaleUp() |
432 evt.accept() |
441 evt.accept() |
433 elif ( |
442 elif key == Qt.Key.Key_ZoomOut or ( |
434 key == Qt.Key.Key_ZoomOut or |
443 key == Qt.Key.Key_Minus and isControlModifier |
435 (key == Qt.Key.Key_Minus and isControlModifier) |
|
436 ): |
444 ): |
437 self.scaleDown() |
445 self.scaleDown() |
438 evt.accept() |
446 evt.accept() |
439 elif key == Qt.Key.Key_0 and isControlModifier: |
447 elif key == Qt.Key.Key_0 and isControlModifier: |
440 self.resetScale() |
448 self.resetScale() |
441 evt.accept() |
449 evt.accept() |
442 elif ( |
450 elif key == Qt.Key.Key_Backspace or ( |
443 key == Qt.Key.Key_Backspace or |
451 key == Qt.Key.Key_Left and isControlModifier |
444 (key == Qt.Key.Key_Left and isControlModifier) |
|
445 ): |
452 ): |
446 self.backward() |
453 self.backward() |
447 evt.accept() |
454 evt.accept() |
448 elif key == Qt.Key.Key_Right and isControlModifier: |
455 elif key == Qt.Key.Key_Right and isControlModifier: |
449 self.forward() |
456 self.forward() |
450 evt.accept() |
457 evt.accept() |
451 elif key == Qt.Key.Key_F and isControlModifier: |
458 elif key == Qt.Key.Key_F and isControlModifier: |
452 self.__helpViewerWidget.showHideSearch(True) |
459 self.__helpViewerWidget.showHideSearch(True) |
453 evt.accept() |
460 evt.accept() |
|
461 elif key == Qt.Key.Key_F3 and evt.modifiers() == Qt.KeyboardModifier.NoModifier: |
|
462 self.__helpViewerWidget.searchNext() |
|
463 evt.accept() |
454 elif ( |
464 elif ( |
455 key == Qt.Key.Key_F3 and |
465 key == Qt.Key.Key_F3 |
456 evt.modifiers() == Qt.KeyboardModifier.NoModifier |
466 and evt.modifiers() == Qt.KeyboardModifier.ShiftModifier |
457 ): |
|
458 self.__helpViewerWidget.searchNext() |
|
459 evt.accept() |
|
460 elif ( |
|
461 key == Qt.Key.Key_F3 and |
|
462 evt.modifiers() == Qt.KeyboardModifier.ShiftModifier |
|
463 ): |
467 ): |
464 self.__helpViewerWidget.searchPrev() |
468 self.__helpViewerWidget.searchPrev() |
465 evt.accept() |
469 evt.accept() |
466 |
470 |
467 def _mouseReleaseEvent(self, evt): |
471 def _mouseReleaseEvent(self, evt): |
468 """ |
472 """ |
469 Protected method called by a mouse release event. |
473 Protected method called by a mouse release event. |
470 |
474 |
471 @param evt reference to the mouse event |
475 @param evt reference to the mouse event |
472 @type QMouseEvent |
476 @type QMouseEvent |
473 """ |
477 """ |
474 accepted = evt.isAccepted() |
478 accepted = evt.isAccepted() |
475 self.__page.event(evt) |
479 self.__page.event(evt) |
476 if ( |
480 if not evt.isAccepted() and evt.button() == Qt.MouseButton.MiddleButton: |
477 not evt.isAccepted() and |
481 url = QUrl(QGuiApplication.clipboard().text(QClipboard.Mode.Selection)) |
478 evt.button() == Qt.MouseButton.MiddleButton |
482 if not url.isEmpty() and url.isValid() and url.scheme() != "": |
479 ): |
|
480 url = QUrl(QGuiApplication.clipboard().text( |
|
481 QClipboard.Mode.Selection)) |
|
482 if ( |
|
483 not url.isEmpty() and |
|
484 url.isValid() and |
|
485 url.scheme() != "" |
|
486 ): |
|
487 self.setLink(url) |
483 self.setLink(url) |
488 accepted = True |
484 accepted = True |
489 evt.setAccepted(accepted) |
485 evt.setAccepted(accepted) |
490 |
486 |
491 def _wheelEvent(self, evt): |
487 def _wheelEvent(self, evt): |
492 """ |
488 """ |
493 Protected method to handle wheel events. |
489 Protected method to handle wheel events. |
494 |
490 |
495 @param evt reference to the wheel event |
491 @param evt reference to the wheel event |
496 @type QWheelEvent |
492 @type QWheelEvent |
497 """ |
493 """ |
498 delta = evt.angleDelta().y() |
494 delta = evt.angleDelta().y() |
499 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: |
495 if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: |
500 if delta < 0: |
496 if delta < 0: |
501 self.scaleDown() |
497 self.scaleDown() |
502 elif delta > 0: |
498 elif delta > 0: |
503 self.scaleUp() |
499 self.scaleUp() |
504 evt.accept() |
500 evt.accept() |
505 |
501 |
506 elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: |
502 elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: |
507 if delta < 0: |
503 if delta < 0: |
508 self.backward() |
504 self.backward() |
509 elif delta > 0: |
505 elif delta > 0: |
510 self.forward() |
506 self.forward() |
511 evt.accept() |
507 evt.accept() |
512 |
508 |
513 def _gestureEvent(self, evt): |
509 def _gestureEvent(self, evt): |
514 """ |
510 """ |
515 Protected method handling gesture events. |
511 Protected method handling gesture events. |
516 |
512 |
517 @param evt reference to the gesture event |
513 @param evt reference to the gesture event |
518 @type QGestureEvent |
514 @type QGestureEvent |
519 """ |
515 """ |
520 pinch = evt.gesture(Qt.GestureType.PinchGesture) |
516 pinch = evt.gesture(Qt.GestureType.PinchGesture) |
521 if pinch: |
517 if pinch: |
523 pinch.setTotalScaleFactor(self.__currentScale / 100.0) |
519 pinch.setTotalScaleFactor(self.__currentScale / 100.0) |
524 elif pinch.state() == Qt.GestureState.GestureUpdated: |
520 elif pinch.state() == Qt.GestureState.GestureUpdated: |
525 scaleFactor = pinch.totalScaleFactor() |
521 scaleFactor = pinch.totalScaleFactor() |
526 self.setScale(int(scaleFactor * 100)) |
522 self.setScale(int(scaleFactor * 100)) |
527 evt.accept() |
523 evt.accept() |
528 |
524 |
529 def event(self, evt): |
525 def event(self, evt): |
530 """ |
526 """ |
531 Public method handling events. |
527 Public method handling events. |
532 |
528 |
533 @param evt reference to the event (QEvent) |
529 @param evt reference to the event (QEvent) |
534 @return flag indicating, if the event was handled (boolean) |
530 @return flag indicating, if the event was handled (boolean) |
535 """ |
531 """ |
536 if evt.type() == QEvent.Type.Gesture: |
532 if evt.type() == QEvent.Type.Gesture: |
537 self._gestureEvent(evt) |
533 self._gestureEvent(evt) |
538 return True |
534 return True |
539 |
535 |
540 return super().event(evt) |
536 return super().event(evt) |
541 |
537 |
542 ####################################################################### |
538 ####################################################################### |
543 ## Context menu related methods below |
539 ## Context menu related methods below |
544 ####################################################################### |
540 ####################################################################### |
545 |
541 |
546 def contextMenuEvent(self, evt): |
542 def contextMenuEvent(self, evt): |
547 """ |
543 """ |
548 Protected method called to create a context menu. |
544 Protected method called to create a context menu. |
549 |
545 |
550 This method is overridden from QWebEngineView. |
546 This method is overridden from QWebEngineView. |
551 |
547 |
552 @param evt reference to the context menu event object |
548 @param evt reference to the context menu event object |
553 @type QContextMenuEvent |
549 @type QContextMenuEvent |
554 """ |
550 """ |
555 pos = evt.pos() |
551 pos = evt.pos() |
556 reason = evt.reason() |
552 reason = evt.reason() |
557 QTimer.singleShot( |
553 QTimer.singleShot( |
558 0, |
554 0, lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)) |
559 lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos))) |
555 ) |
560 # needs to be done this way because contextMenuEvent is blocking |
556 # needs to be done this way because contextMenuEvent is blocking |
561 # the main loop |
557 # the main loop |
562 |
558 |
563 def _contextMenuEvent(self, evt): |
559 def _contextMenuEvent(self, evt): |
564 """ |
560 """ |
565 Protected method called to create a context menu. |
561 Protected method called to create a context menu. |
566 |
562 |
567 @param evt reference to the context menu event object |
563 @param evt reference to the context menu event object |
568 (QContextMenuEvent) |
564 (QContextMenuEvent) |
569 """ |
565 """ |
570 self.__menu.clear() |
566 self.__menu.clear() |
571 |
567 |
572 self.__createContextMenu(self.__menu) |
568 self.__createContextMenu(self.__menu) |
573 |
569 |
574 if not self.__menu.isEmpty(): |
570 if not self.__menu.isEmpty(): |
575 pos = evt.globalPos() |
571 pos = evt.globalPos() |
576 self.__menu.popup(QPoint(pos.x(), pos.y() + 1)) |
572 self.__menu.popup(QPoint(pos.x(), pos.y() + 1)) |
577 |
573 |
578 def __createContextMenu(self, menu): |
574 def __createContextMenu(self, menu): |
579 """ |
575 """ |
580 Private method to populate the context menu. |
576 Private method to populate the context menu. |
581 |
577 |
582 @param menu reference to the menu to be populated |
578 @param menu reference to the menu to be populated |
583 @type QMenu |
579 @type QMenu |
584 """ |
580 """ |
585 contextMenuData = self.lastContextMenuRequest() |
581 contextMenuData = self.lastContextMenuRequest() |
586 |
582 |
587 act = menu.addAction( |
583 act = menu.addAction( |
588 UI.PixmapCache.getIcon("back"), |
584 UI.PixmapCache.getIcon("back"), self.tr("Backward"), self.backward |
589 self.tr("Backward"), |
585 ) |
590 self.backward) |
|
591 act.setEnabled(self.isBackwardAvailable()) |
586 act.setEnabled(self.isBackwardAvailable()) |
592 |
587 |
593 act = menu.addAction( |
588 act = menu.addAction( |
594 UI.PixmapCache.getIcon("forward"), |
589 UI.PixmapCache.getIcon("forward"), self.tr("Forward"), self.forward |
595 self.tr("Forward"), |
590 ) |
596 self.forward) |
|
597 act.setEnabled(self.isForwardAvailable()) |
591 act.setEnabled(self.isForwardAvailable()) |
598 |
592 |
599 act = menu.addAction( |
593 act = menu.addAction( |
600 UI.PixmapCache.getIcon("reload"), |
594 UI.PixmapCache.getIcon("reload"), self.tr("Reload"), self.reload |
601 self.tr("Reload"), |
595 ) |
602 self.reload) |
596 |
603 |
|
604 if ( |
597 if ( |
605 not contextMenuData.linkUrl().isEmpty() and |
598 not contextMenuData.linkUrl().isEmpty() |
606 contextMenuData.linkUrl().scheme() != "javascript" |
599 and contextMenuData.linkUrl().scheme() != "javascript" |
607 ): |
600 ): |
608 self.__createLinkContextMenu(menu, contextMenuData) |
601 self.__createLinkContextMenu(menu, contextMenuData) |
609 |
602 |
610 menu.addSeparator() |
603 menu.addSeparator() |
611 |
604 |
612 act = menu.addAction( |
605 act = menu.addAction( |
613 UI.PixmapCache.getIcon("editCopy"), |
606 UI.PixmapCache.getIcon("editCopy"), self.tr("Copy Page URL to Clipboard") |
614 self.tr("Copy Page URL to Clipboard")) |
607 ) |
615 act.setData(self.link()) |
608 act.setData(self.link()) |
616 act.triggered.connect( |
609 act.triggered.connect(functools.partial(self.__copyLink, act)) |
617 functools.partial(self.__copyLink, act)) |
610 |
618 |
611 act = menu.addAction( |
619 act = menu.addAction( |
612 UI.PixmapCache.getIcon("bookmark22"), self.tr("Bookmark Page") |
620 UI.PixmapCache.getIcon("bookmark22"), |
613 ) |
621 self.tr("Bookmark Page")) |
614 act.setData({"title": self.pageTitle(), "url": self.link()}) |
622 act.setData({ |
615 act.triggered.connect(functools.partial(self.__bookmarkPage, act)) |
623 "title": self.pageTitle(), |
616 |
624 "url": self.link() |
|
625 }) |
|
626 act.triggered.connect( |
|
627 functools.partial(self.__bookmarkPage, act)) |
|
628 |
|
629 menu.addSeparator() |
617 menu.addSeparator() |
630 |
618 |
631 act = menu.addAction( |
619 act = menu.addAction( |
632 UI.PixmapCache.getIcon("zoomIn"), |
620 UI.PixmapCache.getIcon("zoomIn"), self.tr("Zoom in"), self.scaleUp |
633 self.tr("Zoom in"), |
621 ) |
634 self.scaleUp) |
|
635 act.setEnabled(self.isScaleUpAvailable()) |
622 act.setEnabled(self.isScaleUpAvailable()) |
636 |
623 |
637 act = menu.addAction( |
624 act = menu.addAction( |
638 UI.PixmapCache.getIcon("zoomOut"), |
625 UI.PixmapCache.getIcon("zoomOut"), self.tr("Zoom out"), self.scaleDown |
639 self.tr("Zoom out"), |
626 ) |
640 self.scaleDown) |
|
641 act.setEnabled(self.isScaleDownAvailable()) |
627 act.setEnabled(self.isScaleDownAvailable()) |
642 |
628 |
643 menu.addAction( |
629 menu.addAction( |
644 UI.PixmapCache.getIcon("zoomReset"), |
630 UI.PixmapCache.getIcon("zoomReset"), self.tr("Zoom reset"), self.resetScale |
645 self.tr("Zoom reset"), |
631 ) |
646 self.resetScale) |
632 |
647 |
|
648 menu.addSeparator() |
633 menu.addSeparator() |
649 |
634 |
650 act = menu.addAction( |
635 act = menu.addAction( |
651 UI.PixmapCache.getIcon("editCopy"), |
636 UI.PixmapCache.getIcon("editCopy"), self.tr("Copy"), self.__copyText |
652 self.tr("Copy"), |
637 ) |
653 self.__copyText) |
|
654 act.setEnabled(bool(contextMenuData.selectedText())) |
638 act.setEnabled(bool(contextMenuData.selectedText())) |
655 |
639 |
656 menu.addAction( |
640 menu.addAction( |
657 UI.PixmapCache.getIcon("editSelectAll"), |
641 UI.PixmapCache.getIcon("editSelectAll"), |
658 self.tr("Select All"), |
642 self.tr("Select All"), |
659 self.__selectAll) |
643 self.__selectAll, |
660 |
644 ) |
|
645 |
661 menu.addSeparator() |
646 menu.addSeparator() |
662 |
647 |
663 menu.addAction( |
648 menu.addAction( |
664 UI.PixmapCache.getIcon("tabClose"), |
649 UI.PixmapCache.getIcon("tabClose"), self.tr("Close"), self.__closePage |
665 self.tr('Close'), |
650 ) |
666 self.__closePage) |
651 |
667 |
|
668 act = menu.addAction( |
652 act = menu.addAction( |
669 UI.PixmapCache.getIcon("tabCloseOther"), |
653 UI.PixmapCache.getIcon("tabCloseOther"), |
670 self.tr("Close Others"), |
654 self.tr("Close Others"), |
671 self.__closeOtherPages) |
655 self.__closeOtherPages, |
|
656 ) |
672 act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1) |
657 act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1) |
673 |
658 |
674 def __createLinkContextMenu(self, menu, contextMenuData): |
659 def __createLinkContextMenu(self, menu, contextMenuData): |
675 """ |
660 """ |
676 Private method to populate the context menu for URLs. |
661 Private method to populate the context menu for URLs. |
677 |
662 |
678 @param menu reference to the menu to be populated |
663 @param menu reference to the menu to be populated |
679 @type QMenu |
664 @type QMenu |
680 @param contextMenuData data of the last context menu request |
665 @param contextMenuData data of the last context menu request |
681 @type QWebEngineContextMenuRequest |
666 @type QWebEngineContextMenuRequest |
682 """ |
667 """ |
683 if not menu.isEmpty(): |
668 if not menu.isEmpty(): |
684 menu.addSeparator() |
669 menu.addSeparator() |
685 |
670 |
686 act = menu.addAction( |
671 act = menu.addAction( |
687 UI.PixmapCache.getIcon("openNewTab"), |
672 UI.PixmapCache.getIcon("openNewTab"), self.tr("Open Link in New Page") |
688 self.tr("Open Link in New Page")) |
673 ) |
689 act.setData(contextMenuData.linkUrl()) |
674 act.setData(contextMenuData.linkUrl()) |
690 act.triggered.connect( |
675 act.triggered.connect(functools.partial(self.__openLinkInNewPage, act)) |
691 functools.partial(self.__openLinkInNewPage, act)) |
676 |
692 |
677 act = menu.addAction( |
693 act = menu.addAction( |
678 UI.PixmapCache.getIcon("newWindow"), self.tr("Open Link in Background Page") |
694 UI.PixmapCache.getIcon("newWindow"), |
679 ) |
695 self.tr("Open Link in Background Page")) |
|
696 act.setData(contextMenuData.linkUrl()) |
680 act.setData(contextMenuData.linkUrl()) |
697 act.triggered.connect( |
681 act.triggered.connect(functools.partial(self.__openLinkInBackgroundPage, act)) |
698 functools.partial(self.__openLinkInBackgroundPage, act)) |
682 |
699 |
|
700 menu.addSeparator() |
683 menu.addSeparator() |
701 |
684 |
702 act = menu.addAction( |
685 act = menu.addAction( |
703 UI.PixmapCache.getIcon("editCopy"), |
686 UI.PixmapCache.getIcon("editCopy"), self.tr("Copy URL to Clipboard") |
704 self.tr("Copy URL to Clipboard")) |
687 ) |
705 act.setData(contextMenuData.linkUrl()) |
688 act.setData(contextMenuData.linkUrl()) |
706 act.triggered.connect( |
689 act.triggered.connect(functools.partial(self.__copyLink, act)) |
707 functools.partial(self.__copyLink, act)) |
690 |
708 |
|
709 def __openLinkInNewPage(self, act): |
691 def __openLinkInNewPage(self, act): |
710 """ |
692 """ |
711 Private method called by the context menu to open a link in a new page. |
693 Private method called by the context menu to open a link in a new page. |
712 |
694 |
713 @param act reference to the action that triggered |
695 @param act reference to the action that triggered |
714 @type QAction |
696 @type QAction |
715 """ |
697 """ |
716 url = act.data() |
698 url = act.data() |
717 if url.isEmpty(): |
699 if url.isEmpty(): |
718 return |
700 return |
719 |
701 |
720 self.__helpViewerWidget.openUrlNewPage(url) |
702 self.__helpViewerWidget.openUrlNewPage(url) |
721 |
703 |
722 def __openLinkInBackgroundPage(self, act): |
704 def __openLinkInBackgroundPage(self, act): |
723 """ |
705 """ |
724 Private method called by the context menu to open a link in a |
706 Private method called by the context menu to open a link in a |
725 background page. |
707 background page. |
726 |
708 |
727 @param act reference to the action that triggered |
709 @param act reference to the action that triggered |
728 @type QAction |
710 @type QAction |
729 """ |
711 """ |
730 url = act.data() |
712 url = act.data() |
731 if url.isEmpty(): |
713 if url.isEmpty(): |
732 return |
714 return |
733 |
715 |
734 self.__helpViewerWidget.openUrlNewBackgroundPage(url) |
716 self.__helpViewerWidget.openUrlNewBackgroundPage(url) |
735 |
717 |
736 def __bookmarkPage(self, act): |
718 def __bookmarkPage(self, act): |
737 """ |
719 """ |
738 Private method called by the context menu to bookmark the page. |
720 Private method called by the context menu to bookmark the page. |
739 |
721 |
740 @param act reference to the action that triggered |
722 @param act reference to the action that triggered |
741 @type QAction |
723 @type QAction |
742 """ |
724 """ |
743 data = act.data() |
725 data = act.data() |
744 if data: |
726 if data: |
745 with contextlib.suppress(KeyError): |
727 with contextlib.suppress(KeyError): |
746 url = data["url"] |
728 url = data["url"] |
747 title = data["title"] |
729 title = data["title"] |
748 |
730 |
749 self.__helpViewerWidget.bookmarkPage(title, url) |
731 self.__helpViewerWidget.bookmarkPage(title, url) |
750 |
732 |
751 def __copyLink(self, act): |
733 def __copyLink(self, act): |
752 """ |
734 """ |
753 Private method called by the context menu to copy a link to the |
735 Private method called by the context menu to copy a link to the |
754 clipboard. |
736 clipboard. |
755 |
737 |
756 @param act reference to the action that triggered |
738 @param act reference to the action that triggered |
757 @type QAction |
739 @type QAction |
758 """ |
740 """ |
759 data = act.data() |
741 data = act.data() |
760 if isinstance(data, QUrl) and data.isEmpty(): |
742 if isinstance(data, QUrl) and data.isEmpty(): |
761 return |
743 return |
762 |
744 |
763 if isinstance(data, QUrl): |
745 if isinstance(data, QUrl): |
764 data = data.toString() |
746 data = data.toString() |
765 |
747 |
766 # copy the URL to both clipboard areas |
748 # copy the URL to both clipboard areas |
767 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard) |
749 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard) |
768 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection) |
750 QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection) |
769 |
751 |
770 def __copyText(self): |
752 def __copyText(self): |
771 """ |
753 """ |
772 Private method called by the context menu to copy selected text to the |
754 Private method called by the context menu to copy selected text to the |
773 clipboard. |
755 clipboard. |
774 """ |
756 """ |
775 self.triggerPageAction(QWebEnginePage.WebAction.Copy) |
757 self.triggerPageAction(QWebEnginePage.WebAction.Copy) |
776 |
758 |
777 def __selectAll(self): |
759 def __selectAll(self): |
778 """ |
760 """ |
779 Private method called by the context menu to select all text. |
761 Private method called by the context menu to select all text. |
780 """ |
762 """ |
781 self.triggerPageAction(QWebEnginePage.WebAction.SelectAll) |
763 self.triggerPageAction(QWebEnginePage.WebAction.SelectAll) |
782 |
764 |
783 def __closePage(self): |
765 def __closePage(self): |
784 """ |
766 """ |
785 Private method called by the context menu to close the current page. |
767 Private method called by the context menu to close the current page. |
786 """ |
768 """ |
787 self.__helpViewerWidget.closeCurrentPage() |
769 self.__helpViewerWidget.closeCurrentPage() |
788 |
770 |
789 def __closeOtherPages(self): |
771 def __closeOtherPages(self): |
790 """ |
772 """ |
791 Private method called by the context menu to close all other pages. |
773 Private method called by the context menu to close all other pages. |
792 """ |
774 """ |
793 self.__helpViewerWidget.closeOtherPages() |
775 self.__helpViewerWidget.closeOtherPages() |