Forward all scroll events in text area widgets to parent (#1511)

This commit is contained in:
Veronica Berglyd Olsen
2023-08-31 15:43:39 +01:00
committed by GitHub
5 changed files with 143 additions and 1 deletions
@@ -0,0 +1,65 @@
"""
novelWriter Custom Object: Wheel Event Filter
===============================================
File History:
Created: 2023-08-31 [2.1rc1]
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from PyQt5.QtGui import QWheelEvent
from PyQt5.QtCore import QEvent, QObject
from PyQt5.QtWidgets import QWidget
class WheelEventFilter(QObject):
"""Extensions: Wheel Event Filter
An event filter that filters mouse wheel events for a widget and
forward them to the root widget. This solves the lack of mouse wheel
scrolling response in margins of widgets like the editor and viewer.
Solves: https://github.com/vkbo/novelWriter/issues/1425
Reference: https://stackoverflow.com/a/17739995/5825851
"""
__slots__ = ("_parent", "_locked")
def __init__(self, parent: QWidget) -> None:
super().__init__(parent=parent)
self._parent = parent
self._locked = False
return
def eventFilter(self, object: QObject, event: QEvent) -> bool:
"""Filter events of type QWheelEvent and forward them to the
parent widget's wheelEvent handler.
"""
if self._locked:
return False
if isinstance(event, QWheelEvent):
# Recursion protection may be redundant, but a comment on
# StackOverflow points to Qt 5.12 as the change, and we
# still support Qt 5.10
self._locked = True
self._parent.wheelEvent(event)
self._locked = False
return False
# END Class WheelEventFilter
+5
View File
@@ -55,6 +55,7 @@ from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwConst, nwKeyWords, nwUnicode from novelwriter.constants import nwConst, nwKeyWords, nwUnicode
from novelwriter.core.index import countWords from novelwriter.core.index import countWords
from novelwriter.gui.dochighlight import GuiDocHighlighter from novelwriter.gui.dochighlight import GuiDocHighlighter
from novelwriter.extensions.wheeleventfilter import WheelEventFilter
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
@@ -178,6 +179,10 @@ class GuiDocEditor(QTextEdit):
self.wCounterSel.setAutoDelete(False) self.wCounterSel.setAutoDelete(False)
self.wCounterSel.signals.countsReady.connect(self._updateSelCounts) self.wCounterSel.signals.countsReady.connect(self._updateSelCounts)
# Install Event Filter for Mouse Wheel
self.wheelEventFilter = WheelEventFilter(self)
self.installEventFilter(self.wheelEventFilter)
# Finalise # Finalise
self.updateSyntaxColours() self.updateSyntaxColours()
self.initEditor() self.initEditor()
+5
View File
@@ -47,6 +47,7 @@ from novelwriter.enum import nwItemType, nwDocAction, nwDocMode
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.core.tohtml import ToHtml from novelwriter.core.tohtml import ToHtml
from novelwriter.extensions.wheeleventfilter import WheelEventFilter
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain from novelwriter.guimain import GuiMain
@@ -85,6 +86,10 @@ class GuiDocViewer(QTextBrowser):
# Signals # Signals
self.anchorClicked.connect(self._linkClicked) self.anchorClicked.connect(self._linkClicked)
# Install Event Filter for Mouse Wheel
self.wheelEventFilter = WheelEventFilter(self)
self.installEventFilter(self.wheelEventFilter)
# Context Menu # Context Menu
self.setContextMenuPolicy(Qt.CustomContextMenu) self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._openContextMenu) self.customContextMenuRequested.connect(self._openContextMenu)
+1 -1
View File
@@ -3,7 +3,7 @@ novelWriter Shared Data Class
=============================== ===============================
File History: File History:
Created: 2023-08-10 [2.1b2] Created: 2023-08-10 [2.1rc1]
This file is a part of novelWriter This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen Copyright 20182023, Veronica Berglyd Olsen
@@ -0,0 +1,67 @@
"""
novelWriter Wheel Event Filter Tester
=======================================
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from PyQt5.QtCore import QEvent, QObject, QPoint, Qt
import pytest
from PyQt5.QtGui import QKeyEvent, QWheelEvent
from PyQt5.QtWidgets import QWidget
from novelwriter.extensions.wheeleventfilter import WheelEventFilter
class MockWidget(QWidget):
def __init__(self):
super().__init__(None)
self.count = 0
return
def wheelEvent(self, event: QWheelEvent) -> None:
self.count += 1
return
@pytest.mark.gui
def testExtWheelEventFilter_Main():
"""Test the WheelEventFilter class."""
obj = QObject()
widget = MockWidget()
eFilter = WheelEventFilter(widget)
assert widget.count == 0
# Sending a key event does nothing
event = QKeyEvent(QEvent.KeyPress, 1, Qt.ShiftModifier)
eFilter.eventFilter(obj, event)
assert widget.count == 0
# Sending a mouse wheel event forwards it
pos = QPoint(0, 0)
event = QWheelEvent(pos, pos, pos, pos, Qt.NoButton, Qt.NoModifier, Qt.NoScrollPhase, False)
eFilter.eventFilter(obj, event)
assert widget.count == 1
# But ignore if we're locked
eFilter._locked = True
eFilter.eventFilter(obj, event)
assert widget.count == 1
# END Test testExtWheelEventFilter_Main