Continued implementing the embedded help viewer widget. Added context menus to the help viewer variants. eric7

Sun, 17 Oct 2021 15:26:31 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 17 Oct 2021 15:26:31 +0200
branch
eric7
changeset 8696
7e88f292b1b1
parent 8695
8a5179bced9e
child 8697
936662560d04

Continued implementing the embedded help viewer widget. Added context menus to the help viewer variants.

eric7.epj file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerImpl.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerImplQTB.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerImplQWE.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerImpl_qtb.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerImpl_qwe.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/HelpViewerWidget.py file | annotate | diff | comparison | revisions
eric7/HelpViewer/OpenPagesWidget.py file | annotate | diff | comparison | revisions
--- a/eric7.epj	Sun Oct 17 15:26:01 2021 +0200
+++ b/eric7.epj	Sun Oct 17 15:26:31 2021 +0200
@@ -2284,8 +2284,8 @@
       "eric7/HelpViewer/HelpViewerWidget.py",
       "eric7/HelpViewer/OpenPagesWidget.py",
       "eric7/HelpViewer/HelpViewerImpl.py",
-      "eric7/HelpViewer/HelpViewerImpl_qtb.py",
-      "eric7/HelpViewer/HelpViewerImpl_qwe.py"
+      "eric7/HelpViewer/HelpViewerImplQTB.py",
+      "eric7/HelpViewer/HelpViewerImplQWE.py"
     ],
     "SPELLEXCLUDES": "Dictionaries/excludes.dic",
     "SPELLLANGUAGE": "en_US",
--- a/eric7/HelpViewer/HelpViewerImpl.py	Sun Oct 17 15:26:01 2021 +0200
+++ b/eric7/HelpViewer/HelpViewerImpl.py	Sun Oct 17 15:26:31 2021 +0200
@@ -7,23 +7,7 @@
 Module implementing the help viewer base class.
 """
 
-from PyQt6.QtCore import pyqtSignal, QCoreApplication
-
-AboutBlank = QCoreApplication.translate(
-    "HelpViewer",
-    "<html>"
-    "<head><title>about:blank</title></head>"
-    "<body></body>"
-    "</html>")
-
-PageNotFound = QCoreApplication.translate(
-    "HelpViewer",
-    """<html>"""
-    """<head><title>Error 404...</title></head>"""
-    """<body><div align="center"><br><br>"""
-    """<h1>The page could not be found</h1><br>"""
-    """<h3>'{0}'</h3></div></body>"""
-    """</html>""")
+from PyQt6.QtCore import pyqtSignal, QUrl
 
 
 class HelpViewerImpl:
@@ -32,6 +16,10 @@
     
     This is the base class of help viewer implementations and defines the
     interface. Als subclasses must implement the these methods.
+    
+    @signal titleChanged() emitted to indicate a change of the page title
+    @signal loadFinished(ok) emitted to indicate the completion of a page load
+    @signal zoomChanged() emitted to indicate a change of the zoom level
     """
     titleChanged = pyqtSignal()
     loadFinished = pyqtSignal(bool)
@@ -46,7 +34,7 @@
         """
         self._engine = engine
     
-    def setUrl(self, url):
+    def setLink(self, url):
         """
         Public method to set the URL of the document to be shown.
         
@@ -56,53 +44,18 @@
         """
         raise RuntimeError("Not implemented")
     
-    def url(self):
+    def link(self):
         """
         Public method to get the URL of the shown document.
         
-        @return url URL of the document
+        @return URL of the document
         @rtype QUrl
         @exception RuntimeError raised when not implemented
         """
         raise RuntimeError("Not implemented")
-        return None
+        return QUrl()
     
-    def getData(self, url):
-        """
-        Public method to get the data to be shown.
-        
-        @param url URL to be loaded
-        @type QUrl
-        @return data to be shown
-        @rtype str
-        """
-        scheme = url.scheme()
-        if scheme == "about":
-            if url.toString() == "about:blank":
-                return AboutBlank
-            else:
-                return PageNotFound.format(url.toString())
-        elif scheme in ("file", ""):
-            filePath = url.toLocalFile()
-            try:
-                with open(filePath, "r", encoding="utf-8") as f:
-                    htmlText = f.read()
-                return htmlText
-            except OSError:
-                return PageNotFound.format(url.toString())
-        elif scheme == "qthelp":
-            if self._engine.findFile(url).isValid():
-                data = bytes(self._engine.fileData(url)).decode("utf-8")
-                if not data:
-                    data = PageNotFound.format(url.toString())
-                return data
-            else:
-                return PageNotFound.format(url.toString())
-        else:
-            # None is an indicator that we cannot handle the request
-            return None
-    
-    def title(self):
+    def pageTitle(self):
         """
         Public method get the page title.
         
@@ -128,6 +81,8 @@
         Public method to check, if stepping backward through the history is
         available.
         
+        @return flag indicating backward stepping is available
+        @rtype bool
         @exception RuntimeError raised when not implemented
         """
         raise RuntimeError("Not implemented")
@@ -138,6 +93,8 @@
         Public method to check, if stepping forward through the history is
         available.
         
+        @return flag indicating forward stepping is available
+        @rtype bool
         @exception RuntimeError raised when not implemented
         """
         raise RuntimeError("Not implemented")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/HelpViewer/HelpViewerImplQTB.py	Sun Oct 17 15:26:31 2021 +0200
