Rewrite Preferences dialog (#1652)
This commit is contained in:
@@ -61,6 +61,7 @@ forward = typ_chevron-right.svg
|
||||
maximise = typ_arrow-maximise.svg
|
||||
menu = typ_th-dot-menu.svg
|
||||
minimise = typ_arrow-minimise.svg
|
||||
more = typ_th-dot-more.svg
|
||||
noncheckable = mixed_input-none.svg
|
||||
panel = nw_panel.svg
|
||||
proj_chapter = mixed_document-chapter.svg
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 22,12 A 2.6315,2.6315 0 0 0 19.3686,9.3686 2.6315,2.6315 0 0 0 16.7372,12 2.6315,2.6315 0 0 0 19.3686,14.6314 2.6315,2.6315 0 0 0 22,12 Z m -7.3686,0 A 2.6315,2.6315 0 0 0 12,9.3686 2.6315,2.6315 0 0 0 9.3686,12 2.6315,2.6315 0 0 0 12,14.6314 2.6315,2.6315 0 0 0 14.6314,12 Z M 7.2628,12 A 2.6315,2.6315 0 0 0 4.6314,9.3686 2.6315,2.6315 0 0 0 2,12 2.6315,2.6315 0 0 0 4.6314,14.6314 2.6315,2.6315 0 0 0 7.2628,12 Z" fill="#aeaeae" stroke-width=".90909"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 612 B |
@@ -61,6 +61,7 @@ forward = typ_chevron-right.svg
|
||||
maximise = typ_arrow-maximise.svg
|
||||
menu = typ_th-dot-menu.svg
|
||||
minimise = typ_arrow-minimise.svg
|
||||
more = typ_th-dot-more.svg
|
||||
noncheckable = mixed_input-none.svg
|
||||
panel = nw_panel.svg
|
||||
proj_chapter = mixed_document-chapter.svg
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 22,12 A 2.6315,2.6315 0 0 0 19.3686,9.3686 2.6315,2.6315 0 0 0 16.7372,12 2.6315,2.6315 0 0 0 19.3686,14.6314 2.6315,2.6315 0 0 0 22,12 Z m -7.3686,0 A 2.6315,2.6315 0 0 0 12,9.3686 2.6315,2.6315 0 0 0 9.3686,12 2.6315,2.6315 0 0 0 12,14.6314 2.6315,2.6315 0 0 0 14.6314,12 Z M 7.2628,12 A 2.6315,2.6315 0 0 0 4.6314,9.3686 2.6315,2.6315 0 0 0 2,12 2.6315,2.6315 0 0 0 4.6314,14.6314 2.6315,2.6315 0 0 0 7.2628,12 Z" fill-opacity=".78039" stroke-width=".90909"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 619 B |
@@ -150,9 +150,6 @@ class Config:
|
||||
self.autoScrollPos = 30 # Start point for typewriter-like scrolling
|
||||
self.scrollPastEnd = True # Scroll past end of document, and centre cursor
|
||||
|
||||
self.wordCountTimer = 5.0 # Interval for word count update in seconds
|
||||
self.incNotesWCount = True # The status bar word count includes notes
|
||||
|
||||
self.highlightQuotes = True # Highlight text in quotes
|
||||
self.allowOpenSQuote = False # Allow open-ended single quotes
|
||||
self.allowOpenDQuote = True # Allow open-ended double quotes
|
||||
@@ -160,6 +157,7 @@ class Config:
|
||||
|
||||
self.stopWhenIdle = True # Stop the status bar clock when the user is idle
|
||||
self.userIdleTime = 300 # Time of inactivity to consider user idle
|
||||
self.incNotesWCount = True # The status bar word count includes notes
|
||||
|
||||
# User-Selected Symbol Settings
|
||||
self.fmtApostrophe = nwUnicode.U_RSQUO
|
||||
@@ -591,7 +589,6 @@ class Config:
|
||||
self.showTabsNSpaces = conf.rdBool(sec, "showtabsnspaces", self.showTabsNSpaces)
|
||||
self.showLineEndings = conf.rdBool(sec, "showlineendings", self.showLineEndings)
|
||||
self.showMultiSpaces = conf.rdBool(sec, "showmultispaces", self.showMultiSpaces)
|
||||
self.wordCountTimer = conf.rdFlt(sec, "wordcounttimer", self.wordCountTimer)
|
||||
self.incNotesWCount = conf.rdBool(sec, "incnoteswcount", self.incNotesWCount)
|
||||
self.showFullPath = conf.rdBool(sec, "showfullpath", self.showFullPath)
|
||||
self.highlightQuotes = conf.rdBool(sec, "highlightquotes", self.highlightQuotes)
|
||||
@@ -698,7 +695,6 @@ class Config:
|
||||
"showtabsnspaces": str(self.showTabsNSpaces),
|
||||
"showlineendings": str(self.showLineEndings),
|
||||
"showmultispaces": str(self.showMultiSpaces),
|
||||
"wordcounttimer": str(self.wordCountTimer),
|
||||
"incnoteswcount": str(self.incNotesWCount),
|
||||
"showfullpath": str(self.showFullPath),
|
||||
"highlightquotes": str(self.highlightQuotes),
|
||||
|
||||
@@ -56,6 +56,9 @@ class nwConst:
|
||||
# Gui Settings
|
||||
STATUS_MSG_TIMEOUT = 15000 # milliseconds
|
||||
|
||||
# Dialogs
|
||||
DLG_FINISHED = 2
|
||||
|
||||
# END Class nwConst
|
||||
|
||||
|
||||
|
||||
+519
-611
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtGui import QCloseEvent, QFontMetrics
|
||||
from PyQt5.QtGui import QFontMetrics
|
||||
from PyQt5.QtCore import QSize, Qt, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QLabel, QListWidget,
|
||||
@@ -40,11 +40,11 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuiQuoteSelect(QDialog):
|
||||
|
||||
selectedQuote = ""
|
||||
_selected = ""
|
||||
|
||||
D_KEY = Qt.ItemDataRole.UserRole
|
||||
|
||||
def __init__(self, parent: QWidget, currentQuote: str = '"') -> None:
|
||||
def __init__(self, parent: QWidget, current: str = '"') -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiQuoteSelect")
|
||||
@@ -54,7 +54,7 @@ class GuiQuoteSelect(QDialog):
|
||||
self.innerBox = QHBoxLayout()
|
||||
self.labelBox = QVBoxLayout()
|
||||
|
||||
self.selectedQuote = currentQuote
|
||||
self._selected = current
|
||||
|
||||
qMetrics = QFontMetrics(self.font())
|
||||
pxW = 7*qMetrics.boundingRectChar("M").width()
|
||||
@@ -65,7 +65,7 @@ class GuiQuoteSelect(QDialog):
|
||||
lblFont.setPointSizeF(4*lblFont.pointSizeF())
|
||||
|
||||
# Preview Label
|
||||
self.previewLabel = QLabel(currentQuote)
|
||||
self.previewLabel = QLabel(current)
|
||||
self.previewLabel.setFont(lblFont)
|
||||
self.previewLabel.setFixedSize(QSize(pxW, pxH))
|
||||
self.previewLabel.setAlignment(Qt.AlignCenter)
|
||||
@@ -82,7 +82,7 @@ class GuiQuoteSelect(QDialog):
|
||||
qtItem = QListWidgetItem(theText)
|
||||
qtItem.setData(self.D_KEY, sKey)
|
||||
self.listBox.addItem(qtItem)
|
||||
if sKey == currentQuote:
|
||||
if sKey == current:
|
||||
self.listBox.setCurrentItem(qtItem)
|
||||
|
||||
self.listBox.setMinimumWidth(minSize + CONFIG.pxInt(40))
|
||||
@@ -113,15 +113,20 @@ class GuiQuoteSelect(QDialog):
|
||||
logger.debug("Delete: GuiQuoteSelect")
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
@property
|
||||
def selectedQuote(self) -> str:
|
||||
"""Return the selected quote symbol."""
|
||||
return self._selected
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
@classmethod
|
||||
def getQuote(cls, parent: QWidget, current: str = "") -> tuple[str, bool]:
|
||||
"""Pop the dialog and return the result."""
|
||||
cls = GuiQuoteSelect(parent, current=current)
|
||||
cls.exec_()
|
||||
quote = cls._selected
|
||||
accepted = cls.result() == QDialog.DialogCode.Accepted
|
||||
cls.deleteLater()
|
||||
return quote, accepted
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
@@ -130,11 +135,10 @@ class GuiQuoteSelect(QDialog):
|
||||
@pyqtSlot()
|
||||
def _selectedSymbol(self) -> None:
|
||||
"""Update the preview label and the selected quote style."""
|
||||
selItems = self.listBox.selectedItems()
|
||||
if selItems:
|
||||
theSymbol = selItems[0].data(self.D_KEY)
|
||||
self.previewLabel.setText(theSymbol)
|
||||
self.selectedQuote = theSymbol
|
||||
if items := self.listBox.selectedItems():
|
||||
quote = items[0].data(self.D_KEY)
|
||||
self.previewLabel.setText(quote)
|
||||
self._selected = quote
|
||||
return
|
||||
|
||||
# END Class GuiQuoteSelect
|
||||
|
||||
@@ -3,7 +3,9 @@ novelWriter – Custom Widget: Config Layout
|
||||
==========================================
|
||||
|
||||
File History:
|
||||
Created: 2020-05-03 [0.4.5]
|
||||
Created: 2020-05-03 [0.4.5] NConfigLayout, NHelpLabel
|
||||
Created: 2023-05-23 [2.1b1] NSimpleLayout
|
||||
Created: 2024-01-08 [2.3b1] NScrollableForm
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2024, Veronica Berglyd Olsen
|
||||
@@ -26,7 +28,7 @@ from __future__ import annotations
|
||||
from PyQt5.QtGui import QColor, QPalette
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QSizePolicy,
|
||||
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea, QSizePolicy,
|
||||
QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
@@ -37,6 +39,133 @@ RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
|
||||
LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
|
||||
|
||||
|
||||
class NScrollableForm(QScrollArea):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self._helpCol = QColor(0, 0, 0)
|
||||
self._fontScale = FONT_SCALE
|
||||
self._first = True
|
||||
|
||||
self._sections: dict[int, QLabel] = {}
|
||||
self._editable: dict[str, NHelpLabel] = {}
|
||||
self._index: dict[str, QWidget] = {}
|
||||
|
||||
self._layout = QVBoxLayout()
|
||||
self._layout.setSpacing(CONFIG.pxInt(12))
|
||||
|
||||
self._widget = QWidget(self)
|
||||
self._widget.setLayout(self._layout)
|
||||
|
||||
self.setWidget(self._widget)
|
||||
self.setWidgetResizable(True)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Properties
|
||||
##
|
||||
|
||||
@property
|
||||
def labels(self) -> list[str]:
|
||||
return list(self._index.keys())
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple, scale: float = FONT_SCALE) -> None:
|
||||
"""Set the text color for the help text."""
|
||||
self._helpCol = color if isinstance(color, QColor) else QColor(*color)
|
||||
self._fontScale = scale
|
||||
return
|
||||
|
||||
def setHelpText(self, key: str, text: str) -> None:
|
||||
"""Set the text for the help label."""
|
||||
if qHelp := self._editable.get(key):
|
||||
qHelp.setText(text)
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def scrollToSection(self, identifier: int) -> None:
|
||||
"""Scroll to the requested section identifier."""
|
||||
if identifier in self._sections:
|
||||
yPos = self._sections[identifier].pos().y() - CONFIG.pxInt(8)
|
||||
self.verticalScrollBar().setValue(yPos)
|
||||
return
|
||||
|
||||
def scrollToLabel(self, label: str) -> None:
|
||||
"""Scroll to the requested label."""
|
||||
if label in self._index:
|
||||
yPos = self._index[label].pos().y() - CONFIG.pxInt(8)
|
||||
self.verticalScrollBar().setValue(yPos)
|
||||
return
|
||||
|
||||
def addGroupLabel(self, label: str, identifier: int) -> None:
|
||||
"""Add a text label to separate groups of settings."""
|
||||
hM = CONFIG.pxInt(4)
|
||||
qLabel = QLabel(f"<b>{label}</b>", self)
|
||||
qLabel.setContentsMargins(0, hM, 0, hM)
|
||||
if not self._first:
|
||||
self._layout.addSpacing(5*hM)
|
||||
self._layout.addWidget(qLabel)
|
||||
self._sections[identifier] = qLabel
|
||||
self._first = False
|
||||
return
|
||||
|
||||
def addRow(self, label: str, widget: QWidget, helpText: str = "", unit: str | None = None,
|
||||
button: QWidget | None = None, editable: str | None = None) -> None:
|
||||
"""Add a label and a widget as a new row of the form."""
|
||||
row = QHBoxLayout()
|
||||
row.setSpacing(CONFIG.pxInt(4))
|
||||
|
||||
mPx = CONFIG.pxInt(12)
|
||||
qLabel = QLabel(label, self)
|
||||
qLabel.setIndent(mPx)
|
||||
qLabel.setBuddy(widget)
|
||||
|
||||
if helpText:
|
||||
qHelp = NHelpLabel(str(helpText), self._helpCol, self._fontScale)
|
||||
qHelp.setIndent(mPx)
|
||||
labelBox = QVBoxLayout()
|
||||
labelBox.addWidget(qLabel)
|
||||
labelBox.addWidget(qHelp)
|
||||
labelBox.setSpacing(0)
|
||||
labelBox.addStretch(1)
|
||||
row.addLayout(labelBox)
|
||||
if editable:
|
||||
self._editable[editable] = qHelp
|
||||
else:
|
||||
row.addWidget(qLabel)
|
||||
|
||||
row.addSpacing(mPx)
|
||||
row.addWidget(widget)
|
||||
|
||||
if isinstance(unit, str):
|
||||
row.addWidget(QLabel(unit, self))
|
||||
elif isinstance(button, QAbstractButton):
|
||||
row.addWidget(button)
|
||||
|
||||
self._layout.addLayout(row)
|
||||
self._index[label.strip()] = widget
|
||||
self._first = False
|
||||
|
||||
return
|
||||
|
||||
def finalise(self) -> None:
|
||||
"""Finalise the layout when the form is built."""
|
||||
self._layout.addSpacing(CONFIG.pxInt(20))
|
||||
self._layout.addStretch(1)
|
||||
return
|
||||
|
||||
# END Class NScrollableForm
|
||||
|
||||
|
||||
class NConfigLayout(QGridLayout):
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -58,11 +187,10 @@ class NConfigLayout(QGridLayout):
|
||||
# Getters and Setters
|
||||
##
|
||||
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple,
|
||||
fontScale: float = FONT_SCALE) -> None:
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple, scale: float = FONT_SCALE) -> None:
|
||||
"""Set the text color for the help text."""
|
||||
self._helpCol = color if isinstance(color, QColor) else QColor(*color)
|
||||
self._fontScale = fontScale
|
||||
self._fontScale = scale
|
||||
return
|
||||
|
||||
def setHelpText(self, row: int, text: str) -> None:
|
||||
|
||||
@@ -26,7 +26,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
from PyQt5.QtGui import QColor, QPaintEvent, QPainter, QPolygon
|
||||
from PyQt5.QtCore import QPoint, QRectF, Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtCore import QPoint, QRectF, QSize, Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractButton, QAction, QButtonGroup, QLabel, QSizePolicy, QStyle,
|
||||
QStyleOptionToolButton, QToolBar, QToolButton, QWidget
|
||||
@@ -63,6 +63,10 @@ class NPagedSideBar(QToolBar):
|
||||
|
||||
return
|
||||
|
||||
def button(self, buttonId: int) -> _NPagedToolButton:
|
||||
"""Return a specific button."""
|
||||
return self._buttons[buttonId]
|
||||
|
||||
def setLabelColor(self, color: list | QColor) -> None:
|
||||
"""Set the text color for the labels."""
|
||||
self._labelCol = color if isinstance(color, QColor) else QColor(*color)
|
||||
@@ -136,6 +140,10 @@ class _NPagedToolButton(QToolButton):
|
||||
|
||||
return
|
||||
|
||||
def sizeHint(self) -> QSize:
|
||||
"""Return a size hint that includes the arrow."""
|
||||
return super().sizeHint() + QSize(4*self._aH, 0)
|
||||
|
||||
def paintEvent(self, event: QPaintEvent) -> None:
|
||||
"""Overload the paint event to draw a simple, left aligned text
|
||||
label, with a highlight when selected and a transparent base
|
||||
|
||||
@@ -24,31 +24,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
|
||||
from PyQt5.QtCore import QEvent, QPropertyAnimation, QRectF, Qt, pyqtProperty
|
||||
from PyQt5.QtCore import QEvent, QPropertyAnimation, Qt, pyqtProperty
|
||||
from PyQt5.QtWidgets import QAbstractButton, QSizePolicy, QWidget
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.constants import nwUnicode
|
||||
|
||||
|
||||
class NSwitch(QAbstractButton):
|
||||
|
||||
def __init__(self, parent: QWidget | None = None,
|
||||
width: int | None = None, height: int | None = None) -> None:
|
||||
__slots__ = ("_xW", "_xH", "_xR", "_rB", "_rH", "_rR", "_offset")
|
||||
|
||||
def __init__(self, parent: QWidget | None = None, width: int = 0, height: int = 0) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
if width is None:
|
||||
self._xW = CONFIG.pxInt(40)
|
||||
else:
|
||||
self._xW = width
|
||||
|
||||
if height is None:
|
||||
self._xH = CONFIG.pxInt(20)
|
||||
else:
|
||||
self._xH = height
|
||||
|
||||
self._xW = width or CONFIG.pxInt(40)
|
||||
self._xH = height or CONFIG.pxInt(20)
|
||||
self._xR = int(self._xH*0.5)
|
||||
self._xT = int(self._xH*0.6)
|
||||
self._rB = int(CONFIG.guiScale*2)
|
||||
self._rH = self._xH - 2*self._rB
|
||||
self._rR = self._xR - self._rB
|
||||
@@ -82,10 +73,7 @@ class NSwitch(QAbstractButton):
|
||||
def setChecked(self, checked: bool) -> None:
|
||||
"""Overload setChecked to also alter the offset."""
|
||||
super().setChecked(checked)
|
||||
if checked:
|
||||
self._offset = self._xW - self._xR
|
||||
else:
|
||||
self._offset = self._xR
|
||||
self._offset = (self._xW - self._xR) if checked else self._xR
|
||||
return
|
||||
|
||||
##
|
||||
@@ -95,53 +83,36 @@ class NSwitch(QAbstractButton):
|
||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||
"""Overload resize to ensure correct offset."""
|
||||
super().resizeEvent(event)
|
||||
if self.isChecked():
|
||||
self._offset = self._xW - self._xR
|
||||
else:
|
||||
self._offset = self._xR
|
||||
self._offset = (self._xW - self._xR) if self.isChecked() else self._xR
|
||||
return
|
||||
|
||||
def paintEvent(self, event: QPaintEvent) -> None:
|
||||
"""Drawing the switch itself."""
|
||||
qPaint = QPainter(self)
|
||||
qPaint.setRenderHint(QPainter.Antialiasing, True)
|
||||
qPaint.setPen(Qt.NoPen)
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing, True)
|
||||
painter.setPen(Qt.NoPen)
|
||||
|
||||
qPalette = self.palette()
|
||||
palette = self.palette()
|
||||
if self.isChecked():
|
||||
trackBrush = qPalette.highlight()
|
||||
thumbBrush = qPalette.highlightedText()
|
||||
textColor = qPalette.highlight().color()
|
||||
thumbText = nwUnicode.U_CHECK
|
||||
trackBrush = palette.highlight()
|
||||
thumbBrush = palette.highlightedText()
|
||||
else:
|
||||
trackBrush = qPalette.dark()
|
||||
thumbBrush = qPalette.light()
|
||||
textColor = qPalette.dark().color()
|
||||
thumbText = nwUnicode.U_CROSS
|
||||
trackBrush = palette.dark()
|
||||
thumbBrush = palette.light()
|
||||
|
||||
if self.isEnabled():
|
||||
trackOpacity = 1.0
|
||||
else:
|
||||
trackOpacity = 0.6
|
||||
trackBrush = qPalette.shadow()
|
||||
thumbBrush = qPalette.mid()
|
||||
textColor = qPalette.shadow().color()
|
||||
trackBrush = palette.shadow()
|
||||
thumbBrush = palette.mid()
|
||||
|
||||
qPaint.setBrush(trackBrush)
|
||||
qPaint.setOpacity(trackOpacity)
|
||||
qPaint.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
||||
painter.setBrush(trackBrush)
|
||||
painter.setOpacity(trackOpacity)
|
||||
painter.drawRoundedRect(0, 0, self._xW, self._xH, self._xR, self._xR)
|
||||
|
||||
qPaint.setBrush(thumbBrush)
|
||||
qPaint.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
|
||||
|
||||
font = qPaint.font()
|
||||
font.setPixelSize(self._xT)
|
||||
qPaint.setPen(textColor)
|
||||
qPaint.setFont(font)
|
||||
qPaint.drawText(
|
||||
QRectF(self._offset - self._rR, self._rB, self._rH, self._rH),
|
||||
Qt.AlignCenter, thumbText
|
||||
)
|
||||
painter.setBrush(thumbBrush)
|
||||
painter.drawEllipse(self._offset - self._rR, self._rB, self._rH, self._rH)
|
||||
|
||||
return
|
||||
|
||||
@@ -149,14 +120,11 @@ class NSwitch(QAbstractButton):
|
||||
"""Animate the switch on mouse release."""
|
||||
super().mouseReleaseEvent(event)
|
||||
if event.button() == Qt.LeftButton:
|
||||
doAnim = QPropertyAnimation(self, b"offset", self)
|
||||
doAnim.setDuration(120)
|
||||
doAnim.setStartValue(self._offset)
|
||||
if self.isChecked():
|
||||
doAnim.setEndValue(self._xW - self._xR)
|
||||
else:
|
||||
doAnim.setEndValue(self._xR)
|
||||
doAnim.start()
|
||||
anim = QPropertyAnimation(self, b"offset", self)
|
||||
anim.setDuration(120)
|
||||
anim.setStartValue(self._offset)
|
||||
anim.setEndValue((self._xW - self._xR) if self.isChecked() else self._xR)
|
||||
anim.start()
|
||||
return
|
||||
|
||||
def enterEvent(self, event: QEvent) -> None:
|
||||
|
||||
@@ -187,13 +187,12 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
# Set Up Document Word Counter
|
||||
self.wcTimerDoc = QTimer()
|
||||
self.wcTimerDoc.timeout.connect(self._runDocCounter)
|
||||
self.wcTimerDoc.setInterval(5000)
|
||||
|
||||
self.wCounterDoc = BackgroundWordCounter(self)
|
||||
self.wCounterDoc.setAutoDelete(False)
|
||||
self.wCounterDoc.signals.countsReady.connect(self._updateDocCounts)
|
||||
|
||||
self.wcInterval = CONFIG.wordCountTimer
|
||||
|
||||
# Set Up Selection Word Counter
|
||||
self.wcTimerSel = QTimer()
|
||||
self.wcTimerSel.timeout.connect(self._runSelCounter)
|
||||
@@ -320,10 +319,10 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
SHARED.updateSpellCheckLanguage()
|
||||
|
||||
# Set font
|
||||
textFont = QFont()
|
||||
textFont.setFamily(CONFIG.textFont)
|
||||
textFont.setPointSize(CONFIG.textSize)
|
||||
self.setFont(textFont)
|
||||
font = QFont()
|
||||
font.setFamily(CONFIG.textFont)
|
||||
font.setPointSize(CONFIG.textSize)
|
||||
self.setFont(font)
|
||||
|
||||
# Set default text margins
|
||||
# Due to cursor visibility, a part of the margin must be
|
||||
@@ -359,17 +358,14 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
# Refresh the tab stops
|
||||
self.setTabStopDistance(CONFIG.getTabWidth())
|
||||
|
||||
# Configure word count timer
|
||||
self.wcInterval = CONFIG.wordCountTimer
|
||||
self.wcTimerDoc.setInterval(int(self.wcInterval*1000))
|
||||
|
||||
# If we have a document open, we should reload it in case the
|
||||
# If we have a document open, we should refresh it in case the
|
||||
# font changed, otherwise we just clear the editor entirely,
|
||||
# which makes it read only.
|
||||
if self._docHandle is None:
|
||||
self.clearEditor()
|
||||
else:
|
||||
if self._docHandle:
|
||||
self._qDocument.syntaxHighlighter.rehighlight()
|
||||
self.docHeader.setTitleFromHandle(self._docHandle)
|
||||
else:
|
||||
self.clearEditor()
|
||||
|
||||
return
|
||||
|
||||
@@ -1190,7 +1186,7 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
logger.debug("Word counter is busy")
|
||||
return
|
||||
|
||||
if time() - self._lastEdit < 5.0 * self.wcInterval:
|
||||
if time() - self._lastEdit < 25.0:
|
||||
logger.debug("Running word counter")
|
||||
SHARED.runInThreadPool(self.wCounterDoc)
|
||||
|
||||
|
||||
@@ -142,10 +142,10 @@ class GuiDocViewer(QTextBrowser):
|
||||
self._makeStyleSheet()
|
||||
|
||||
# Set Font
|
||||
textFont = QFont()
|
||||
textFont.setFamily(CONFIG.textFont)
|
||||
textFont.setPointSize(CONFIG.textSize)
|
||||
self.setFont(textFont)
|
||||
font = QFont()
|
||||
font.setFamily(CONFIG.textFont)
|
||||
font.setPointSize(CONFIG.textSize)
|
||||
self.setFont(font)
|
||||
|
||||
# Set the widget colours to match syntax theme
|
||||
mainPalette = self.palette()
|
||||
@@ -164,10 +164,10 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
# Set default text margins
|
||||
self.document().setDocumentMargin(0)
|
||||
theOpt = QTextOption()
|
||||
options = QTextOption()
|
||||
if CONFIG.doJustify:
|
||||
theOpt.setAlignment(Qt.AlignmentFlag.AlignJustify)
|
||||
self.document().setDefaultTextOption(theOpt)
|
||||
options.setAlignment(Qt.AlignmentFlag.AlignJustify)
|
||||
self.document().setDefaultTextOption(options)
|
||||
|
||||
# Scroll bars
|
||||
if CONFIG.hideVScroll:
|
||||
|
||||
@@ -466,7 +466,7 @@ class GuiIcons:
|
||||
|
||||
# General Button Icons
|
||||
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
|
||||
"document", "down", "edit", "export", "forward", "maximise", "menu", "minimise",
|
||||
"document", "down", "edit", "export", "forward", "maximise", "menu", "minimise", "more",
|
||||
"noncheckable", "panel", "refresh", "remove", "revert", "search_replace", "search",
|
||||
"settings", "star", "unchecked", "up", "view",
|
||||
|
||||
|
||||
@@ -57,7 +57,6 @@ spellcheck = en
|
||||
showtabsnspaces = False
|
||||
showlineendings = False
|
||||
showmultispaces = True
|
||||
wordcounttimer = 5.0
|
||||
incnoteswcount = True
|
||||
showfullpath = True
|
||||
highlightquotes = True
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
[Meta]
|
||||
timestamp = 2023-12-29 14:09:13
|
||||
|
||||
[Main]
|
||||
theme = default
|
||||
syntax = default_light
|
||||
font = Cantarell
|
||||
fontsize = 12
|
||||
localisation = en_GB
|
||||
hidevscroll = True
|
||||
hidehscroll = True
|
||||
lastnotes = 0x0
|
||||
lastpath =
|
||||
|
||||
[Sizes]
|
||||
mainwindow = 1200, 650
|
||||
welcome = 800, 500
|
||||
preferences = 713, 614
|
||||
mainpane = 300, 800
|
||||
viewpane = 500, 150
|
||||
outlinepane = 500, 150
|
||||
|
||||
[Project]
|
||||
autosaveproject = 40
|
||||
autosavedoc = 20
|
||||
emphlabels = True
|
||||
backuppath = some/dir
|
||||
backuponclose = True
|
||||
askbeforebackup = True
|
||||
|
||||
[Editor]
|
||||
textfont = None
|
||||
textsize = 13
|
||||
width = 700
|
||||
margin = 45
|
||||
tabwidth = 45
|
||||
focuswidth = 900
|
||||
hidefocusfooter = True
|
||||
justify = True
|
||||
autoselect = False
|
||||
autoreplace = False
|
||||
repsquotes = True
|
||||
repdquotes = True
|
||||
repdash = True
|
||||
repdots = True
|
||||
autoscroll = True
|
||||
autoscrollpos = 30
|
||||
scrollpastend = True
|
||||
fmtsquoteopen = ‘
|
||||
fmtsquoteclose = ’
|
||||
fmtdquoteopen = “
|
||||
fmtdquoteclose = ”
|
||||
fmtpadbefore =
|
||||
fmtpadafter =
|
||||
fmtpadthin = False
|
||||
spellcheck = en
|
||||
showtabsnspaces = True
|
||||
showlineendings = True
|
||||
showmultispaces = True
|
||||
wordcounttimer = 5.0
|
||||
incnoteswcount = True
|
||||
showfullpath = False
|
||||
highlightquotes = False
|
||||
allowopensquote = False
|
||||
allowopendquote = True
|
||||
highlightemph = False
|
||||
stopwhenidle = True
|
||||
useridletime = 300
|
||||
|
||||
[State]
|
||||
showviewerpanel = True
|
||||
showedittoolbar = False
|
||||
useshortcodes = False
|
||||
viewcomments = True
|
||||
viewsynopsis = True
|
||||
searchcase = False
|
||||
searchword = False
|
||||
searchregex = False
|
||||
searchloop = False
|
||||
searchnextfile = False
|
||||
searchmatchcap = False
|
||||
|
||||
@@ -31,8 +31,10 @@ from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgOther_QuoteSelect(qtbot, nwGUI):
|
||||
def testDlgOther_QuoteSelect(qtbot, monkeypatch, nwGUI):
|
||||
"""Test the quote symbols dialog."""
|
||||
monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *a: None)
|
||||
|
||||
nwQuot = GuiQuoteSelect(nwGUI)
|
||||
nwQuot.show()
|
||||
|
||||
@@ -41,16 +43,25 @@ def testDlgOther_QuoteSelect(qtbot, nwGUI):
|
||||
anItem = nwQuot.listBox.item(i)
|
||||
assert isinstance(anItem, QListWidgetItem)
|
||||
nwQuot.listBox.clearSelection()
|
||||
nwQuot.listBox.setCurrentItem(anItem, QItemSelectionModel.Select)
|
||||
nwQuot.listBox.setCurrentItem(anItem, QItemSelectionModel.SelectionFlag.Select)
|
||||
lastItem = anItem.text()[2]
|
||||
assert nwQuot.previewLabel.text() == lastItem
|
||||
|
||||
nwQuot.accept()
|
||||
assert nwQuot.result() == QDialog.Accepted
|
||||
assert nwQuot.selectedQuote == lastItem
|
||||
nwQuot.close()
|
||||
|
||||
# Test Class Method
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "result", lambda *a: QDialog.DialogCode.Accepted)
|
||||
assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", True)
|
||||
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "result", lambda *a: QDialog.DialogCode.Rejected)
|
||||
assert GuiQuoteSelect.getQuote(nwGUI, current="X") == ("X", False)
|
||||
|
||||
# qtbot.stop()
|
||||
nwQuot.close()
|
||||
|
||||
# END Test testDlgOther_QuoteSelect
|
||||
|
||||
|
||||
@@ -22,191 +22,389 @@ from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
from tools import cmpFiles, getGuiItem
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialogButtonBox, QDialog, QAction, QFileDialog, QFontDialog
|
||||
)
|
||||
from PyQt5.QtGui import QFontDatabase, QKeyEvent
|
||||
from PyQt5.QtCore import QEvent, Qt
|
||||
from PyQt5.QtWidgets import QDialogButtonBox, QFileDialog, QFontDialog
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||
from novelwriter.constants import nwConst, nwUnicode
|
||||
from novelwriter.dialogs.preferences import GuiPreferences
|
||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||
|
||||
KEY_DELAY = 1
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths):
|
||||
"""Test the preferences dialog."""
|
||||
monkeypatch.setattr(GuiPreferences, "exec_", lambda *a: None)
|
||||
monkeypatch.setattr(GuiPreferences, "result", lambda *a: QDialog.Accepted)
|
||||
"""Test the preferences dialog loading."""
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
|
||||
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
|
||||
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
|
||||
nwPrefs = getGuiItem("GuiPreferences")
|
||||
assert isinstance(nwPrefs, GuiPreferences)
|
||||
nwPrefs.show()
|
||||
# Load GUI with standard values
|
||||
prefs = GuiPreferences(nwGUI)
|
||||
prefs.show()
|
||||
|
||||
# General Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabGeneral = nwPrefs.tabGeneral
|
||||
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
|
||||
# Check Languages
|
||||
languages = [prefs.guiLocale.itemData(i) for i in range(prefs.guiLocale.count())]
|
||||
assert len(languages) > 0
|
||||
assert "en_GB" in languages
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert tabGeneral.showFullPath.isChecked()
|
||||
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
|
||||
assert not tabGeneral.showFullPath.isChecked()
|
||||
# Check GUI Themes
|
||||
themes = [prefs.guiTheme.itemData(i) for i in range(prefs.guiTheme.count())]
|
||||
assert len(themes) >= 5
|
||||
assert "default" in themes
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabGeneral.hideVScroll.isChecked()
|
||||
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
|
||||
assert tabGeneral.hideVScroll.isChecked()
|
||||
# Check GUI Syntax
|
||||
syntax = [prefs.guiSyntax.itemData(i) for i in range(prefs.guiSyntax.count())]
|
||||
assert len(syntax) >= 10
|
||||
assert "default_dark" in syntax
|
||||
assert "default_light" in syntax
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabGeneral.hideHScroll.isChecked()
|
||||
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
|
||||
assert tabGeneral.hideHScroll.isChecked()
|
||||
# Check Spell Checking
|
||||
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
|
||||
assert len(spelling) == 1
|
||||
assert spelling == ["en"]
|
||||
|
||||
# Check font button
|
||||
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
|
||||
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
|
||||
prefs.close()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabGeneral.guiFontSize.setValue(12)
|
||||
# Check Fallback Values
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(CONFIG, "hasEnchant", False)
|
||||
prefs = GuiPreferences(nwGUI)
|
||||
prefs.show()
|
||||
|
||||
# Projects Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabProjects = nwPrefs.tabProjects
|
||||
nwPrefs._tabBox.setCurrentWidget(tabProjects)
|
||||
tabProjects.backupPath = "no/where"
|
||||
# Check Spell Checking
|
||||
spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())]
|
||||
assert len(spelling) == 1
|
||||
assert spelling == [""]
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabProjects.backupOnClose.isChecked()
|
||||
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
|
||||
assert tabProjects.backupOnClose.isChecked()
|
||||
|
||||
# Check Browse button
|
||||
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "")
|
||||
assert not tabProjects._backupFolder()
|
||||
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "some/dir")
|
||||
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabProjects.autoSaveDoc.setValue(20)
|
||||
tabProjects.autoSaveProj.setValue(40)
|
||||
|
||||
# Document Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabDocs = nwPrefs.tabDocs
|
||||
nwPrefs._tabBox.setCurrentWidget(tabDocs)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
qtbot.mouseClick(tabDocs.fontButton, Qt.LeftButton)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabDocs.textSize.setValue(13)
|
||||
tabDocs.textWidth.setValue(700)
|
||||
tabDocs.focusWidth.setValue(900)
|
||||
tabDocs.textMargin.setValue(45)
|
||||
tabDocs.tabWidth.setValue(45)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabDocs.hideFocusFooter.isChecked()
|
||||
qtbot.mouseClick(tabDocs.hideFocusFooter, Qt.LeftButton)
|
||||
assert tabDocs.hideFocusFooter.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabDocs.doJustify.isChecked()
|
||||
qtbot.mouseClick(tabDocs.doJustify, Qt.LeftButton)
|
||||
assert tabDocs.doJustify.isChecked()
|
||||
|
||||
# Editor Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabEditor = nwPrefs.tabEditor
|
||||
nwPrefs._tabBox.setCurrentWidget(tabEditor)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabEditor.showTabsNSpaces.isChecked()
|
||||
qtbot.mouseClick(tabEditor.showTabsNSpaces, Qt.LeftButton)
|
||||
assert tabEditor.showTabsNSpaces.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabEditor.showLineEndings.isChecked()
|
||||
qtbot.mouseClick(tabEditor.showLineEndings, Qt.LeftButton)
|
||||
assert tabEditor.showLineEndings.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabEditor.autoScroll.isChecked()
|
||||
qtbot.mouseClick(tabEditor.autoScroll, Qt.LeftButton)
|
||||
assert tabEditor.autoScroll.isChecked()
|
||||
|
||||
# Syntax Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabSyntax = nwPrefs.tabSyntax
|
||||
nwPrefs._tabBox.setCurrentWidget(tabSyntax)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert tabSyntax.highlightQuotes.isChecked()
|
||||
qtbot.mouseClick(tabSyntax.highlightQuotes, Qt.LeftButton)
|
||||
assert not tabSyntax.highlightQuotes.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert tabSyntax.highlightEmph.isChecked()
|
||||
qtbot.mouseClick(tabSyntax.highlightEmph, Qt.LeftButton)
|
||||
assert not tabSyntax.highlightEmph.isChecked()
|
||||
|
||||
# Automation Settings
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabAuto = nwPrefs.tabAuto
|
||||
nwPrefs._tabBox.setCurrentWidget(tabAuto)
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert tabAuto.autoSelect.isChecked()
|
||||
qtbot.mouseClick(tabAuto.autoSelect, Qt.LeftButton)
|
||||
assert not tabAuto.autoSelect.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert tabAuto.doReplace.isChecked()
|
||||
qtbot.mouseClick(tabAuto.doReplace, Qt.LeftButton)
|
||||
assert not tabAuto.doReplace.isChecked()
|
||||
|
||||
qtbot.wait(KEY_DELAY)
|
||||
assert not tabAuto.doReplaceSQuote.isEnabled()
|
||||
assert not tabAuto.doReplaceDQuote.isEnabled()
|
||||
assert not tabAuto.doReplaceDash.isEnabled()
|
||||
assert not tabAuto.doReplaceDots.isEnabled()
|
||||
|
||||
# Quotation Style
|
||||
qtbot.wait(KEY_DELAY)
|
||||
tabQuote = nwPrefs.tabQuote
|
||||
nwPrefs._tabBox.setCurrentWidget(tabQuote)
|
||||
|
||||
monkeypatch.setattr(GuiQuoteSelect, "selectedQuote", "'")
|
||||
monkeypatch.setattr(GuiQuoteSelect, "exec_", lambda *a: QDialog.Accepted)
|
||||
qtbot.mouseClick(tabQuote.btnDoubleStyleC, Qt.LeftButton)
|
||||
|
||||
# Save and Check Config
|
||||
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
|
||||
|
||||
assert CONFIG.saveConfig()
|
||||
projFile = tstPaths.cnfDir / "novelwriter.conf"
|
||||
testFile = tstPaths.outDir / "guiPreferences_novelwriter.conf"
|
||||
compFile = tstPaths.refDir / "guiPreferences_novelwriter.conf"
|
||||
copyfile(projFile, testFile)
|
||||
ignTuple = (
|
||||
"timestamp", "font", "lastnotes", "localisation", "geometry",
|
||||
"preferences", "projcols", "mainpane", "docpane", "viewpane",
|
||||
"outlinepane", "textfont", "textsize", "lastpath", "backuppath"
|
||||
)
|
||||
assert cmpFiles(testFile, compFile, ignoreStart=ignTuple)
|
||||
|
||||
# Clean up
|
||||
nwGUI.closeMain()
|
||||
prefs.close()
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgPreferences_Main
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI):
|
||||
"""Test the preferences dialog actions."""
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
monkeypatch.setattr(GuiPreferences, "deleteLater", lambda *a: None)
|
||||
prefs = GuiPreferences(nwGUI)
|
||||
prefs.show()
|
||||
|
||||
# Check Navigation
|
||||
vBar = prefs.mainForm.verticalScrollBar()
|
||||
old = -1
|
||||
with qtbot.waitSignal(vBar.valueChanged) as value:
|
||||
prefs.sidebar.button(0).click()
|
||||
assert value.args[0] > old
|
||||
old = value.args[0]
|
||||
with qtbot.waitSignal(vBar.valueChanged) as value:
|
||||
prefs.sidebar.button(1).click()
|
||||
assert value.args[0] > old
|
||||
old = value.args[0]
|
||||
with qtbot.waitSignal(vBar.valueChanged) as value:
|
||||
prefs.sidebar.button(2).click()
|
||||
assert value.args[0] > old
|
||||
old = value.args[0]
|
||||
|
||||
# Check Search
|
||||
prefs.searchText.setText("Display language")
|
||||
with qtbot.waitSignal(vBar.valueChanged) as value:
|
||||
prefs._gotoSearch()
|
||||
assert value.args[0] < old
|
||||
|
||||
# Check Apply Button
|
||||
prefs.show()
|
||||
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
|
||||
prefs.buttonBox.button(QDialogButtonBox.StandardButton.Apply).click()
|
||||
assert signal.args == [False, False, False, False]
|
||||
|
||||
# Check Save Button
|
||||
prefs.show()
|
||||
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
|
||||
with qtbot.waitSignal(prefs.finished) as status:
|
||||
prefs.buttonBox.button(QDialogButtonBox.StandardButton.Save).click()
|
||||
assert signal.args == [False, False, False, False]
|
||||
assert status.args == [nwConst.DLG_FINISHED]
|
||||
|
||||
# Check Close Button
|
||||
prefs.show()
|
||||
with qtbot.waitSignal(prefs.finished) as status:
|
||||
prefs.buttonBox.button(QDialogButtonBox.StandardButton.Close).click()
|
||||
assert status.args == [nwConst.DLG_FINISHED]
|
||||
|
||||
# Close Using Escape Key
|
||||
prefs.show()
|
||||
with qtbot.waitSignal(prefs.finished) as status:
|
||||
event = QKeyEvent(QEvent.Type.KeyPress, Qt.Key.Key_Escape, Qt.KeyboardModifier.NoModifier)
|
||||
prefs.keyPressEvent(event)
|
||||
assert status.args == [nwConst.DLG_FINISHED]
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgPreferences_Actions
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths):
|
||||
"""Test the preferences dialog settings."""
|
||||
spelling = [("en", "English [en]"), ("de", "Deutch [de]")]
|
||||
languages = [("en_GB", "British English"), ("en_US", "US English")]
|
||||
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling)
|
||||
monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages)
|
||||
monkeypatch.setattr(GuiPreferences, "deleteLater", lambda *a: None)
|
||||
|
||||
prefs = GuiPreferences(nwGUI)
|
||||
prefs.show()
|
||||
|
||||
# Mock Font
|
||||
class MockFont:
|
||||
|
||||
def family(self):
|
||||
return "TestFont"
|
||||
|
||||
def pointSize(self):
|
||||
return 42
|
||||
|
||||
# Appearance
|
||||
prefs.guiLocale.setCurrentIndex(prefs.guiLocale.findData("en_US"))
|
||||
prefs.guiTheme.setCurrentIndex(prefs.guiTheme.findData("default_dark"))
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QFontDialog, "getFont", lambda *a: (MockFont(), True))
|
||||
prefs.guiFontButton.click()
|
||||
prefs.guiFontSize.stepDown() # Should change it to 41
|
||||
prefs.hideVScroll.setChecked(True)
|
||||
prefs.hideHScroll.setChecked(True)
|
||||
|
||||
assert CONFIG.guiLocale != "en_US"
|
||||
assert CONFIG.guiTheme != "default_dark"
|
||||
assert CONFIG.guiFont != "TestFont"
|
||||
assert CONFIG.guiFontSize < 42
|
||||
assert CONFIG.hideVScroll is False
|
||||
assert CONFIG.hideHScroll is False
|
||||
|
||||
# Document Style
|
||||
prefs.guiSyntax.setCurrentIndex(prefs.guiSyntax.findData("default_dark"))
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QFontDialog, "getFont", lambda *a: (MockFont(), True))
|
||||
prefs.textFontButton.click()
|
||||
prefs.textSize.stepDown() # Should change it to 41
|
||||
prefs.emphLabels.setChecked(False)
|
||||
prefs.showFullPath.setChecked(False)
|
||||
prefs.incNotesWCount.setChecked(False)
|
||||
|
||||
assert CONFIG.guiSyntax != "default_dark"
|
||||
assert CONFIG.textFont != "testFont"
|
||||
assert CONFIG.textSize < 42
|
||||
assert CONFIG.emphLabels is True
|
||||
assert CONFIG.showFullPath is True
|
||||
assert CONFIG.incNotesWCount is True
|
||||
|
||||
# Auto Save
|
||||
prefs.autoSaveDoc.stepUp()
|
||||
prefs.autoSaveProj.stepUp()
|
||||
|
||||
assert CONFIG.autoSaveDoc == 30
|
||||
assert CONFIG.autoSaveProj == 60
|
||||
|
||||
# Project Backup
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: str(tstPaths.testDir))
|
||||
prefs.backupGetPath.click()
|
||||
assert prefs.backupPath == str(tstPaths.testDir)
|
||||
prefs.backupOnClose.setChecked(True)
|
||||
assert prefs.askBeforeBackup.isEnabled() is True
|
||||
prefs.askBeforeBackup.setChecked(False)
|
||||
|
||||
assert CONFIG._backupPath != tstPaths.testDir
|
||||
assert CONFIG.backupOnClose is False
|
||||
assert CONFIG.askBeforeBackup is True
|
||||
|
||||
# Session Timer
|
||||
prefs.stopWhenIdle.setChecked(False)
|
||||
prefs.userIdleTime.stepUp()
|
||||
|
||||
assert CONFIG.stopWhenIdle is True
|
||||
assert CONFIG.userIdleTime == 300
|
||||
|
||||
# Text Flow
|
||||
prefs.textWidth.stepDown()
|
||||
prefs.focusWidth.stepDown()
|
||||
prefs.hideFocusFooter.setChecked(True)
|
||||
prefs.doJustify.setChecked(True)
|
||||
prefs.textMargin.stepUp()
|
||||
prefs.tabWidth.stepUp()
|
||||
|
||||
assert CONFIG.textWidth == 700
|
||||
assert CONFIG.focusWidth == 800
|
||||
assert CONFIG.hideFocusFooter is False
|
||||
assert CONFIG.doJustify is False
|
||||
assert CONFIG.textMargin == 40
|
||||
assert CONFIG.tabWidth == 40
|
||||
|
||||
# Text Editing
|
||||
prefs.spellLanguage.setCurrentIndex(prefs.spellLanguage.findData("de"))
|
||||
prefs.autoSelect.setChecked(False)
|
||||
prefs.showTabsNSpaces.setChecked(True)
|
||||
prefs.showLineEndings.setChecked(True)
|
||||
|
||||
assert CONFIG.spellLanguage != "de"
|
||||
assert CONFIG.autoSelect is True
|
||||
assert CONFIG.showTabsNSpaces is False
|
||||
assert CONFIG.showLineEndings is False
|
||||
|
||||
# Editor Scrolling
|
||||
prefs.scrollPastEnd.setChecked(False)
|
||||
prefs.autoScroll.setChecked(True)
|
||||
prefs.autoScrollPos.stepUp()
|
||||
|
||||
assert CONFIG.scrollPastEnd is True
|
||||
assert CONFIG.autoScroll is False
|
||||
assert CONFIG.autoScrollPos == 30
|
||||
|
||||
# Text Highlighting
|
||||
prefs.allowOpenSQuote.setChecked(True)
|
||||
prefs.allowOpenDQuote.setChecked(False)
|
||||
prefs.highlightQuotes.setChecked(False)
|
||||
prefs.highlightEmph.setChecked(False)
|
||||
prefs.showMultiSpaces.setChecked(False)
|
||||
|
||||
assert prefs.allowOpenSQuote.isEnabled() is False
|
||||
assert prefs.allowOpenDQuote.isEnabled() is False
|
||||
|
||||
assert CONFIG.highlightQuotes is True
|
||||
assert CONFIG.allowOpenSQuote is False
|
||||
assert CONFIG.allowOpenDQuote is True
|
||||
assert CONFIG.highlightEmph is True
|
||||
assert CONFIG.showMultiSpaces is True
|
||||
|
||||
# Text Automation
|
||||
prefs.doReplaceSQuote.setChecked(False)
|
||||
prefs.doReplaceDQuote.setChecked(False)
|
||||
prefs.doReplaceDash.setChecked(False)
|
||||
prefs.doReplaceDots.setChecked(False)
|
||||
prefs.doReplace.setChecked(False)
|
||||
prefs.fmtPadBefore.setText("!?:")
|
||||
prefs.fmtPadAfter.setText("¡¿")
|
||||
prefs.fmtPadThin.setChecked(True)
|
||||
|
||||
assert prefs.doReplaceSQuote.isEnabled() is False
|
||||
assert prefs.doReplaceDQuote.isEnabled() is False
|
||||
assert prefs.doReplaceDash.isEnabled() is False
|
||||
assert prefs.doReplaceDots.isEnabled() is False
|
||||
assert prefs.fmtPadThin.isEnabled() is False
|
||||
|
||||
assert CONFIG.doReplace is True
|
||||
assert CONFIG.doReplaceSQuote is True
|
||||
assert CONFIG.doReplaceDQuote is True
|
||||
assert CONFIG.doReplaceDash is True
|
||||
assert CONFIG.doReplaceDots is True
|
||||
assert CONFIG.fmtPadBefore == ""
|
||||
assert CONFIG.fmtPadAfter == ""
|
||||
assert CONFIG.fmtPadThin is False
|
||||
|
||||
# Quotation Style
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_LSAQUO, True))
|
||||
prefs.btnSingleStyleO.click()
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_RSAQUO, True))
|
||||
prefs.btnSingleStyleC.click()
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_LAQUO, True))
|
||||
prefs.btnDoubleStyleO.click()
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiQuoteSelect, "getQuote", lambda *a, **k: (nwUnicode.U_RAQUO, True))
|
||||
prefs.btnDoubleStyleC.click()
|
||||
|
||||
assert CONFIG.fmtSQuoteOpen == nwUnicode.U_LSQUO
|
||||
assert CONFIG.fmtSQuoteClose == nwUnicode.U_RSQUO
|
||||
assert CONFIG.fmtDQuoteOpen == nwUnicode.U_LDQUO
|
||||
assert CONFIG.fmtDQuoteClose == nwUnicode.U_RDQUO
|
||||
|
||||
# Save Settings
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QFontDatabase, "families", lambda *a: ["TestFont"])
|
||||
with qtbot.waitSignal(prefs.newPreferencesReady) as signal:
|
||||
prefs.buttonBox.button(QDialogButtonBox.StandardButton.Apply).click()
|
||||
assert signal.args == [True, True, True, True]
|
||||
|
||||
# Check Settings
|
||||
# ==============
|
||||
|
||||
# Appearance
|
||||
assert CONFIG.guiLocale == "en_US"
|
||||
assert CONFIG.guiTheme == "default_dark"
|
||||
assert CONFIG.guiFont == "TestFont"
|
||||
assert CONFIG.guiFontSize == 41
|
||||
assert CONFIG.hideVScroll is True
|
||||
assert CONFIG.hideHScroll is True
|
||||
|
||||
# Document Style
|
||||
assert CONFIG.guiSyntax == "default_dark"
|
||||
assert CONFIG.textFont == "TestFont"
|
||||
assert CONFIG.textSize == 41
|
||||
assert CONFIG.emphLabels is False
|
||||
assert CONFIG.showFullPath is False
|
||||
assert CONFIG.incNotesWCount is False
|
||||
|
||||
# Auto Save
|
||||
assert CONFIG.autoSaveDoc == 31
|
||||
assert CONFIG.autoSaveProj == 61
|
||||
|
||||
# Project Backup
|
||||
assert CONFIG._backupPath == tstPaths.testDir
|
||||
assert CONFIG.backupOnClose is True
|
||||
assert CONFIG.askBeforeBackup is False
|
||||
|
||||
# Session Timer
|
||||
assert CONFIG.stopWhenIdle is False
|
||||
assert CONFIG.userIdleTime == 330
|
||||
|
||||
# Text Flow
|
||||
assert CONFIG.textWidth == 690
|
||||
assert CONFIG.focusWidth == 790
|
||||
assert CONFIG.hideFocusFooter is True
|
||||
assert CONFIG.doJustify is True
|
||||
assert CONFIG.textMargin == 41
|
||||
assert CONFIG.tabWidth == 41
|
||||
|
||||
# Text Editing
|
||||
assert CONFIG.spellLanguage == "de"
|
||||
assert CONFIG.autoSelect is False
|
||||
assert CONFIG.showTabsNSpaces is True
|
||||
assert CONFIG.showLineEndings is True
|
||||
|
||||
# Editor Scrolling
|
||||
assert CONFIG.scrollPastEnd is False
|
||||
assert CONFIG.autoScroll is True
|
||||
assert CONFIG.autoScrollPos == 31
|
||||
|
||||
# Text Highlighting
|
||||
assert CONFIG.highlightQuotes is False
|
||||
assert CONFIG.allowOpenSQuote is True
|
||||
assert CONFIG.allowOpenDQuote is False
|
||||
assert CONFIG.highlightEmph is False
|
||||
assert CONFIG.showMultiSpaces is False
|
||||
|
||||
# Text Automation
|
||||
assert CONFIG.doReplace is False
|
||||
assert CONFIG.doReplaceSQuote is False
|
||||
assert CONFIG.doReplaceDQuote is False
|
||||
assert CONFIG.doReplaceDash is False
|
||||
assert CONFIG.doReplaceDots is False
|
||||
assert CONFIG.fmtPadBefore == "!?:"
|
||||
assert CONFIG.fmtPadAfter == "¡¿"
|
||||
assert CONFIG.fmtPadThin is True
|
||||
|
||||
# Quotation Style
|
||||
assert CONFIG.fmtSQuoteOpen == nwUnicode.U_LSAQUO
|
||||
assert CONFIG.fmtSQuoteClose == nwUnicode.U_RSAQUO
|
||||
assert CONFIG.fmtDQuoteOpen == nwUnicode.U_LAQUO
|
||||
assert CONFIG.fmtDQuoteClose == nwUnicode.U_RAQUO
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgPreferences_Settings
|
||||
|
||||
Reference in New Issue
Block a user