From 3b9ef47e0f2db811974bb8671291b66877756f51 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 20:34:42 +0100
Subject: [PATCH 1/7] Make some minor improvements to custom widget
highlighting
---
novelwriter/extensions/circularprogress.py | 22 +++++++++++-----------
novelwriter/extensions/pagedsidebar.py | 2 +-
novelwriter/extensions/simpleprogress.py | 10 +++++-----
novelwriter/extensions/statusled.py | 16 ++++++++--------
novelwriter/tools/welcome.py | 2 +-
5 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/novelwriter/extensions/circularprogress.py b/novelwriter/extensions/circularprogress.py
index f638d3e0..c7ece503 100644
--- a/novelwriter/extensions/circularprogress.py
+++ b/novelwriter/extensions/circularprogress.py
@@ -84,17 +84,17 @@ class NProgressCircle(QProgressBar):
"""Custom painter for the progress bar."""
progress = 100.0*self.value()/self.maximum()
angle = ceil(16*3.6*progress)
- qPaint = QPainter(self)
- qPaint.setRenderHint(QPainter.Antialiasing, True)
- qPaint.setPen(self._dPen)
- qPaint.setBrush(self._dBrush)
- qPaint.drawEllipse(self._dRect)
- qPaint.setPen(self._bPen)
- qPaint.drawArc(self._cRect, 0, 360*16)
- qPaint.setPen(self._cPen)
- qPaint.drawArc(self._cRect, 90*16, -angle)
- qPaint.setPen(self._tColor)
- qPaint.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setPen(self._dPen)
+ painter.setBrush(self._dBrush)
+ painter.drawEllipse(self._dRect)
+ painter.setPen(self._bPen)
+ painter.drawArc(self._cRect, 0, 360*16)
+ painter.setPen(self._cPen)
+ painter.drawArc(self._cRect, 90*16, -angle)
+ painter.setPen(self._tColor)
+ painter.drawText(self._cRect, Qt.AlignCenter, self._text or f"{progress:.1f} %")
return
# END Class NProgressCircle
diff --git a/novelwriter/extensions/pagedsidebar.py b/novelwriter/extensions/pagedsidebar.py
index 86a08fe2..5cba8e41 100644
--- a/novelwriter/extensions/pagedsidebar.py
+++ b/novelwriter/extensions/pagedsidebar.py
@@ -160,7 +160,7 @@ class _NPagedToolButton(QToolButton):
if self.isChecked():
backCol = palette.highlight()
paint.setBrush(backCol)
- paint.setOpacity(0.5)
+ paint.setOpacity(0.35)
paint.drawRoundedRect(0, 0, width, height, self._cR, self._cR)
textCol = palette.highlightedText().color()
else:
diff --git a/novelwriter/extensions/simpleprogress.py b/novelwriter/extensions/simpleprogress.py
index 519d0c32..73452ad8 100644
--- a/novelwriter/extensions/simpleprogress.py
+++ b/novelwriter/extensions/simpleprogress.py
@@ -43,11 +43,11 @@ class NProgressSimple(QProgressBar):
"""Custom painter for the progress bar."""
if (value := self.value()) > 0:
progress = ceil(self.width()*float(value)/self.maximum())
- qPaint = QPainter(self)
- qPaint.setRenderHint(QPainter.Antialiasing, True)
- qPaint.setPen(self.palette().highlight().color())
- qPaint.setBrush(self.palette().highlight())
- qPaint.drawRect(0, 0, progress, self.height())
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setPen(self.palette().highlight().color())
+ painter.setBrush(self.palette().highlight())
+ painter.drawRect(0, 0, progress, self.height())
return
# END Class NProgressSimple
diff --git a/novelwriter/extensions/statusled.py b/novelwriter/extensions/statusled.py
index c49bc76d..77006a02 100644
--- a/novelwriter/extensions/statusled.py
+++ b/novelwriter/extensions/statusled.py
@@ -24,6 +24,7 @@ along with this program. If not, see .
from __future__ import annotations
import logging
+
from typing import Literal
from PyQt5.QtGui import QColor, QPaintEvent, QPainter
@@ -64,14 +65,13 @@ class StatusLED(QAbstractButton):
return
def paintEvent(self, event: QPaintEvent) -> None:
- """Drawing the LED."""
- qPalette = self.palette()
- qPaint = QPainter(self)
- qPaint.setRenderHint(QPainter.Antialiasing, True)
- qPaint.setPen(qPalette.dark().color())
- qPaint.setBrush(self._theCol)
- qPaint.setOpacity(1.0)
- qPaint.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
+ """Draw the LED."""
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setPen(self.palette().dark().color())
+ painter.setBrush(self._theCol)
+ painter.setOpacity(1.0)
+ painter.drawEllipse(1, 1, self.width() - 2, self.height() - 2)
return
# END Class StatusLED
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index 385f7bd2..0ff76315 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -355,7 +355,7 @@ class _ProjectListItem(QStyledItemDelegate):
painter.save()
if opt.state & QStyle.StateFlag.State_Selected == QStyle.StateFlag.State_Selected:
- painter.setOpacity(0.5)
+ painter.setOpacity(0.25)
painter.fillRect(rect, qApp.palette().highlight())
painter.setOpacity(1.0)
From 54f36d4e8e16f3531b35043b347fc1738615246b Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 20:49:06 +0100
Subject: [PATCH 2/7] Improve item auto-select on welcome dialog
---
novelwriter/core/coretools.py | 1 +
novelwriter/tools/welcome.py | 21 ++++++++++++++++-----
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py
index 2ac855eb..0eb4197b 100644
--- a/novelwriter/core/coretools.py
+++ b/novelwriter/core/coretools.py
@@ -460,6 +460,7 @@ class ProjectBuilder:
source = data.get("template")
if not (isinstance(source, Path) and source.is_file()
and source.name == nwFiles.PROJ_FILE):
+ logger.error("Could not access source project: %s", source)
return False
logger.info("Copying project: %s", source)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index 0ff76315..084cf002 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -249,8 +249,7 @@ class _OpenProjectPage(QWidget):
self.setLayout(self.outerBox)
- self.listWidget.setCurrentIndex(self.listModel.index(0))
- self._projectClicked(self.listModel.index(0))
+ self._selectFirstItem()
baseCol = self.palette().base().color()
self.setStyleSheet((
@@ -278,9 +277,9 @@ class _OpenProjectPage(QWidget):
@pyqtSlot(QModelIndex)
def _projectClicked(self, index: QModelIndex) -> None:
"""Process single click on project item."""
- if index.isValid():
- path = self.tr("Path")
- self.selectedPath.setText(f"{path}: {index.data()[1]}")
+ path = self.tr("Path")
+ value = index.data()[1] if index.isValid() else ""
+ self.selectedPath.setText(f"{path}: {value}")
return
@pyqtSlot(QModelIndex)
@@ -300,6 +299,7 @@ class _OpenProjectPage(QWidget):
).format(index.data()[0])
if SHARED.question(text):
self.listModel.removeEntry(index)
+ self._selectFirstItem()
return
@pyqtSlot("QPoint")
@@ -315,6 +315,17 @@ class _OpenProjectPage(QWidget):
ctxMenu.deleteLater()
return
+ ##
+ # Internal Functions
+ ##
+
+ def _selectFirstItem(self) -> None:
+ """Select the first item, if any are available."""
+ index = self.listModel.index(0)
+ self.listWidget.setCurrentIndex(index)
+ self._projectClicked(index)
+ return
+
# END Class _OpenProjectPage
From 27fa356846df7e4ef21ead2532f08bad17cb0050 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 21:22:12 +0100
Subject: [PATCH 3/7] Make the closing + opening of projects from welcome
screen a little smoother
---
novelwriter/guimain.py | 1 +
novelwriter/tools/welcome.py | 3 +++
2 files changed, 4 insertions(+)
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index 7438172b..0978d855 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -1146,6 +1146,7 @@ class GuiMain(QMainWindow):
@pyqtSlot(Path)
def _openProject(self, path: Path) -> None:
"""Handle an open project request."""
+ qApp.processEvents()
self.openProject(path)
return
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index 084cf002..c7357c48 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -197,6 +197,9 @@ class GuiWelcome(QDialog):
def _openProjectPath(self, path: Path) -> None:
"""Emit a project open signal."""
if isinstance(path, Path):
+ # Hide before emitting the open project signal so that any
+ # close/backup dialogs don't pop up over it.
+ self.hide()
self.openProjectRequest.emit(path)
self.close()
return
From c1a14e7aa5f0e337132319c172d49adbfb0ea7e9 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 21:28:20 +0100
Subject: [PATCH 4/7] Fix test
---
tests/test_tools/test_tools_welcome.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/test_tools/test_tools_welcome.py b/tests/test_tools/test_tools_welcome.py
index 60518786..56b61f1e 100644
--- a/tests/test_tools/test_tools_welcome.py
+++ b/tests/test_tools/test_tools_welcome.py
@@ -153,8 +153,9 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
assert listModel.removeEntry(listModel.createIndex(1, 0)) is False
# Delete last entry with keypress
- qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posOne, delay=10)
- qtbot.keyClick(tabOpen, Qt.Key.Key_Delete, delay=10)
+ welcome.show()
+ tabOpen._selectFirstItem()
+ qtbot.keyClick(tabOpen, Qt.Key.Key_Delete)
assert len(CONFIG.recentProjects.listEntries()) == 0
# qtbot.stop()
From c8664919100f73da9467c4f59e4681f2ace5e891 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 22:22:22 +0100
Subject: [PATCH 5/7] Remove test lines that sometimes fail, as they cover no
additional code
---
tests/test_tools/test_tools_welcome.py | 6 ------
1 file changed, 6 deletions(-)
diff --git a/tests/test_tools/test_tools_welcome.py b/tests/test_tools/test_tools_welcome.py
index 56b61f1e..96ed814c 100644
--- a/tests/test_tools/test_tools_welcome.py
+++ b/tests/test_tools/test_tools_welcome.py
@@ -152,12 +152,6 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
mp.setattr(listModel, "data", lambda *a: ("a", "b", "c"))
assert listModel.removeEntry(listModel.createIndex(1, 0)) is False
- # Delete last entry with keypress
- welcome.show()
- tabOpen._selectFirstItem()
- qtbot.keyClick(tabOpen, Qt.Key.Key_Delete)
- assert len(CONFIG.recentProjects.listEntries()) == 0
-
# qtbot.stop()
welcome.close()
From a8f61f2b989c6b527b245226f01650740512fe9f Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 23:35:14 +0100
Subject: [PATCH 6/7] Add modified combo and spinboxes that don't scroll on
mouse wheel
---
novelwriter/dialogs/preferences.py | 35 +++++------
novelwriter/dialogs/projectsettings.py | 7 ++-
novelwriter/extensions/modified.py | 81 ++++++++++++++++++++++++++
novelwriter/tools/manussettings.py | 32 +++++-----
novelwriter/tools/welcome.py | 13 +++--
5 files changed, 126 insertions(+), 42 deletions(-)
create mode 100644 novelwriter/extensions/modified.py
diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py
index 698d6376..690ad08e 100644
--- a/novelwriter/dialogs/preferences.py
+++ b/novelwriter/dialogs/preferences.py
@@ -29,15 +29,16 @@ import logging
from PyQt5.QtGui import QCloseEvent, QFont, QKeyEvent, QKeySequence
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
- QAbstractButton, QComboBox, QCompleter, QDialog, QDialogButtonBox,
- QDoubleSpinBox, QFileDialog, QFontDialog, QHBoxLayout, QLineEdit,
- QPushButton, QSpinBox, QToolButton, QVBoxLayout, QWidget, qApp
+ QAbstractButton, QCompleter, QDialog, QDialogButtonBox, QFileDialog,
+ QFontDialog, QHBoxLayout, QLineEdit, QPushButton, QToolButton, QVBoxLayout,
+ QWidget, qApp
)
from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwConst, nwUnicode
from novelwriter.dialogs.quotes import GuiQuoteSelect
from novelwriter.extensions.switch import NSwitch
+from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NSpinBox
from novelwriter.extensions.configlayout import NColourLabel, NScrollableForm
from novelwriter.extensions.pagedsidebar import NPagedSideBar
@@ -146,7 +147,7 @@ class GuiPreferences(QDialog):
self.mainForm.addGroupLabel(title, section)
# Display Language
- self.guiLocale = QComboBox(self)
+ self.guiLocale = NComboBox(self)
self.guiLocale.setMinimumWidth(minWidth)
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
self.guiLocale.addItem(name, lang)
@@ -159,7 +160,7 @@ class GuiPreferences(QDialog):
)
# Colour Theme
- self.guiTheme = QComboBox(self)
+ self.guiTheme = NComboBox(self)
self.guiTheme.setMinimumWidth(minWidth)
for theme, name in SHARED.theme.listThemes():
self.guiTheme.addItem(name, theme)
@@ -186,7 +187,7 @@ class GuiPreferences(QDialog):
)
# Application Font Size
- self.guiFontSize = QSpinBox(self)
+ self.guiFontSize = NSpinBox(self)
self.guiFontSize.setMinimum(8)
self.guiFontSize.setMaximum(60)
self.guiFontSize.setSingleStep(1)
@@ -221,7 +222,7 @@ class GuiPreferences(QDialog):
self.mainForm.addGroupLabel(title, section)
# Document Colour Theme
- self.guiSyntax = QComboBox(self)
+ self.guiSyntax = NComboBox(self)
self.guiSyntax.setMinimumWidth(CONFIG.pxInt(200))
for syntax, name in SHARED.theme.listSyntax():
self.guiSyntax.addItem(name, syntax)
@@ -248,7 +249,7 @@ class GuiPreferences(QDialog):
)
# Document Font Size
- self.textSize = QSpinBox(self)
+ self.textSize = NSpinBox(self)
self.textSize.setMinimum(8)
self.textSize.setMaximum(60)
self.textSize.setSingleStep(1)
@@ -290,7 +291,7 @@ class GuiPreferences(QDialog):
self.mainForm.addGroupLabel(title, section)
# Document Save Timer
- self.autoSaveDoc = QSpinBox(self)
+ self.autoSaveDoc = NSpinBox(self)
self.autoSaveDoc.setMinimum(5)
self.autoSaveDoc.setMaximum(600)
self.autoSaveDoc.setSingleStep(1)
@@ -301,7 +302,7 @@ class GuiPreferences(QDialog):
)
# Project Save Timer
- self.autoSaveProj = QSpinBox(self)
+ self.autoSaveProj = NSpinBox(self)
self.autoSaveProj.setMinimum(5)
self.autoSaveProj.setMaximum(600)
self.autoSaveProj.setSingleStep(1)
@@ -364,7 +365,7 @@ class GuiPreferences(QDialog):
)
# Inactive Time for Idle
- self.userIdleTime = QDoubleSpinBox(self)
+ self.userIdleTime = NDoubleSpinBox(self)
self.userIdleTime.setMinimum(0.5)
self.userIdleTime.setMaximum(600.0)
self.userIdleTime.setSingleStep(0.5)
@@ -388,7 +389,7 @@ class GuiPreferences(QDialog):
self.mainForm.addGroupLabel(title, section)
# Max Text Width in Normal Mode
- self.textWidth = QSpinBox(self)
+ self.textWidth = NSpinBox(self)
self.textWidth.setMinimum(0)
self.textWidth.setMaximum(10000)
self.textWidth.setSingleStep(10)
@@ -399,7 +400,7 @@ class GuiPreferences(QDialog):
)
# Max Text Width in Focus Mode
- self.focusWidth = QSpinBox(self)
+ self.focusWidth = NSpinBox(self)
self.focusWidth.setMinimum(200)
self.focusWidth.setMaximum(10000)
self.focusWidth.setSingleStep(10)
@@ -426,7 +427,7 @@ class GuiPreferences(QDialog):
)
# Document Margins
- self.textMargin = QSpinBox(self)
+ self.textMargin = NSpinBox(self)
self.textMargin.setMinimum(0)
self.textMargin.setMaximum(900)
self.textMargin.setSingleStep(1)
@@ -438,7 +439,7 @@ class GuiPreferences(QDialog):
)
# Tab Width
- self.tabWidth = QSpinBox(self)
+ self.tabWidth = NSpinBox(self)
self.tabWidth.setMinimum(0)
self.tabWidth.setMaximum(200)
self.tabWidth.setSingleStep(1)
@@ -458,7 +459,7 @@ class GuiPreferences(QDialog):
self.mainForm.addGroupLabel(title, section)
# Spell Checking
- self.spellLanguage = QComboBox(self)
+ self.spellLanguage = NComboBox(self)
self.spellLanguage.setMinimumWidth(minWidth)
if CONFIG.hasEnchant:
@@ -523,7 +524,7 @@ class GuiPreferences(QDialog):
)
# Typewriter Position
- self.autoScrollPos = QSpinBox(self)
+ self.autoScrollPos = NSpinBox(self)
self.autoScrollPos.setMinimum(10)
self.autoScrollPos.setMaximum(90)
self.autoScrollPos.setSingleStep(1)
diff --git a/novelwriter/dialogs/projectsettings.py b/novelwriter/dialogs/projectsettings.py
index c76c9f6b..c9e327cb 100644
--- a/novelwriter/dialogs/projectsettings.py
+++ b/novelwriter/dialogs/projectsettings.py
@@ -29,7 +29,7 @@ import logging
from PyQt5.QtGui import QCloseEvent, QColor, QIcon, QPixmap
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
- QColorDialog, QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, QLineEdit,
+ QColorDialog, QDialog, QDialogButtonBox, QHBoxLayout, QLineEdit,
QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
QWidget, qApp
)
@@ -37,6 +37,7 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED
from novelwriter.common import simplified
from novelwriter.extensions.switch import NSwitch
+from novelwriter.extensions.modified import NComboBox
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
from novelwriter.extensions.pagedsidebar import NPagedSideBar
@@ -257,7 +258,7 @@ class _SettingsPage(NScrollableForm):
)
# Project Language
- self.projLang = QComboBox(self)
+ self.projLang = NComboBox(self)
self.projLang.setMinimumWidth(xW)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag)
@@ -270,7 +271,7 @@ class _SettingsPage(NScrollableForm):
self.projLang.setCurrentIndex(idx)
# Spell Check Language
- self.spellLang = QComboBox(self)
+ self.spellLang = NComboBox(self)
self.spellLang.setMinimumWidth(xW)
self.spellLang.addItem(self.tr("Default"), "None")
if CONFIG.hasEnchant:
diff --git a/novelwriter/extensions/modified.py b/novelwriter/extensions/modified.py
new file mode 100644
index 00000000..cebb8b74
--- /dev/null
+++ b/novelwriter/extensions/modified.py
@@ -0,0 +1,81 @@
+"""
+novelWriter – Custom Widget: Modified Widgets
+=============================================
+
+File History:
+Created: 2024-02-01 [2.3b1] NComboBox
+Created: 2024-02-01 [2.3b1] NSpinBox
+Created: 2024-02-01 [2.3b1] NDoubleSpinBox
+
+This file is a part of novelWriter
+Copyright 2018–2024, 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 .
+"""
+from __future__ import annotations
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QWheelEvent
+from PyQt5.QtWidgets import QComboBox, QDoubleSpinBox, QSpinBox, QWidget
+
+
+class NComboBox(QComboBox):
+
+ def __init__(self, parent: QWidget | None = None) -> None:
+ super().__init__(parent=parent)
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ return
+
+ def wheelEvent(self, event: QWheelEvent) -> None:
+ if self.hasFocus():
+ super().wheelEvent(event)
+ else:
+ event.ignore()
+ return
+
+# END Class NComboBox
+
+
+class NSpinBox(QSpinBox):
+
+ def __init__(self, parent: QWidget | None = None) -> None:
+ super().__init__(parent=parent)
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ return
+
+ def wheelEvent(self, event: QWheelEvent) -> None:
+ if self.hasFocus():
+ super().wheelEvent(event)
+ else:
+ event.ignore()
+ return
+
+# END Class NSpinBox
+
+
+class NDoubleSpinBox(QDoubleSpinBox):
+
+ def __init__(self, parent: QWidget | None = None) -> None:
+ super().__init__(parent=parent)
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
+ return
+
+ def wheelEvent(self, event: QWheelEvent) -> None:
+ if self.hasFocus():
+ super().wheelEvent(event)
+ else:
+ event.ignore()
+ return
+
+# END Class NDoubleSpinBox
diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py
index 76709dbf..28404c50 100644
--- a/novelwriter/tools/manussettings.py
+++ b/novelwriter/tools/manussettings.py
@@ -30,17 +30,17 @@ from typing import TYPE_CHECKING
from PyQt5.QtGui import QFont, QIcon, QSyntaxHighlighter, QTextCharFormat, QTextDocument
from PyQt5.QtCore import QEvent, QSize, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
- QAbstractButton, QAbstractItemView, QComboBox, QDialog, QDialogButtonBox,
- QDoubleSpinBox, QFontDialog, QFrame, QGridLayout, QHBoxLayout, QHeaderView,
- QLabel, QLineEdit, QMenu, QPlainTextEdit, QPushButton, QSpinBox, QSplitter,
- QStackedWidget, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
- QWidget
+ QAbstractButton, QAbstractItemView, QDialog, QDialogButtonBox,
+ QFontDialog, QFrame, QGridLayout, QHBoxLayout, QHeaderView, QLabel,
+ QLineEdit, QMenu, QPlainTextEdit, QPushButton, QSplitter, QStackedWidget,
+ QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwHeadFmt, nwLabels, trConst
from novelwriter.core.buildsettings import BuildSettings, FilterMode
from novelwriter.extensions.switch import NSwitch
+from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NSpinBox
from novelwriter.extensions.switchbox import NSwitchBox
from novelwriter.extensions.configlayout import (
NColourLabel, NFixedPage, NScrollableForm, NScrollablePage
@@ -968,7 +968,7 @@ class _FormatTab(NScrollableForm):
)
# Font Size
- self.textSize = QSpinBox(self)
+ self.textSize = NSpinBox(self)
self.textSize.setMinimum(8)
self.textSize.setMaximum(60)
self.textSize.setSingleStep(1)
@@ -976,7 +976,7 @@ class _FormatTab(NScrollableForm):
self.addRow(self._build.getLabel("format.textSize"), self.textSize, unit="pt")
# Line Height
- self.lineHeight = QDoubleSpinBox(self)
+ self.lineHeight = NDoubleSpinBox(self)
self.lineHeight.setFixedWidth(spW)
self.lineHeight.setMinimum(0.75)
self.lineHeight.setMaximum(3.0)
@@ -1002,34 +1002,34 @@ class _FormatTab(NScrollableForm):
self.addGroupLabel(self._build.getLabel("format.grpPage"))
- self.pageUnit = QComboBox(self)
+ self.pageUnit = NComboBox(self)
for key, name in nwLabels.UNIT_NAME.items():
self.pageUnit.addItem(trConst(name), key)
- self.pageSize = QComboBox(self)
+ self.pageSize = NComboBox(self)
for key, name in nwLabels.PAPER_NAME.items():
self.pageSize.addItem(trConst(name), key)
- self.pageWidth = QDoubleSpinBox(self)
+ self.pageWidth = NDoubleSpinBox(self)
self.pageWidth.setFixedWidth(dbW)
self.pageWidth.setMaximum(500.0)
self.pageWidth.valueChanged.connect(self._pageSizeValueChanged)
- self.pageHeight = QDoubleSpinBox(self)
+ self.pageHeight = NDoubleSpinBox(self)
self.pageHeight.setFixedWidth(dbW)
self.pageHeight.setMaximum(500.0)
self.pageHeight.valueChanged.connect(self._pageSizeValueChanged)
- self.topMargin = QDoubleSpinBox(self)
+ self.topMargin = NDoubleSpinBox(self)
self.topMargin.setFixedWidth(dbW)
- self.bottomMargin = QDoubleSpinBox(self)
+ self.bottomMargin = NDoubleSpinBox(self)
self.bottomMargin.setFixedWidth(dbW)
- self.leftMargin = QDoubleSpinBox(self)
+ self.leftMargin = NDoubleSpinBox(self)
self.leftMargin.setFixedWidth(dbW)
- self.rightMargin = QDoubleSpinBox(self)
+ self.rightMargin = NDoubleSpinBox(self)
self.rightMargin.setFixedWidth(dbW)
self.addRow(self._build.getLabel("format.pageUnit"), self.pageUnit)
@@ -1232,7 +1232,7 @@ class _OutputTab(NScrollableForm):
button=self.btnPageHeader, stretch=(1, 1)
)
- self.odtPageCountOffset = QSpinBox(self)
+ self.odtPageCountOffset = NSpinBox(self)
self.odtPageCountOffset.setMinimum(0)
self.odtPageCountOffset.setMaximum(999)
self.odtPageCountOffset.setSingleStep(1)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index c7357c48..98c3520b 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -34,9 +34,9 @@ from PyQt5.QtCore import (
pyqtSignal, pyqtSlot
)
from PyQt5.QtWidgets import (
- QComboBox, QDialog, QDialogButtonBox, QFileDialog, QFormLayout,
- QHBoxLayout, QLabel, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
- QSpinBox, QStackedWidget, QStyle, QStyleOptionViewItem, QStyledItemDelegate,
+ QDialog, QDialogButtonBox, QFileDialog, QFormLayout, QHBoxLayout, QLabel,
+ QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
+ QStackedWidget, QStyle, QStyleOptionViewItem, QStyledItemDelegate,
QToolButton, QVBoxLayout, QWidget, qApp
)
@@ -46,6 +46,7 @@ from novelwriter.common import formatInt, formatVersion, makeFileNameSafe
from novelwriter.constants import nwUnicode
from novelwriter.core.coretools import ProjectBuilder
from novelwriter.extensions.switch import NSwitch
+from novelwriter.extensions.modified import NComboBox, NSpinBox
logger = logging.getLogger(__name__)
@@ -534,7 +535,7 @@ class _NewProjectForm(QWidget):
self.projAuthor.setPlaceholderText(self.tr("Optional"))
# Project Language
- self.projLang = QComboBox(self)
+ self.projLang = NComboBox(self)
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
self.projLang.addItem(language, tag)
@@ -594,7 +595,7 @@ class _NewProjectForm(QWidget):
# Chapters and Scenes
# ===================
- self.numChapters = QSpinBox()
+ self.numChapters = NSpinBox(self)
self.numChapters.setRange(0, 200)
self.numChapters.setValue(5)
self.numChapters.setToolTip(self.tr("Set to 0 to only add scenes"))
@@ -605,7 +606,7 @@ class _NewProjectForm(QWidget):
self.chapterBox.addWidget(QLabel(self.tr("chapter documents")))
self.chapterBox.addStretch(1)
- self.numScenes = QSpinBox()
+ self.numScenes = NSpinBox(self)
self.numScenes.setRange(0, 200)
self.numScenes.setValue(5)
From 68a63908680146a10093953ce74d1b21ab2cc352 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 1 Feb 2024 23:35:30 +0100
Subject: [PATCH 7/7] Add test coverage of modified widgets
---
tests/test_ext/test_ext_modified.py | 132 ++++++++++++++++++++++++++++
tests/tools.py | 22 ++++-
2 files changed, 153 insertions(+), 1 deletion(-)
create mode 100644 tests/test_ext/test_ext_modified.py
diff --git a/tests/test_ext/test_ext_modified.py b/tests/test_ext/test_ext_modified.py
new file mode 100644
index 00000000..c1d0e3c2
--- /dev/null
+++ b/tests/test_ext/test_ext_modified.py
@@ -0,0 +1,132 @@
+"""
+novelWriter – Wheel Event Filter Tester
+=======================================
+
+This file is a part of novelWriter
+Copyright 2018–2024, 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 .
+"""
+from __future__ import annotations
+
+import pytest
+
+from tools import SimpleDialog
+
+from PyQt5.QtGui import QWheelEvent
+from PyQt5.QtCore import QPoint, QPointF, Qt
+
+from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NSpinBox
+
+
+class MockWheelEvent(QWheelEvent):
+
+ def __init__(self):
+ super().__init__(
+ QPointF(0, 0), QPointF(0, 0), QPoint(0, 0), QPoint(0, 0),
+ Qt.MouseButton.NoButton, Qt.KeyboardModifier.NoModifier,
+ Qt.ScrollPhase.ScrollUpdate, False
+ )
+ self.ignored = False
+ return
+
+ def ignore(self):
+ super().ignore()
+ self.ignored = True
+ return
+
+# END Class MockWheelEvent
+
+
+@pytest.mark.gui
+def testExtModified_NComboBox(qtbot, monkeypatch):
+ """Test the NComboBox class."""
+ widget = NComboBox()
+ widget.addItem("Item 1", 1)
+ widget.addItem("Item 2", 2)
+
+ dialog = SimpleDialog(widget)
+ dialog.show()
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NComboBox, "hasFocus", lambda *a: True)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is False
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NComboBox, "hasFocus", lambda *a: False)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is True
+
+ # qtbot.stop()
+
+# END Test testExtModified_NComboBox
+
+
+@pytest.mark.gui
+def testExtModified_NSpinBox(qtbot, monkeypatch):
+ """Test the NSpinBox class."""
+ widget = NSpinBox()
+ widget.setMinimum(0)
+ widget.setMaximum(100)
+ widget.setValue(42)
+
+ dialog = SimpleDialog(widget)
+ dialog.show()
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NSpinBox, "hasFocus", lambda *a: True)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is False
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NSpinBox, "hasFocus", lambda *a: False)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is True
+
+ # qtbot.stop()
+
+# END Test testExtModified_NSpinBox
+
+
+@pytest.mark.gui
+def testExtModified_NDoubleSpinBox(qtbot, monkeypatch):
+ """Test the NDoubleSpinBox class."""
+ widget = NDoubleSpinBox()
+ widget.setMinimum(0.0)
+ widget.setMaximum(100.0)
+ widget.setValue(42.0)
+
+ dialog = SimpleDialog(widget)
+ dialog.show()
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NDoubleSpinBox, "hasFocus", lambda *a: True)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is False
+
+ with monkeypatch.context() as mp:
+ mp.setattr(NDoubleSpinBox, "hasFocus", lambda *a: False)
+ event = MockWheelEvent()
+ widget.wheelEvent(event)
+ assert event.ignored is True
+
+ # qtbot.stop()
+
+# END Test testExtModified_NDoubleSpinBox
diff --git a/tests/tools.py b/tests/tools.py
index 2da23818..7a00b4b6 100644
--- a/tests/tools.py
+++ b/tests/tools.py
@@ -25,7 +25,7 @@ import shutil
from pathlib import Path
from datetime import datetime
-from PyQt5.QtWidgets import qApp
+from PyQt5.QtWidgets import QDialog, QVBoxLayout, QWidget, qApp
XML_IGNORE = (" None:
nwGUI.rebuildTrees()
return
+
+
+class SimpleDialog(QDialog):
+
+ def __init__(self, widget: QWidget) -> None:
+ super().__init__()
+ self._widget = widget
+
+ layout = QVBoxLayout()
+ layout.addWidget(widget)
+ layout.setContentsMargins(40, 40, 40, 40)
+ self.setLayout(layout)
+
+ return
+
+ @property
+ def widget(self) -> QWidget:
+ return self._widget
+
+# END Class TestDialog