@@ -0,0 +1,615 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the QTextBrowser based help viewer class.
+"""
+
+import functools
+
+from PyQt6.QtCore import (
+    pyqtSlot, Qt, QByteArray, QUrl, QEvent, QCoreApplication, QPoint
+)
+from PyQt6.QtGui import QDesktopServices, QImage, QGuiApplication, QClipboard
+from PyQt6.QtWidgets import QTextBrowser, QMenu
+
+from .HelpViewerImpl import HelpViewerImpl
+
+import UI.PixmapCache
+
+
+AboutBlank = QCoreApplication.translate(
+    "HelpViewer",
+    "<html>"
+    "<head><title>about:blank</title></head>"
+    "<body></body>"
+    "</html>")
+
+PageNotFound = QCoreApplication.translate(
+    "HelpViewer",
+    """<html>"""
+    """<head><title>Error 404...</title></head>"""
+    """<body><div align="center"><br><br>"""
+    """<h1>The page could not be found</h1><br>"""
+    """<h3>'{0}'</h3></div></body>"""
+    """</html>""")
+
+
+class HelpViewerImplQTB(HelpViewerImpl, QTextBrowser):
+    """
+    Class implementing the QTextBrowser based help viewer class.
+    """
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine
+        @type QHelpEngine
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        QTextBrowser.__init__(self, parent=parent)
+        HelpViewerImpl.__init__(self, engine)
+        
+        self.__helpViewerWidget = parent
+        
+        self.__zoomCount = 0
+        
+        self.__menu = QMenu(self)
+        self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__showContextMenu)
+        
+        self.sourceChanged.connect(self.titleChanged)
+        
+        self.grabGesture(Qt.GestureType.PinchGesture)
+    
+    def setLink(self, url):
+        """
+        Public method to set the URL of the document to be shown.
+        
+        @param url source of the document
+        @type QUrl
+        """
+        self.setSource(url)
+    
+    def link(self):
+        """
+        Public method to get the URL of the shown document.
+        
+        @return URL of the document
+        @rtype QUrl
+        """
+        return self.source()
+    
+    def doSetSource(self, url, type_):
+        """
+        Public method to load the data and show it.
+        
+        @param url URL of resource to load
+        @type QUrl
+        @param type_ type of the resource to load
+        @type QTextDocument.ResourceType
+        """
+        data = self.__getData(url)
+        if data is None:
+            QDesktopServices.openUrl(url)
+            return
+        
+        if url != QUrl("about:blank"):
+            super().doSetSource(url, type)
+        
+        self.setHtml(data)
+        self.sourceChanged.emit(url)
+        self.loadFinished.emit(True)
+    
+    def __getData(self, url):
+        """
+        Private method to get the data to be shown.
+        
+        @param url URL to be loaded
+        @type QUrl
+        @return data to be shown
+        @rtype str
+        """
+        scheme = url.scheme()
+        if scheme == "about":
+            if url.toString() == "about:blank":
+                return AboutBlank
+            else:
+                return PageNotFound.format(url.toString())
+        elif scheme in ("file", ""):
+            filePath = url.toLocalFile()
+            try:
+                with open(filePath, "r", encoding="utf-8") as f:
+                    htmlText = f.read()
+                return htmlText
+            except OSError:
+                return PageNotFound.format(url.toString())
+        elif scheme == "qthelp":
+            if self._engine.findFile(url).isValid():
+                data = bytes(self._engine.fileData(url)).decode("utf-8")
+                if not data:
+                    data = PageNotFound.format(url.toString())
+                return data
+            else:
+                return PageNotFound.format(url.toString())
+        else:
+            # None is an indicator that we cannot handle the request
+            return None
+    
+    def pageTitle(self):
+        """
+        Public method get the page title.
+        
+        @return page title
+        @rtype str
+        """
+        titleStr = self.documentTitle()
+        if not titleStr:
+            url = self.link()
+            
+            titleStr = url.host()
+            if not titleStr:
+                titleStr = url.toString(
+                    QUrl.UrlFormattingOption.RemoveFragment)
+        
+        if not titleStr or titleStr == "about:blank":
+            titleStr = self.tr("Empty Page")
+        
+        return titleStr
+    
+    def loadResource(self, type_, name):
+        """
+        Public method to load data of the specified type from the resource with
+        the given name.
+        
+        @param type_ resource type
+        @type int
+        @param name resource name
+        @type QUrl
+        @return byte array containing the loaded data
+        @rtype QByteArray
+        """
+        ba = QByteArray()
+        
+        if type_ < 4:     # QTextDocument.ResourceType.MarkdownResource
+            # TODO: change to use getData()
+            url = self._engine.findFile(name)
+            ba = self._engine.fileData(url)
+            if url.toString().lower().endswith(".svg"):
+                image = QImage()
+                image.loadFromData(ba, "svg")
+                if not image.isNull():
+                    return image
+        
+        return ba
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        if evt.button() == Qt.MouseButton.XButton1:
+            self.backward()
+            evt.accept()
+        elif evt.button() == Qt.MouseButton.XButton2:
+            self.forward()
+            evt.accept()
+        else:
+            super().mousePressEvent(evt)
+    
+    def mouseReleaseEvent(self, evt):
+        """
+        Protected method called by a mouse release event.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        hasModifier = evt.modifiers() != Qt.KeyboardModifier.NoModifier
+        if evt.button() == Qt.MouseButton.LeftButton and hasModifier:
+            
+            anchor = self.anchorAt(evt.pos())
+            if anchor:
+                url = self.link().resolved(QUrl(anchor))
+                if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
+                    self.__helpViewerWidget.openUrlNewBackgroundPage(url)
+                else:
+                    self.__helpViewerWidget.openUrlNewPage(url)
+                evt.accept()
+        else:
+            super().mousePressEvent(evt)
+    
+    def gotoHistory(self, index):
+        """
+        Public method to step through the history.
+        
+        @param index history index (<0 backward, >0 forward)
+        @type int
+        """
+        if index < 0:
+            # backward
+            for _ind in range(-index):
+                self.backward()
+        else:
+            # forward
+            for _ind in range(index):
+                self.forward()
+    
+    def isBackwardAvailable(self):
+        """
+        Public method to check, if stepping backward through the history is
+        available.
+        
+        @return flag indicating backward stepping is available
+        @rtype bool
+        """
+        return QTextBrowser.isBackwardAvailable(self)
+    
+    def isForwardAvailable(self):
+        """
+        Public method to check, if stepping forward through the history is
+        available.
+        
+        @return flag indicating forward stepping is available
+        @rtype bool
+        """
+        return QTextBrowser.isForwardAvailable(self)
+    
+    def scaleUp(self):
+        """
+        Public method to zoom in.
+        """
+        if self.__zoomCount < 10:
+            self.__zoomCount += 1
+            self.zoomIn()
+            self.zoomChanged.emit()
+    
+    def scaleDown(self):
+        """
+        Public method to zoom out.
+        """
+        if self.__zoomCount > -5:
+            self.__zoomCount -= 1
+            self.zoomOut()
+            self.zoomChanged.emit()
+    
+    def setScale(self, scale):
+        """
+        Public method to set the zoom level.
+        
+        @param scale zoom level to set
+        @type int
+        """
+        if -5 <= scale <= 10:
+            self.zoomOut(scale)
+            self.__zoomCount = scale
+            self.zoomChanged.emit()
+    
+    def resetScale(self):
+        """
+        Public method to reset the zoom level.
+        """
+        if self.__zoomCount != 0:
+            self.zoomOut(self.__zoomCount)
+            self.zoomChanged.emit()
+        self.__zoomCount = 0
+    
+    def scale(self):
+        """
+        Public method to get the zoom level.
+        
+        @return current zoom level
+        @rtype int
+        """
+        return self.__zoomCount
+    
+    def isScaleUpAvailable(self):
+        """
+        Public method to check, if the max. zoom level is reached.
+        
+        @return flag indicating scale up is available
+        @rtype bool
+        """
+        return self.__zoomCount < 10
+    
+    def isScaleDownAvailable(self):
+        """
+        Public method to check, if the min. zoom level is reached.
+        
+        @return flag indicating scale down is available
+        @rtype bool
+        """
+        return self.__zoomCount > -5
+    
+    def wheelEvent(self, evt):
+        """
+        Protected method to handle wheel event to zoom.
+        
+        @param evt reference to the event object
+        @type QWheelEvent
+        """
+        delta = evt.angleDelta().y()
+        if evt.modifiers() == Qt.KeyboardModifier.ControlModifier:
+            if delta > 0:
+                self.scaleUp()
+            else:
+                self.scaleDown()
+            evt.accept()
+        
+        elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier:
+            if delta < 0:
+                self.backward()
+            elif delta > 0:
+                self.forward()
+            evt.accept()
+        
+        else:
+            QTextBrowser.wheelEvent(self, evt)
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key press events.
+        
+        @param evt reference to the key event
+        @type QKeyEvent
+        """
+        key = evt.key()
+        isControlModifier = bool(
+            evt.modifiers() & Qt.KeyboardModifier.ControlModifier)
+        
+        if (
+            key == Qt.Key.Key_ZoomIn or
+            (key == Qt.Key.Key_Plus and isControlModifier)
+        ):
+            self.scaleUp()
+            evt.accept()
+        elif (
+            key == Qt.Key.Key_ZoomOut or
+            (key == Qt.Key.Key_Minus and isControlModifier)
+        ):
+            self.scaleDown()
+            evt.accept()
+        elif key == Qt.Key.Key_0 and isControlModifier:
+            self.resetScale()
+            evt.accept()
+        elif (
+            key == Qt.Key.Key_Backspace or
+            (key == Qt.Key.Key_Left and isControlModifier)
+        ):
+            self.backward()
+            evt.accept()
+        elif key == Qt.Key.Key_Right and isControlModifier:
+            self.forward()
+            evt.accept()
+        else:
+            super().keyPressEvent(evt)
+    
+    def event(self, evt):
+        """
+        Public method handling events.
+        
+        @param evt reference to the event
+        @type QEvent
+        @return flag indicating the event was handled
+        @rtype bool
+        """
+        if evt.type() == QEvent.Type.Gesture:
+            self.gestureEvent(evt)
+            return True
+        
+        return super().event(evt)
+    
+    def gestureEvent(self, evt):
+        """
+        Protected method handling gesture events.
+        
+        @param evt reference to the gesture event
+        @type QGestureEvent
+        """
+        pinch = evt.gesture(Qt.GestureType.PinchGesture)
+        if pinch:
+            if pinch.state() == Qt.GestureState.GestureStarted:
+                zoom = (self.getZoom() + 6) / 10.0
+                pinch.setTotalScaleFactor(zoom)
+            elif pinch.state() == Qt.GestureState.GestureUpdated:
+                zoom = int(pinch.totalScaleFactor() * 10) - 6
+                if zoom <= -5:
+                    zoom = -5
+                    pinch.setTotalScaleFactor(0.1)
+                elif zoom >= 10:
+                    zoom = 10
+                    pinch.setTotalScaleFactor(1.6)
+                self.setScale(zoom)
+            evt.accept()
+    
+    # TODO: add Ctrl+LMB action (open link in new page)
+    
+    #######################################################################
+    ## Context menu related methods below
+    #######################################################################
+    
+    @pyqtSlot(QPoint)
+    def __showContextMenu(self, pos):
+        """
+        Private slot to show the context menu.
+        
+        @param pos position to show the context menu at
+        @type QPoint
+        """
+        self.__menu.clear()
+        anchor = self.anchorAt(pos)
+        linkUrl = self.link().resolved(QUrl(anchor)) if anchor else QUrl()
+        selectedText = self.textCursor().selectedText()
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("back"),
+            self.tr("Backward"),
+            self.backward)
+        act.setEnabled(self.isBackwardAvailable())
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("forward"),
+            self.tr("Forward"),
+            self.forward)
+        act.setEnabled(self.isForwardAvailable())
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("reload"),
+            self.tr("Reload"),
+            self.reload)
+        
+        if not linkUrl.isEmpty() and linkUrl.scheme() != "javascript":
+            self.__createLinkContextMenu(self.__menu, linkUrl)
+        
+        self.__menu.addSeparator()
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy Page URL to Clipboard"))
+        act.setData(self.link())
+        act.triggered.connect(
+            functools.partial(self.__copyLink, act))
+        
+        self.__menu.addSeparator()
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("zoomIn"),
+            self.tr("Zoom in"),
+            self.scaleUp)
+        act.setEnabled(self.isScaleUpAvailable())
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("zoomOut"),
+            self.tr("Zoom out"),
+            self.scaleDown)
+        act.setEnabled(self.isScaleDownAvailable())
+        
+        self.__menu.addAction(
+            UI.PixmapCache.getIcon("zoomReset"),
+            self.tr("Zoom reset"),
+            self.resetScale)
+        
+        self.__menu.addSeparator()
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy"),
+            self.copy)
+        act.setEnabled(bool(selectedText))
+        
+        self.__menu.addAction(
+            UI.PixmapCache.getIcon("editSelectAll"),
+            self.tr("Select All"),
+            self.selectAll)
+        
+        self.__menu.addSeparator()
+        
+        self.__menu.addAction(
+            UI.PixmapCache.getIcon("tabClose"),
+            self.tr('Close'),
+            self.__closePage)
+        
+        act = self.__menu.addAction(
+            UI.PixmapCache.getIcon("tabCloseOther"),
+            self.tr("Close Others"),
+            self.__closeOtherPages)
+        act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1)
+        
+        self.__menu.popup(self.mapToGlobal(pos))
+    
+    def __createLinkContextMenu(self, menu, linkUrl):
+        """
+        Private method to populate the context menu for URLs.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param linkUrl URL to create the menu part for
+        @type QUrl
+        """
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("openNewTab"),
+            self.tr("Open Link in New Page"))
+        act.setData(linkUrl)
+        act.triggered.connect(
+            functools.partial(self.__openLinkInNewPage, act))
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("newWindow"),
+            self.tr("Open Link in Background Page"))
+        act.setData(linkUrl)
+        act.triggered.connect(
+            functools.partial(self.__openLinkInBackgroundPage, act))
+        
+        menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy URL to Clipboard"))
+        act.setData(linkUrl)
+        act.triggered.connect(
+            functools.partial(self.__copyLink, act))
+    
+    def __openLinkInNewPage(self, act):
+        """
+        Private method called by the context menu to open a link in a new page.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__helpViewerWidget.openUrlNewPage(url)
+    
+    def __openLinkInBackgroundPage(self, act):
+        """
+        Private method called by the context menu to open a link in a
+        background page.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__helpViewerWidget.openUrlNewBackgroundPage(url)
+    
+    def __copyLink(self, act):
+        """
+        Private method called by the context menu to copy a link to the
+        clipboard.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        
+        # copy the URL to both clipboard areas
+        QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard)
+        QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection)
+    
+    def __closePage(self):
+        """
+        Private method called by the context menu to close the current page.
+        """
+        self.__helpViewerWidget.closeCurrentPage()
+    
+    def __closeOtherPages(self):
+        """
+        Private method called by the context menu to close all other pages.
+        """
+        self.__helpViewerWidget.closeOtherPages()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/HelpViewer/HelpViewerImplQWE.py	Sun Oct 17 15:26:31 2021 +0200
@@ -0,0 +1,739 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the help viewer base class.
+"""
+
+import functools
+
+from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QTimer, QUrl, QPoint
+from PyQt6.QtGui import QGuiApplication, QClipboard, QContextMenuEvent
+from PyQt6.QtWidgets import QMenu
+from PyQt6.QtWebEngineWidgets import QWebEngineView
+from PyQt6.QtWebEngineCore import QWebEnginePage, QWebEngineNewWindowRequest
+
+from .HelpViewerWidget import HelpViewerWidget
+from .HelpViewerImpl import HelpViewerImpl
+
+import UI.PixmapCache
+
+
+class HelpViewerImplQWE(HelpViewerImpl, QWebEngineView):
+    """
+    Class implementing the QTextBrowser based help viewer class.
+    """
+    ZoomLevels = [
+        30, 40, 50, 67, 80, 90,
+        100,
+        110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300,
+    ]
+    ZoomLevelDefault = 100
+    
+    def __init__(self, engine, parent=None):
+        """
+        Constructor
+        
+        @param engine reference to the help engine
+        @type QHelpEngine
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        QWebEngineView.__init__(self, parent=parent)
+        HelpViewerImpl.__init__(self, engine)
+        
+        self.__helpViewerWidget = parent
+        
+        self.__rwhvqt = None
+        self.installEventFilter(self)
+        
+        self.__page = None
+        self.__createNewPage()
+        
+        self.__currentScale = 100
+        
+        self.__menu = QMenu(self)
+    
+    def __createNewPage(self):
+        """
+        Private method to create a new page object.
+        """
+        self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile())
+        self.setPage(self.__page)
+        
+        self.__page.titleChanged.connect(self.__titleChanged)
+        self.__page.urlChanged.connect(self.__titleChanged)
+        self.__page.newWindowRequested.connect(self.__newWindowRequested)
+    
+    def __newWindowRequested(self, request):
+        """
+        Private slot handling new window requests of the web page.
+        
+        @param request reference to the new window request
+        @type QWebEngineNewWindowRequest
+        """
+        background = (
+            request.destination() ==
+            QWebEngineNewWindowRequest.DestinationType.InNewBackgroundTab
+        )
+        newViewer = self.__helpViewerWidget.addPage(background=background)
+        request.openIn(newViewer.page())
+    
+    def __setRwhvqt(self):
+        """
+        Private slot to set widget that receives input events.
+        """
+        self.grabGesture(Qt.GestureType.PinchGesture)
+        self.__rwhvqt = self.focusProxy()
+        if self.__rwhvqt:
+            self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture)
+            self.__rwhvqt.installEventFilter(self)
+        else:
+            print("Focus proxy is null!")   # __IGNORE_WARNING_M801__
+    
+    def setLink(self, url):
+        """
+        Public method to set the URL of the document to be shown.
+        
+        @param url URL of the document
+        @type QUrl
+        """
+        super().setUrl(url)
+    
+    def link(self):
+        """
+        Public method to get the URL of the shown document.
+        
+        @return url URL of the document
+        @rtype QUrl
+        """
+        return super().url()
+    
+    @pyqtSlot()
+    def __titleChanged(self):
+        """
+        Private method to handle a change of the web page title.
+        """
+        super().titleChanged.emit()
+    
+    def pageTitle(self):
+        """
+        Public method get the page title.
+        
+        @return page title
+        @rtype str
+        """
+        titleStr = super().title()
+        if not titleStr:
+            if self.link().isEmpty():
+                url = self.__page.requestedUrl()
+            else:
+                url = self.link()
+            
+            titleStr = url.host()
+            if not titleStr:
+                titleStr = url.toString(
+                    QUrl.UrlFormattingOption.RemoveFragment)
+        
+        if not titleStr or titleStr == "about:blank":
+            titleStr = self.tr("Empty Page")
+        
+        return titleStr
+    
+    #######################################################################
+    ## History related methods below
+    #######################################################################
+    
+    def isBackwardAvailable(self):
+        """
+        Public method to check, if stepping backward through the history is
+        available.
+        
+        @return flag indicating backward stepping is available
+        @rtype bool
+        """
+        return self.history().canGoBack()
+    
+    def isForwardAvailable(self):
+        """
+        Public method to check, if stepping forward through the history is
+        available.
+        
+        @return flag indicating forward stepping is available
+        @rtype bool
+        """
+        return self.history().canGoForward()
+    
+    def backward(self):
+        """
+        Public slot to move backwards in history.
+        """
+        self.triggerPageAction(QWebEnginePage.WebAction.Back)
+    
+    def forward(self):
+        """
+        Public slot to move forward in history.
+        """
+        self.triggerPageAction(QWebEnginePage.WebAction.Forward)
+    
+    def reload(self):
+        """
+        Public slot to reload the current page.
+        """
+        self.triggerPageAction(QWebEnginePage.WebAction.Reload)
+    
+    def backwardHistoryCount(self):
+        """
+        Public method to get the number of available back history items.
+        
+        Note: For performance reasons this is limited to the maximum number of
+        history items the help viewer is interested in.
+        
+        @return count of available back history items
+        @rtype int
+        """
+        history = self.history()
+        return len(history.backItems(HelpViewerWidget.MaxHistoryItems))
+    
+    def forwardHistoryCount(self):
+        """
+        Public method to get the number of available forward history items.
+        
+        Note: For performance reasons this is limited to the maximum number of
+        history items the help viewer is interested in.
+        
+        @return count of available forward history items
+        @rtype int
+        """
+        history = self.history()
+        return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems))
+    
+    def historyTitle(self, offset):
+        """
+        Public method to get the title of a history item.
+        
+        @param offset offset of the item with respect to the current page
+        @type int
+        @return title of the requeted item in history
+        @rtype str
+        """
+        history = self.history()
+        currentIndex = history.currentItemIndex()
+        itm = self.history().itemAt(currentIndex + offset)
+        return itm.title()
+    
+    def gotoHistory(self, offset):
+        """
+        Public method to go to a history item.
+        
+        @param offset offset of the item with respect to the current page
+        @type int
+        """
+        history = self.history()
+        currentIndex = history.currentItemIndex()
+        itm = self.history().itemAt(currentIndex + offset)
+        history.goToItem(itm)
+    
+    def clearHistory(self):
+        """
+        Public method to clear the history.
+        """
+        self.history().clear()
+    
+    #######################################################################
+    ## Zoom related methods below
+    #######################################################################
+    
+    def __levelForScale(self, scale):
+        """
+        Private method determining the zoom level index given a zoom factor.
+        
+        @param scale zoom factor
+        @type int
+        @return index of zoom factor
+        @rtype int
+        """
+        try:
+            index = self.ZoomLevels.index(scale)
+        except ValueError:
+            for _index in range(len(self.ZoomLevels)):
+                if scale <= self.ZoomLevels[scale]:
+                    break
+        return index
+    
+    def scaleUp(self):
+        """
+        Public method to zoom in.
+        """
+        index = self.__levelForScale(self.__currentScale)
+        if index < len(self.ZoomLevels) - 1:
+            self.setScale(self.ZoomLevels[index + 1])
+    
+    def scaleDown(self):
+        """
+        Public method to zoom out.
+        """
+        index = self.__levelForScale(self.__currentScale)
+        if index > 0:
+            self.setScale(self.ZoomLevels[index - 1])
+    
+    def setScale(self, scale):
+        """
+        Public method to set the zoom level.
+        
+        @param scale zoom level to set
+        @type int
+        """
+        if scale != self.__currentScale:
+            self.setZoomFactor(scale / 100.0)
+            self.__currentScale = scale
+            self.zoomChanged.emit()
+    
+    def resetScale(self):
+        """
+        Public method to reset the zoom level.
+        """
+        index = self.__levelForScale(self.ZoomLevelDefault)
+        self.setScale(self.ZoomLevels[index])
+    
+    def scale(self):
+        """
+        Public method to get the zoom level.
+        
+        @return current zoom level
+        @rtype int
+        """
+        return self.__currentScale
+    
+    def isScaleUpAvailable(self):
+        """
+        Public method to check, if the max. zoom level is reached.
+        
+        @return flag indicating scale up is available
+        @rtype bool
+        """
+        index = self.__levelForScale(self.__currentScale)
+        return index < len(self.ZoomLevels) - 1
+    
+    def isScaleDownAvailable(self):
+        """
+        Public method to check, if the min. zoom level is reached.
+        
+        @return flag indicating scale down is available
+        @rtype bool
+        """
+        index = self.__levelForScale(self.__currentScale)
+        return index > 0
+    
+    #######################################################################
+    ## Event handlers below
+    #######################################################################
+    
+    def eventFilter(self, obj, evt):
+        """
+        Public method to process event for other objects.
+        
+        @param obj reference to object to process events for
+        @type QObject
+        @param evt reference to event to be processed
+        @type QEvent
+        @return flag indicating that the event should be filtered out
+        @rtype bool
+        """
+        if (
+            obj is self and
+            evt.type() == QEvent.Type.ParentChange and
+            self.parentWidget() is not None
+        ):
+            self.parentWidget().installEventFilter(self)
+        
+        # find the render widget receiving events for the web page
+        if obj is self and evt.type() == QEvent.Type.ChildAdded:
+            QTimer.singleShot(0, self.__setRwhvqt)
+        
+        # forward events to WebBrowserView
+        if (
+            obj is self.__rwhvqt and
+            evt.type() in [QEvent.Type.KeyPress,
+                           QEvent.Type.MouseButtonRelease,
+                           QEvent.Type.Wheel,
+                           QEvent.Type.Gesture]
+        ):
+            wasAccepted = evt.isAccepted()
+            evt.setAccepted(False)
+            if evt.type() == QEvent.Type.KeyPress:
+                self._keyPressEvent(evt)
+            elif evt.type() == QEvent.Type.MouseButtonRelease:
+                self._mouseReleaseEvent(evt)
+            elif evt.type() == QEvent.Type.Wheel:
+                self._wheelEvent(evt)
+            elif evt.type() == QEvent.Type.Gesture:
+                self._gestureEvent(evt)
+            ret = evt.isAccepted()
+            evt.setAccepted(wasAccepted)
+            return ret
+        
+        if (
+            obj is self.parentWidget() and
+            evt.type() in [QEvent.Type.KeyPress, QEvent.Type.KeyRelease]
+        ):
+            wasAccepted = evt.isAccepted()
+            evt.setAccepted(False)
+            if evt.type() == QEvent.Type.KeyPress:
+                self._keyPressEvent(evt)
+            ret = evt.isAccepted()
+            evt.setAccepted(wasAccepted)
+            return ret
+        
+        # block already handled events
+        if (
+            obj is self and
+            evt.type() in [QEvent.Type.KeyPress,
+                           QEvent.Type.MouseButtonRelease,
+                           QEvent.Type.Wheel,
+                           QEvent.Type.Gesture]
+        ):
+            return True
+        
+        return super().eventFilter(obj, evt)
+    
+    def _keyPressEvent(self, evt):
+        """
+        Protected method called by a key press.
+        
+        @param evt reference to the key event
+        @type QKeyEvent
+        """
+        key = evt.key()
+        isControlModifier = bool(
+            evt.modifiers() & Qt.KeyboardModifier.ControlModifier)
+        
+        if (
+            key == Qt.Key.Key_ZoomIn or
+            (key == Qt.Key.Key_Plus and isControlModifier)
+        ):
+            self.scaleUp()
+            evt.accept()
+        elif (
+            key == Qt.Key.Key_ZoomOut or
+            (key == Qt.Key.Key_Minus and isControlModifier)
+        ):
+            self.scaleDown()
+            evt.accept()
+        elif key == Qt.Key.Key_0 and isControlModifier:
+            self.resetScale()
+            evt.accept()
+        elif (
+            key == Qt.Key.Key_Backspace or
+            (key == Qt.Key.Key_Left and isControlModifier)
+        ):
+            self.backward()
+            evt.accept()
+        elif key == Qt.Key.Key_Right and isControlModifier:
+            self.forward()
+            evt.accept()
+    
+    def _mouseReleaseEvent(self, evt):
+        """
+        Protected method called by a mouse release event.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        accepted = evt.isAccepted()
+        self.__page.event(evt)
+        if (
+            not evt.isAccepted() and
+            evt.button() == Qt.MouseButton.MiddleButton
+        ):
+            url = QUrl(QGuiApplication.clipboard().text(
+                QClipboard.Mode.Selection))
+            if (
+                not url.isEmpty() and
+                url.isValid() and
+                url.scheme() != ""
+            ):
+                self.setLink(url)
+                accepted = True
+        evt.setAccepted(accepted)
+    
+    def _wheelEvent(self, evt):
+        """
+        Protected method to handle wheel events.
+        
+        @param evt reference to the wheel event
+        @type QWheelEvent
+        """
+        delta = evt.angleDelta().y()
+        if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
+            if delta < 0:
+                self.scaleDown()
+            elif delta > 0:
+                self.scaleUp()
+            evt.accept()
+        
+        elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier:
+            if delta < 0:
+                self.backward()
+            elif delta > 0:
+                self.forward()
+            evt.accept()
+    
+    def _gestureEvent(self, evt):
+        """
+        Protected method handling gesture events.
+        
+        @param evt reference to the gesture event
+        @type QGestureEvent
+        """
+        pinch = evt.gesture(Qt.GestureType.PinchGesture)
+        if pinch:
+            if pinch.state() == Qt.GestureState.GestureStarted:
+                pinch.setTotalScaleFactor(self.__currentScale / 100.0)
+            elif pinch.state() == Qt.GestureState.GestureUpdated:
+                scaleFactor = pinch.totalScaleFactor()
+                self.setScale(int(scaleFactor * 100))
+            evt.accept()
+    
+    def event(self, evt):
+        """
+        Public method handling events.
+        
+        @param evt reference to the event (QEvent)
+        @return flag indicating, if the event was handled (boolean)
+        """
+        if evt.type() == QEvent.Type.Gesture:
+            self._gestureEvent(evt)
+            return True
+        
+        return super().event(evt)
+    
+    #######################################################################
+    ## Context menu related methods below
+    #######################################################################
+    
+    def contextMenuEvent(self, evt):
+        """
+        Protected method called to create a context menu.
+        
+        This method is overridden from QWebEngineView.
+        
+        @param evt reference to the context menu event object
+        @type QContextMenuEvent
+        """
+        pos = evt.pos()
+        reason = evt.reason()
+        QTimer.singleShot(
+            0,
+            lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)))
+        # needs to be done this way because contextMenuEvent is blocking
+        # the main loop
+    
+    def _contextMenuEvent(self, evt):
+        """
+        Protected method called to create a context menu.
+        
+        @param evt reference to the context menu event object
+            (QContextMenuEvent)
+        """
+        self.__menu.clear()
+        
+        self.__createContextMenu(self.__menu)
+        
+        if not self.__menu.isEmpty():
+            pos = evt.globalPos()
+            self.__menu.popup(QPoint(pos.x(), pos.y() + 1))
+    
+    def __createContextMenu(self, menu):
+        """
+        Private method to populate the context menu.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        """
+        contextMenuData = self.lastContextMenuRequest()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("back"),
+            self.tr("Backward"),
+            self.backward)
+        act.setEnabled(self.isBackwardAvailable())
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("forward"),
+            self.tr("Forward"),
+            self.forward)
+        act.setEnabled(self.isForwardAvailable())
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("reload"),
+            self.tr("Reload"),
+            self.reload)
+        
+        if (
+            not contextMenuData.linkUrl().isEmpty() and
+            contextMenuData.linkUrl().scheme() != "javascript"
+        ):
+            self.__createLinkContextMenu(menu, contextMenuData)
+        
+        menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy Page URL to Clipboard"))
+        act.setData(self.link())
+        act.triggered.connect(
+            functools.partial(self.__copyLink, act))
+        
+        menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("zoomIn"),
+            self.tr("Zoom in"),
+            self.scaleUp)
+        act.setEnabled(self.isScaleUpAvailable())
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("zoomOut"),
+            self.tr("Zoom out"),
+            self.scaleDown)
+        act.setEnabled(self.isScaleDownAvailable())
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("zoomReset"),
+            self.tr("Zoom reset"),
+            self.resetScale)
+        
+        menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy"),
+            self.__copyText)
+        act.setEnabled(bool(contextMenuData.selectedText()))
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("editSelectAll"),
+            self.tr("Select All"),
+            self.__selectAll)
+        
+        menu.addSeparator()
+        
+        menu.addAction(
+            UI.PixmapCache.getIcon("tabClose"),
+            self.tr('Close'),
+            self.__closePage)
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("tabCloseOther"),
+            self.tr("Close Others"),
+            self.__closeOtherPages)
+        act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1)
+    
+    def __createLinkContextMenu(self, menu, contextMenuData):
+        """
+        Private method to populate the context menu for URLs.
+        
+        @param menu reference to the menu to be populated
+        @type QMenu
+        @param contextMenuData data of the last context menu request
+        @type QWebEngineContextMenuRequest
+        """
+        if not menu.isEmpty():
+            menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("openNewTab"),
+            self.tr("Open Link in New Page"))
+        act.setData(contextMenuData.linkUrl())
+        act.triggered.connect(
+            functools.partial(self.__openLinkInNewPage, act))
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("newWindow"),
+            self.tr("Open Link in Background Page"))
+        act.setData(contextMenuData.linkUrl())
+        act.triggered.connect(
+            functools.partial(self.__openLinkInBackgroundPage, act))
+        
+        menu.addSeparator()
+        
+        act = menu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy URL to Clipboard"))
+        act.setData(contextMenuData.linkUrl())
+        act.triggered.connect(
+            functools.partial(self.__copyLink, act))
+    
+    def __openLinkInNewPage(self, act):
+        """
+        Private method called by the context menu to open a link in a new page.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__helpViewerWidget.openUrlNewPage(url)
+    
+    def __openLinkInBackgroundPage(self, act):
+        """
+        Private method called by the context menu to open a link in a
+        background page.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        url = act.data()
+        if url.isEmpty():
+            return
+        
+        self.__helpViewerWidget.openUrlNewBackgroundPage(url)
+    
+    def __copyLink(self, act):
+        """
+        Private method called by the context menu to copy a link to the
+        clipboard.
+        
+        @param act reference to the action that triggered
+        @type QAction
+        """
+        data = act.data()
+        if isinstance(data, QUrl) and data.isEmpty():
+            return
+        
+        if isinstance(data, QUrl):
+            data = data.toString()
+        
+        # copy the URL to both clipboard areas
+        QGuiApplication.clipboard().setText(data, QClipboard.Mode.Clipboard)
+        QGuiApplication.clipboard().setText(data, QClipboard.Mode.Selection)
+    
+    def __copyText(self):
+        """
+        Private method called by the context menu to copy selected text to the
+        clipboard.
+        """
+        self.triggerPageAction(QWebEnginePage.WebAction.Copy)
+    
+    def __selectAll(self):
+        """
+        Private method called by the context menu to select all text.
+        """
+        self.triggerPageAction(QWebEnginePage.WebAction.SelectAll)
+    
+    def __closePage(self):
+        """
+        Private method called by the context menu to close the current page.
+        """
+        self.__helpViewerWidget.closeCurrentPage()
+    
+    def __closeOtherPages(self):
+        """
+        Private method called by the context menu to close all other pages.
+        """
+        self.__helpViewerWidget.closeOtherPages()
--- a/eric7/HelpViewer/HelpViewerImpl_qtb.py	Sun Oct 17 15:26:01 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,334 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing the QTextBrowser based help viewer class.
-"""
-
-from PyQt6.QtCore import Qt, QByteArray, QUrl, QEvent
-from PyQt6.QtGui import QDesktopServices,  QImage
-from PyQt6.QtWidgets import QTextBrowser
-
-from .HelpViewerImpl import HelpViewerImpl
-
-
-class HelpViewerImpl_qtb(HelpViewerImpl, QTextBrowser):
-    """
-    Class implementing the QTextBrowser based help viewer class.
-    """
-    def __init__(self, engine, parent=None):
-        """
-        Constructor
-        
-        @param engine reference to the help engine
-        @type QHelpEngine
-        @param parent reference to the parent widget
-        @type QWidget
-        """
-        QTextBrowser.__init__(self,  parent=parent)
-        HelpViewerImpl.__init__(self, engine)
-        
-        self.__zoomCount = 0
-        
-        self.sourceChanged.connect(self.titleChanged)
-        
-        self.grabGesture(Qt.GestureType.PinchGesture)
-    
-    def setUrl(self, url):
-        """
-        Public method to set the URL of the document to be shown.
-        
-        @param url source of the document
-        @type QUrl
-        """
-        self.setSource(url)
-    
-    def url(self):
-        """
-        Public method to get the URL of the shown document.
-        
-        @return url URL of the document
-        @rtype QUrl
-        """
-        return self.source()
-    
-    def doSetSource(self, url, type):
-        """
-        Public method to load the data and show it.
-        
-        @param url DESCRIPTION
-        @type TYPE
-        @param type DESCRIPTION
-        @type TYPE
-        """
-        data = self.getData(url)
-        if data is None:
-            QDesktopServices.openUrl(url)
-            return
-        
-        if url != QUrl("about:blank"):
-            super().doSetSource(url, type)
-        
-        self.setHtml(data)
-        self.sourceChanged.emit(url)
-        self.loadFinished.emit(True)
-    
-    def title(self):
-        """
-        Public method get the page title.
-        
-        @return page title
-        @rtype str
-        """
-        titleStr = self.documentTitle()
-        if not titleStr:
-            url = self.url()
-            
-            titleStr = url.host()
-            if not titleStr:
-                titleStr = url.toString(
-                    QUrl.UrlFormattingOption.RemoveFragment)
-        
-        if not titleStr or titleStr == "about:blank":
-            titleStr = self.tr("Empty Page")
-        
-        return titleStr
-    
-    def loadResource(self, type_,  name):
-        """
-        Public method to load data of the specified type from the resource with
-        the given name.
-        
-        @param type_ resource type
-        @type int
-        @param name resource name
-        @type QUrl
-        """
-        ba = QByteArray()
-        
-        if type_ < 4:     # QTextDocument.ResourceType.MarkdownResource
-            url = self._engine.findFile(name)
-            ba = self._engine.fileData(url)
-            if url.toString().lower().endswith(".svg"):
-                image = QImage()
-                image.loadFromData(ba, "svg")
-                if not image.isNull():
-                    return image
-        
-        return ba
-    
-    def mousePressEvent(self, evt):
-        """
-        Protected method called by a mouse press event.
-        
-        @param evt reference to the mouse event
-        @type QMouseEvent
-        """
-        if evt.button() == Qt.MouseButton.XButton1:
-            self.backward()
-        elif evt.button() == Qt.MouseButton.XButton2:
-            self.forward()
-        else:
-            super().mousePressEvent(evt)
-    
-    def gotoHistory(self, index):
-        """
-        Public method to step through the history.
-        
-        @param index history index (<0 backward, >0 forward)
-        @type int
-        """
-        if index < 0:
-            # backward
-            for ind in range(-index):
-                self.backward()
-        else:
-            # forward
-            for ind in range(index):
-                self.forward()
-    
-    def isBackwardAvailable(self):
-        """
-        Public method to check, if stepping backward through the history is
-        available.
-        """
-        return QTextBrowser.isBackwardAvailable(self)
-    
-    def isForwardAvailable(self):
-        """
-        Public method to check, if stepping forward through the history is
-        available.
-        """
-        return QTextBrowser.isForwardAvailable(self)
-    
-    def scaleUp(self):
-        """
-        Public method to zoom in.
-        """
-        if self.__zoomCount < 10:
-            self.__zoomCount += 1
-            self.zoomIn()
-            self.zoomChanged.emit()
-    
-    def scaleDown(self):
-        """
-        Public method to zoom out.
-        """
-        if self.__zoomCount > -5:
-            self.__zoomCount -= 1
-            self.zoomOut()
-            self.zoomChanged.emit()
-    
-    def setScale(self, scale):
-        """
-        Public method to set the zoom level.
-        
-        @param scale zoom level to set
-        @type int
-        """
-        if -5 <= scale <= 10:
-            self.zoomOut(scale)
-            self.__zoomCount = scale
-            self.zoomChanged.emit()
-    
-    def resetScale(self):
-        """
-        Public method to reset the zoom level.
-        """
-        if self.__zoomCount != 0:
-            self.zoomOut(self.__zoomCount)
-            self.zoomChanged.emit()
-        self.__zoomCount = 0
-    
-    def scale(self):
-        """
-        Public method to get the zoom level.
-        
-        @return current zoom level
-        @rtype int
-        """
-        return self.__zoomCount
-    
-    def isScaleUpAvailable(self):
-        """
-        Public method to check, if the max. zoom level is reached.
-        
-        @return flag indicating scale up is available
-        @rtype bool
-        """
-        return self.__zoomCount < 10
-    
-    def isScaleDownAvailable(self):
-        """
-        Public method to check, if the min. zoom level is reached.
-        
-        @return flag indicating scale down is available
-        @rtype bool
-        """
-        return self.__zoomCount > -5
-    
-    def wheelEvent(self, evt):
-        """
-        Public method to handle wheel event to zoom.
-        
-        @param evt reference to the event object
-        @type QWheelEvent
-        """
-        delta = evt.angleDelta().y()
-        if evt.modifiers() == Qt.KeyboardModifier.ControlModifier:
-            if delta > 0:
-                self.scaleUp()
-            else:
-                self.scaleDown()
-            evt.accept()
-        
-        elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier:
-            if delta < 0:
-                self.backward()
-            elif delta > 0:
-                self.forward()
-            evt.accept()
-        
-        else:
-            QTextBrowser.wheelEvent(self, evt)
-    
-    def keyPressEvent(self, evt):
-        """
-        Public method to handle key press events.
-        
-        @param evt reference to the key event
-        @type QKeyEvent
-        """
-        if evt.key() == Qt.Key.Key_ZoomIn:
-            self.scaleUp()
-            evt.accept()
-        elif evt.key() == Qt.Key.Key_ZoomOut:
-            self.scaleDown()
-            evt.accept()
-        elif evt.key() == Qt.Key.Key_Plus:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.scaleUp()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_Minus:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.scaleDown()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_0:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.resetScale()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_Backspace:
-            self.backward()
-            evt.accept()
-    
-    def event(self, evt):
-        """
-        Public method handling events.
-        
-        @param evt reference to the event
-        @type QEvent
-        @return flag indicating the event was handled
-        @rtype bool
-        """
-        if evt.type() == QEvent.Type.Gesture:
-            self.gestureEvent(evt)    
-            return True
-        
-        return super().event(evt)
-    
-    def gestureEvent(self, evt):
-        """
-        Protected method handling gesture events.
-        
-        @param evt reference to the gesture event
-        @type QGestureEvent
-        """
-        pinch = evt.gesture(Qt.GestureType.PinchGesture)
-        if pinch:
-            if pinch.state() == Qt.GestureState.GestureStarted:
-                zoom = (self.getZoom() + 6) / 10.0
-                pinch.setTotalScaleFactor(zoom)
-            elif pinch.state() == Qt.GestureState.GestureUpdated:
-                zoom = int(pinch.totalScaleFactor() * 10) - 6
-                if zoom <= -5:
-                    zoom = -5
-                    pinch.setTotalScaleFactor(0.1)
-                elif zoom >= 10:
-                    zoom = 10
-                    pinch.setTotalScaleFactor(1.6)
-                self.setScale(zoom)
-            evt.accept()
-    
-    # TODO: implement context menu
-    #       - Backward
-    #       - Forward
-    #       - Reload
-    #       - Open Link
-    #       - Open Link in New Page
-    #       - Open Link in Background Page
-    #       - Copy
-    #       - Copy Link Location
-    #       - Select All
-    # TODO: add Ctrl+LMB action (open link in new page)
--- a/eric7/HelpViewer/HelpViewerImpl_qwe.py	Sun Oct 17 15:26:01 2021 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,688 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2021 Detlev Offenbach <detlev@die-offenbachs.de>
-#
-
-"""
-Module implementing the help viewer base class.
-"""
-
-from PyQt6.QtCore import pyqtSlot, Qt, QEvent, QTimer, QUrl, QPoint
-from PyQt6.QtGui import QGuiApplication, QClipboard, QContextMenuEvent
-from PyQt6.QtWidgets import QMenu
-from PyQt6.QtWebEngineWidgets import QWebEngineView
-from PyQt6.QtWebEngineCore import QWebEnginePage
-
-from .HelpViewerWidget import HelpViewerWidget
-from .HelpViewerImpl import HelpViewerImpl
-
-import UI.PixmapCache
-
-class HelpViewerImpl_qwe(HelpViewerImpl, QWebEngineView):
-    """
-    Class implementing the QTextBrowser based help viewer class.
-    """
-    ZoomLevels = [
-        30, 40, 50, 67, 80, 90,
-        100,
-        110, 120, 133, 150, 170, 200, 220, 233, 250, 270, 285, 300,
-    ]
-    ZoomLevelDefault = 100
-    
-    def __init__(self, engine, parent=None):
-        """
-        Constructor
-        
-        @param engine reference to the help engine
-        @type QHelpEngine
-        @param parent reference to the parent widget
-        @type QWidget
-        """
-        QWebEngineView.__init__(self, parent=parent)
-        HelpViewerImpl.__init__(self, engine)
-        
-        self.__helpViewerWidget = parent
-        
-        self.__rwhvqt = None
-        self.installEventFilter(self)
-        
-        self.__page = None
-        self.__createNewPage()
-        
-        self.__currentScale = 100
-        
-        self.__menu = QMenu(self)
-    
-    def __createNewPage(self):
-        """
-        Private method to create a new page object.
-        """
-        self.__page = QWebEnginePage(self.__helpViewerWidget.webProfile())
-        self.setPage(self.__page)
-        
-        self.__page.titleChanged.connect(self.__titleChanged)
-        self.__page.urlChanged.connect(self.__titleChanged)
-    
-    def __setRwhvqt(self):
-        """
-        Private slot to set widget that receives input events.
-        """
-        self.grabGesture(Qt.GestureType.PinchGesture)
-        self.__rwhvqt = self.focusProxy()
-        if self.__rwhvqt:
-            self.__rwhvqt.grabGesture(Qt.GestureType.PinchGesture)
-            self.__rwhvqt.installEventFilter(self)
-        else:
-            print("Focus proxy is null!")   # __IGNORE_WARNING_M801__
-    
-    def setUrl(self, url):
-        """
-        Public method to set the URL of the document to be shown.
-        
-        @param url URL of the document
-        @type QUrl
-        """
-        QWebEngineView.setUrl(self, url)
-    
-    def url(self):
-        """
-        Public method to get the URL of the shown document.
-        
-        @return url URL of the document
-        @rtype QUrl
-        """
-        return QWebEngineView.url(self)
-    
-    @pyqtSlot()
-    def __titleChanged(self):
-        """
-        Private method to handle a change of the web page title.
-        """
-        super().titleChanged.emit()
-    
-    def title(self):
-        """
-        Public method get the page title.
-        
-        @return page title
-        @rtype str
-        """
-        titleStr = QWebEngineView.title(self)
-        if not titleStr:
-            if self.url().isEmpty():
-                url = self.__page.requestedUrl()
-            else:
-                url = self.url()
-            
-            titleStr = url.host()
-            if not titleStr:
-                titleStr = url.toString(
-                    QUrl.UrlFormattingOption.RemoveFragment)
-        
-        if not titleStr or titleStr == "about:blank":
-            titleStr = self.tr("Empty Page")
-        
-        return titleStr
-    
-    def isBackwardAvailable(self):
-        """
-        Public method to check, if stepping backward through the history is
-        available.
-        """
-        return self.history().canGoBack()
-    
-    def isForwardAvailable(self):
-        """
-        Public method to check, if stepping forward through the history is
-        available.
-        """
-        return self.history().canGoForward()
-    
-    def backward(self):
-        """
-        Public slot to move backwards in history.
-        """
-        self.triggerPageAction(QWebEnginePage.WebAction.Back)
-    
-    def forward(self):
-        """
-        Public slot to move forward in history.
-        """
-        self.triggerPageAction(QWebEnginePage.WebAction.Forward)
-    
-    def reload(self):
-        """
-        Public slot to reload the current page.
-        """
-        self.triggerPageAction(QWebEnginePage.WebAction.Reload)
-    
-    def backwardHistoryCount(self):
-        """
-        Public method to get the number of available back history items.
-        
-        Note: For performance reasons this is limited to the maximum number of
-        history items the help viewer is interested in.
-        
-        @return count of available back history items
-        @rtype int
-        """
-        history = self.history()
-        return len(history.backItems(HelpViewerWidget.MaxHistoryItems))
-    
-    def forwardHistoryCount(self):
-        """
-        Public method to get the number of available forward history items.
-        
-        Note: For performance reasons this is limited to the maximum number of
-        history items the help viewer is interested in.
-        
-        @return count of available forward history items
-        @rtype int
-        """
-        history = self.history()
-        return len(history.forwardItems(HelpViewerWidget.MaxHistoryItems))
-    
-    def historyTitle(self, offset):
-        """
-        Public method to get the title of a history item.
-        
-        @param offset offset of the item with respect to the current page
-        @type int
-        @return title of the requeted item in history
-        @rtype str
-        """
-        history = self.history()
-        currentIndex = history.currentItemIndex()
-        itm = self.history().itemAt(currentIndex + offset)
-        return itm.title()
-    
-    def gotoHistory(self, offset):
-        """
-        Public method to go to ahistory item
-        
-        @param offset offset of the item with respect to the current page
-        @type int
-        """
-        history = self.history()
-        currentIndex = history.currentItemIndex()
-        itm = self.history().itemAt(currentIndex + offset)
-        history.goToItem(itm)
-    
-    def clearHistory(self):
-        """
-        Public method to clear the history.
-        """
-        self.history().clear()
-    
-    #######################################################################
-    
-    def __levelForScale(self, scale):
-        """
-        Private method determining the zoom level index given a zoom factor.
-        
-        @param zoom zoom factor
-        @type int
-        @return index of zoom factor
-        @rtype int
-        """
-        try:
-            index = self.ZoomLevels.index(scale)
-        except ValueError:
-            for index in range(len(self.ZoomLevels)):
-                if scale <= self.ZoomLevels[scale]:
-                    break
-        return index
-    
-    def scaleUp(self):
-        """
-        Public method to zoom in.
-        """
-        index = self.__levelForScale(self.__currentScale)
-        if index < len(self.ZoomLevels) - 1:
-            self.setScale(self.ZoomLevels[index + 1])
-    
-    def scaleDown(self):
-        """
-        Public method to zoom out.
-        """
-        index = self.__levelForScale(self.__currentScale)
-        if index > 0:
-            self.setScale(self.ZoomLevels[index - 1])
-    
-    def setScale(self, scale):
-        """
-        Public method to set the zoom level.
-        
-        @param scale zoom level to set
-        @type int
-        """
-        if scale != self.__currentScale:
-            self.setZoomFactor(scale / 100.0)
-            self.__currentScale = scale
-            self.zoomChanged.emit()
-    
-    def resetScale(self):
-        """
-        Public method to reset the zoom level.
-        """
-        index = self.__levelForScale(self.ZoomLevelDefault)
-        self.setScale(self.ZoomLevels[index])
-    
-    def scale(self):
-        """
-        Public method to get the zoom level.
-        
-        @return current zoom level
-        @rtype int
-        """
-        return self.__currentScale
-    
-    def isScaleUpAvailable(self):
-        """
-        Public method to check, if the max. zoom level is reached.
-        
-        @return flag indicating scale up is available
-        @rtype bool
-        """
-        index = self.__levelForScale(self.__currentScale)
-        return index < len(self.ZoomLevels) - 1
-    
-    def isScaleDownAvailable(self):
-        """
-        Public method to check, if the min. zoom level is reached.
-        
-        @return flag indicating scale down is available
-        @rtype bool
-        """
-        index = self.__levelForScale(self.__currentScale)
-        return index > 0
-    
-    #######################################################################
-    ## Event handlers below
-    #######################################################################
-    
-    def eventFilter(self, obj, evt):
-        """
-        Public method to process event for other objects.
-        
-        @param obj reference to object to process events for
-        @type QObject
-        @param evt reference to event to be processed
-        @type QEvent
-        @return flag indicating that the event should be filtered out
-        @rtype bool
-        """
-        if (
-            obj is self and
-            evt.type() == QEvent.Type.ParentChange and
-            self.parentWidget() is not None
-        ):
-            self.parentWidget().installEventFilter(self)
-        
-        # find the render widget receiving events for the web page
-        if obj is self and evt.type() == QEvent.Type.ChildAdded:
-            QTimer.singleShot(0, self.__setRwhvqt)
-        
-        # forward events to WebBrowserView
-        if (
-            obj is self.__rwhvqt and
-            evt.type() in [QEvent.Type.KeyPress,
-                           QEvent.Type.MouseButtonRelease,
-                           QEvent.Type.Wheel,
-                           QEvent.Type.Gesture]
-        ):
-            wasAccepted = evt.isAccepted()
-            evt.setAccepted(False)
-            if evt.type() == QEvent.Type.KeyPress:
-                self._keyPressEvent(evt)
-            elif evt.type() == QEvent.Type.MouseButtonRelease:
-                self._mouseReleaseEvent(evt)
-            elif evt.type() == QEvent.Type.Wheel:
-                self._wheelEvent(evt)
-            elif evt.type() == QEvent.Type.Gesture:
-                self._gestureEvent(evt)
-            ret = evt.isAccepted()
-            evt.setAccepted(wasAccepted)
-            return ret
-        
-        if (
-            obj is self.parentWidget() and
-            evt.type() in [QEvent.Type.KeyPress, QEvent.Type.KeyRelease]
-        ):
-            wasAccepted = evt.isAccepted()
-            evt.setAccepted(False)
-            if evt.type() == QEvent.Type.KeyPress:
-                self._keyPressEvent(evt)
-            ret = evt.isAccepted()
-            evt.setAccepted(wasAccepted)
-            return ret
-        
-        # block already handled events
-        if obj is self:
-            if evt.type() in [QEvent.Type.KeyPress,
-                              QEvent.Type.MouseButtonRelease,
-                              QEvent.Type.Wheel,
-                              QEvent.Type.Gesture]:
-                return True
-        
-        return super().eventFilter(obj, evt)
-    
-    def _keyPressEvent(self, evt):
-        """
-        Protected method called by a key press.
-        
-        @param evt reference to the key event
-        @type QKeyEvent
-        """
-        if evt.key() == Qt.Key.Key_ZoomIn:
-            self.scaleUp()
-            evt.accept()
-        elif evt.key() == Qt.Key.Key_ZoomOut:
-            self.scaleDown()
-            evt.accept()
-        elif evt.key() == Qt.Key.Key_Plus:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.scaleUp()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_Minus:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.scaleDown()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_0:
-            if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-                self.resetScale()
-                evt.accept()
-        elif evt.key() == Qt.Key.Key_Backspace:
-            self.pageAction(QWebEnginePage.WebAction.Back).trigger()
-            evt.accept()
-    
-    def _mouseReleaseEvent(self, evt):
-        """
-        Protected method called by a mouse release event.
-        
-        @param evt reference to the mouse event
-        @type QMouseEvent
-        """
-        accepted = evt.isAccepted()
-        self.__page.event(evt)
-        if (
-            not evt.isAccepted() and
-            evt.button() == Qt.MouseButton.MiddleButton
-        ):
-            url = QUrl(QGuiApplication.clipboard().text(
-                QClipboard.Mode.Selection))
-            if (
-                not url.isEmpty() and
-                url.isValid() and
-                url.scheme() != ""
-            ):
-                self.setUrl(url)
-                accepted = True
-        evt.setAccepted(accepted)
-    
-    def _wheelEvent(self, evt):
-        """
-        Protected method to handle wheel events.
-        
-        @param evt reference to the wheel event
-        @type QWheelEvent
-        """
-        delta = evt.angleDelta().y()
-        if evt.modifiers() & Qt.KeyboardModifier.ControlModifier:
-            if delta < 0:
-                self.scaleDown()
-            elif delta > 0:
-                self.scaleUp()
-            evt.accept()
-        
-        elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier:
-            if delta < 0:
-                self.backward()
-            elif delta > 0:
-                self.forward()
-            evt.accept()
-    
-    def _gestureEvent(self, evt):
-        """
-        Protected method handling gesture events.
-        
-        @param evt reference to the gesture event
-        @type QGestureEvent
-        """
-        pinch = evt.gesture(Qt.GestureType.PinchGesture)
-        if pinch:
-            if pinch.state() == Qt.GestureState.GestureStarted:
-                pinch.setTotalScaleFactor(self.__currentScale / 100.0)
-            elif pinch.state() == Qt.GestureState.GestureUpdated:
-                scaleFactor = pinch.totalScaleFactor()
-                self.setScale(int(scaleFactor * 100))
-            evt.accept()
-    
-    def event(self, evt):
-        """
-        Public method handling events.
-        
-        @param evt reference to the event (QEvent)
-        @return flag indicating, if the event was handled (boolean)
-        """
-        if evt.type() == QEvent.Type.Gesture:
-            self._gestureEvent(evt)
-            return True
-        
-        return super().event(evt)
-    
-    #######################################################################
-    ## Context menu related methods below
-    #######################################################################
-    # TODO: implement context menu
-    
-    def contextMenuEvent(self, evt):
-        """
-        Protected method called to create a context menu.
-        
-        This method is overridden from QWebEngineView.
-        
-        @param evt reference to the context menu event object
-        @type QContextMenuEvent
-        """
-        pos = evt.pos()
-        reason = evt.reason()
-        QTimer.singleShot(
-            0,
-            lambda: self._contextMenuEvent(QContextMenuEvent(reason, pos)))
-        # needs to be done this way because contextMenuEvent is blocking
-        # the main loop
-    
-    def _contextMenuEvent(self, evt):
-        """
-        Protected method called to create a context menu.
-        
-        @param evt reference to the context menu event object
-            (QContextMenuEvent)
-        """
-        self.__menu.clear()
-        
-        self.__createContextMenu(self.__menu)
-        
-        if not self.__menu.isEmpty():
-            pos = evt.globalPos()
-            self.__menu.popup(QPoint(pos.x(), pos.y() + 1))
-    
-    def __createContextMenu(self, menu):
-        """
-        Private method to populate the context menu.
-        
-        @param menu reference to the menu to be populated
-        @type QMenu
-        """
-        contextMenuData = self.lastContextMenuRequest()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("back"),
-            self.tr("Backward"),
-            self.backward)
-        act.setEnabled(self.isBackwardAvailable())
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("forward"),
-            self.tr("Forward"),
-            self.forward)
-        act.setEnabled(self.isForwardAvailable())
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("reload"),
-            self.tr("Reload"),
-            self.reload)
-        
-        if (
-            not contextMenuData.linkUrl().isEmpty() and
-            contextMenuData.linkUrl().scheme() != "javascript"
-        ):
-            self.__createLinkContextMenu(menu, contextMenuData)
-        
-        menu.addSeparator()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("editCopy"),
-            self.tr("Copy Page URL to Clipboard"))
-        act.setData(self.url())
-        act.triggered.connect(
-            lambda: self.__copyLink(act))
-        
-        menu.addSeparator()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("zoomIn"),
-            self.tr("Zoom in"),
-            self.scaleUp)
-        act.setEnabled(self.isScaleUpAvailable())
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("zoomOut"),
-            self.tr("Zoom out"),
-            self.scaleDown)
-        act.setEnabled(self.isScaleDownAvailable())
-        
-        menu.addAction(
-            UI.PixmapCache.getIcon("zoomReset"),
-            self.tr("Zoom reset"),
-            self.resetScale)
-        
-        menu.addSeparator()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("editCopy"),
-            self.tr("Copy"))
-        act.setData(contextMenuData.selectedText())
-        act.triggered.connect( lambda: self.__copyText(act))
-        act.setEnabled(bool(contextMenuData.selectedText()))
-        
-        menu.addAction(
-            UI.PixmapCache.getIcon("editSelectAll"),
-            self.tr("Select All"),
-            self.__selectAll)
-        
-        menu.addSeparator()
-        
-        menu.addAction(
-            UI.PixmapCache.getIcon("tabClose"),
-            self.tr('Close'),
-            self.__closePage)
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("tabCloseOther"),
-            self.tr("Close Others"),
-            self.__closeOtherPages)
-        act.setEnabled(self.__helpViewerWidget.openPagesCount() > 1)
-    
-    def __createLinkContextMenu(self, menu, contextMenuData):
-        """
-        Private method to populate the context menu for URLs.
-        
-        @param menu reference to the menu to be populated
-        @type QMenu
-        @param contextMenuData data of the last context menu request
-        @type QWebEngineContextMenuRequest
-        """
-        if not menu.isEmpty():
-            menu.addSeparator()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("openNewTab"),
-            self.tr("Open Link in New Page"))
-        act.setData(contextMenuData.linkUrl())
-        act.triggered.connect(
-            lambda: self.__openLinkInNewPage(act))
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("newWindow"),
-            self.tr("Open Link in Background Page"))
-        act.setData(contextMenuData.linkUrl())
-        act.triggered.connect(
-            lambda: self.__openLinkInBackgroundPage(act))
-        
-        menu.addSeparator()
-        
-        act = menu.addAction(
-            UI.PixmapCache.getIcon("editCopy"),
-            self.tr("Copy URL to Clipboard"))
-        act.setData(contextMenuData.linkUrl())
-        act.triggered.connect(
-            lambda: self.__copyLink(act))
-    
-    def __openLinkInNewPage(self, act):
-        """
-        Private method called by the context menu to open a link in a new page.
-        
-        @param act reference to the action that triggered
-        @type QAction
-        """
-        # TODO: not yet implemented
-    
-    def __openLinkInBackgroundPage(self, act):
-        """
-        Private method called by the context menu to open a link in a
-        background page.
-        
-        @param act reference to the action that triggered
-        @type QAction
-        """
-        # TODO: not yet implemented
-    
-    def __copyLink(self, act):
-        """
-        Private method called by the context menu to copy a link to the
-        clipboard.
-        
-        @param act reference to the action that triggered
-        @type QAction
-        """
-        # TODO: not yet implemented
-    
-    def __copyText(self, act):
-        """
-        Private method called by the context menu to copy selected text to the
-        clipboard.
-        
-        @param act reference to the action that triggered
-        @type QAction
-        """
-        # TODO: not yet implemented
-    
-    def __selectAll(self):
-        """
-        Private method called by the context menu to select all text.
-        """
-        # TODO: not yet implemented
-    
-    def __closePage(self):
-        """
-        Private method called by the context menu to close the current page.
-        """
-        # TODO: not yet implemented
-    
-    def __closeOtherPages(self):
-        """
-        Private method called by the context menu to close all other pages.
-        """
-        # TODO: not yet implemented
--- a/eric7/HelpViewer/HelpViewerWidget.py	Sun Oct 17 15:26:01 2021 +0200
+++ b/eric7/HelpViewer/HelpViewerWidget.py	Sun Oct 17 15:26:31 2021 +0200
@@ -202,7 +202,7 @@
         self.__helpTocButton = self.__addNavigationButton(
             "tableOfContents", self.tr("Show the table of contents"))
         self.__helpIndexButton = self.__addNavigationButton(
-            "helpIndex",  self.tr("Show the help document index"))
+            "helpIndex", self.tr("Show the help document index"))
         self.__helpSearchButton = self.__addNavigationButton(
             "documentFind", self.tr("Show the help search window"))
         self.__openPagesButton.setChecked(True)
