Improvements functionality of various dialogs (#1681)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -197,6 +198,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
|
||||
@@ -249,8 +253,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 +281,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 +303,7 @@ class _OpenProjectPage(QWidget):
|
||||
).format(index.data()[0])
|
||||
if SHARED.question(text):
|
||||
self.listModel.removeEntry(index)
|
||||
self._selectFirstItem()
|
||||
return
|
||||
|
||||
@pyqtSlot("QPoint")
|
||||
@@ -315,6 +319,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
|
||||
|
||||
|
||||
@@ -355,7 +370,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)
|
||||
|
||||
@@ -520,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)
|
||||
|
||||
@@ -580,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"))
|
||||
@@ -591,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)
|
||||
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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
|
||||
@@ -152,11 +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
|
||||
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posOne, delay=10)
|
||||
qtbot.keyClick(tabOpen, Qt.Key.Key_Delete, delay=10)
|
||||
assert len(CONFIG.recentProjects.listEntries()) == 0
|
||||
|
||||
# qtbot.stop()
|
||||
welcome.close()
|
||||
|
||||
|
||||
+21
-1
@@ -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 = ("<novelWriterXML", "<project")
|
||||
ODT_IGNORE = ("<meta:generator", "<meta:creation-date", "<dc:date", "<meta:editing")
|
||||
@@ -214,3 +214,23 @@ def buildTestProject(obj: object, projPath: Path) -> 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
|
||||
|
||||
Reference in New Issue
Block a user