@@ -394,9 +394,9 @@
             self.tr("HTML Files (*.htm *.html);;All Files (*)")
         )
         if htmlFile:
-            self.currentViewer().setUrl(QUrl.fromLocalFile(htmlFile))
+            self.currentViewer().setLink(QUrl.fromLocalFile(htmlFile))
     
-    def addPage(self, url=QUrl("about:blank"), background=False):
+    def addPage(self, url=None, background=False):
         """
         Public method to add a new help page with the given URL.
         
@@ -405,23 +405,28 @@
         @param background flag indicating to open the page in the background
             (defaults to False)
         @type bool (optional)
+        @return reference to the created page
+        @rtype HelpViewerImpl
         """
+        if url is None:
+            url = QUrl("about:blank")
+        
         viewer = self.__newViewer()
         viewer.setUrl(url)
         
-        if background:
-            cv = self.currentViewer()
-            if cv:
-                index = self.__helpStack.indexOf(cv) + 1
-                self.__helpStack.insertWidget(index, viewer)
-                self.__openPagesList.insertPage(
-                    index, viewer, background=background)
-                cv.setFocus(Qt.FocusReason.OtherFocusReason)
-                return
+        cv = self.currentViewer()
+        if background and bool(cv):
+            index = self.__helpStack.indexOf(cv) + 1
+            self.__helpStack.insertWidget(index, viewer)
+            self.__openPagesList.insertPage(
+                index, viewer, background=background)
+            cv.setFocus(Qt.FocusReason.OtherFocusReason)
+        else:
+            self.__helpStack.addWidget(viewer)
+            self.__openPagesList.addPage(viewer, background=background)
+            viewer.setFocus(Qt.FocusReason.OtherFocusReason)
         
-        self.__helpStack.addWidget(viewer)
-        self.__openPagesList.addPage(viewer, background=background)
-        viewer.setFocus(Qt.FocusReason.OtherFocusReason)
+        return viewer
     
     @pyqtSlot(QUrl)
     def openUrl(self, url):
@@ -433,7 +438,7 @@
         """
         cv = self.currentViewer()
         if cv:
-            cv.setUrl(url)
+            cv.setLink(url)
             cv.setFocus(Qt.FocusReason.OtherFocusReason)
     
     @pyqtSlot(QUrl)
@@ -457,6 +462,27 @@
         self.addPage(url=url, background=True)
     
     @pyqtSlot()
+    def closeCurrentPage(self):
+        """
+        Public slot to close the current page.
+        """
+        self.__openPagesList.closeCurrentPage()
+    
+    @pyqtSlot()
+    def closeOtherPages(self):
+        """
+        Public slot to close all other pages.
+        """
+        self.__openPagesList.closeOtherPages()
+    
+    @pyqtSlot()
+    def closeAllPages(self):
+        """
+        Public slot to close all pages.
+        """
+        self.__openPagesList.closeAllPages()
+    
+    @pyqtSlot()
     def __activateCurrentPage(self):
         """
         Private slot to activate the current page.
@@ -473,11 +499,11 @@
         @rtype HelpViewerImpl
         """
         if WEBENGINE_AVAILABLE:
-            from .HelpViewerImpl_qwe import HelpViewerImpl_qwe
-            viewer = HelpViewerImpl_qwe(self.__helpEngine, self)
+            from .HelpViewerImplQWE import HelpViewerImplQWE
+            viewer = HelpViewerImplQWE(self.__helpEngine, self)
         else:
-            from .HelpViewerImpl_qtb import HelpViewerImpl_qtb
-            viewer = HelpViewerImpl_qtb(self.__helpEngine, self)
+            from .HelpViewerImplQTB import HelpViewerImplQTB
+            viewer = HelpViewerImplQTB(self.__helpEngine, self)
         
         viewer.zoomChanged.connect(self.__checkActionButtons)
         
@@ -511,7 +537,7 @@
         self.__helpEngine.setupData()
         self.__removeOldDocumentation()
     
-    def __getQtHelpCollectionFileName(cls):
+    def __getQtHelpCollectionFileName(self):
         """
         Private method to determine the name of the QtHelp collection file.
         
@@ -906,14 +932,14 @@
             self.__helpEngine.filterEngine().setActiveFilter(helpFilter)
     
     @pyqtSlot(str)
-    def __currentFilterChanged(self, filter):
+    def __currentFilterChanged(self, filter_):
         """
         Private slot handling a change of the active QtHelp filter.
         
-        @param filter filter name
+        @param filter_ filter name
         @type str
         """
-        index = self.__helpFilterCombo.findData(filter)
+        index = self.__helpFilterCombo.findData(filter_)
         if index < 0:
             index = 0
         self.__helpFilterCombo.setCurrentIndex(index)
--- a/eric7/HelpViewer/OpenPagesWidget.py	Sun Oct 17 15:26:01 2021 +0200
+++ b/eric7/HelpViewer/OpenPagesWidget.py	Sun Oct 17 15:26:31 2021 +0200
@@ -19,6 +19,9 @@
 class OpenPagesWidget(QWidget):
     """
     Class implementing a widget showing the list of open pages.
+    
+    @signal currentChanged(index) emitted to signal a change of the current
+        page index
     """
     currentChanged = pyqtSignal(int)
     
@@ -134,7 +137,7 @@
             (defaults to False)
         @type bool (optional)
         """
-        self.__openPagesList.addItem(viewer.title())
+        self.__openPagesList.addItem(viewer.pageTitle())
         viewer.titleChanged.connect(
             lambda: self.__viewerTitleChanged(viewer))
         
@@ -157,7 +160,7 @@
         @type bool (optional)
         """
         currentRow = self.__openPagesList.currentRow()
-        self.__openPagesList.insertItem(index, viewer.title())
+        self.__openPagesList.insertItem(index, viewer.pageTitle())
         viewer.titleChanged.connect(
             lambda: self.__viewerTitleChanged(viewer))
         
@@ -175,7 +178,7 @@
         """
         index = self.__stack.indexOf(viewer)
         itm = self.__openPagesList.item(index)
-        itm.setText(viewer.title())
+        itm.setText(viewer.pageTitle())
         self.currentChanged.emit(index)
     
     #######################################################################
@@ -185,32 +188,23 @@
     @pyqtSlot()
     def __contextMenuClose(self):
         """
-        Private slot to close a page.
+        Private slot to close a page via the context menu.
         """
-        row = self.__openPagesList.currentRow()
-        self.__removeViewer(row)
-        
-        if self.__openPagesList.count() == 0:
-            self.__helpViewer.addPage()
+        self.closeCurrentPage()
     
     @pyqtSlot()
     def __contextMenuCloseOthers(self):
         """
-        Private slot to close all other pages.
+        Private slot to close all other pages via the context menu.
         """
-        currentRow = self.__openPagesList.currentRow()
-        for row in range(self.__openPagesList.count() - 1, -1, -1):
-            if row != currentRow:
-                self.__removeViewer(row)
+        self.closeOtherPages()
     
     @pyqtSlot()
     def __contextMenuCloseAll(self):
         """
-        Private slot to close all pages.
+        Private slot to close all pages via the context menu.
         """
-        while self.__openPagesList.count() != 0:
-            self.__removeViewer(0)
-        self.__helpViewer.addPage()
+        self.closeAllPages()
     
     @pyqtSlot()
     def __contextMenuCopyUrlToClipboard(self):
@@ -219,7 +213,7 @@
         """
         row = self.__openPagesList.currentRow()
         viewer = self.__stack.widget(row)
-        url = viewer.url()
+        url = viewer.link()
         if url.isValid():
             urlStr = url.toString()
             
@@ -242,3 +236,37 @@
         
         itm = self.__openPagesList.takeItem(row)
         del itm
+    
+    #######################################################################
+    ## Slots for external access below
+    #######################################################################
+    
+    @pyqtSlot()
+    def closeCurrentPage(self):
+        """
+        Public slot to close the current page.
+        """
+        row = self.__openPagesList.currentRow()
+        self.__removeViewer(row)
+        
+        if self.__openPagesList.count() == 0:
+            self.__helpViewer.addPage()
+    
+    @pyqtSlot()
+    def closeOtherPages(self):
+        """
+        Public slot to close all other pages.
+        """
+        currentRow = self.__openPagesList.currentRow()
+        for row in range(self.__openPagesList.count() - 1, -1, -1):
+            if row != currentRow:
+                self.__removeViewer(row)
+    
+    @pyqtSlot()
+    def closeAllPages(self):
+        """
+        Public slot to close all pages.
+        """
+        while self.__openPagesList.count() != 0:
+            self.__removeViewer(0)
+        self.__helpViewer.addPage()

eric ide

mercurial