Update Project Settings dialog (#1668)
This commit is contained in:
@@ -131,7 +131,7 @@ def main(sysArgs: list | None = None):
|
||||
elif inOpt == "--debug":
|
||||
CONFIG.isDebug = True
|
||||
logLevel = logging.DEBUG
|
||||
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
|
||||
logFormat = "[{asctime:}] {filename:>18}:{lineno:<4d} {levelname:8} {message:}"
|
||||
elif inOpt == "--style":
|
||||
qtStyle = inArg
|
||||
elif inOpt == "--config":
|
||||
|
||||
@@ -59,9 +59,9 @@ class GuiPreferences(QDialog):
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Preferences"), SHARED.theme.helpText, parent=self, scale=1.25
|
||||
self.tr("Preferences"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
self.titleLabel.setIndent(CONFIG.pxInt(4))
|
||||
|
||||
# Search Box
|
||||
self.searchText = QLineEdit(self)
|
||||
|
||||
@@ -3,7 +3,8 @@ novelWriter – GUI Project Settings
|
||||
==================================
|
||||
|
||||
File History:
|
||||
Created: 2018-09-29 [0.0.1] GuiProjectSettings
|
||||
Created: 2018-09-29 [0.0.1] GuiProjectSettings
|
||||
Rewritten: 2024-01-26 [2.3b1] GuiProjectSettings
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2024, Veronica Berglyd Olsen
|
||||
@@ -25,75 +26,101 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtGui import QCloseEvent, QIcon, QPixmap, QColor
|
||||
from PyQt5.QtGui import QCloseEvent, QColor, QIcon, QPixmap
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QColorDialog, QComboBox, QDialogButtonBox, QHBoxLayout, QLabel, QLineEdit,
|
||||
QPushButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, qApp
|
||||
QColorDialog, QComboBox, QDialog, QDialogButtonBox, QHBoxLayout, QLineEdit,
|
||||
QPushButton, QStackedWidget, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||
QWidget, qApp
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import simplified
|
||||
from novelwriter.extensions.switch import NSwitch
|
||||
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||
from novelwriter.extensions.configlayout import NConfigLayout
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.guimain import GuiMain
|
||||
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollableForm
|
||||
from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GuiProjectSettings(NPagedDialog):
|
||||
class GuiProjectSettings(QDialog):
|
||||
|
||||
TAB_MAIN = 0
|
||||
TAB_STATUS = 1
|
||||
TAB_IMPORT = 2
|
||||
TAB_REPLACE = 3
|
||||
PAGE_SETTINGS = 0
|
||||
PAGE_STATUS = 1
|
||||
PAGE_IMPORT = 2
|
||||
PAGE_REPLACE = 3
|
||||
|
||||
newProjectSettingsReady = pyqtSignal()
|
||||
newProjectSettingsReady = pyqtSignal(bool)
|
||||
|
||||
def __init__(self, mainGui: GuiMain, focusTab: int = TAB_MAIN) -> None:
|
||||
super().__init__(parent=mainGui)
|
||||
def __init__(self, parent: QWidget, gotoPage: int = PAGE_SETTINGS) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
logger.debug("Create: GuiProjectSettings")
|
||||
self.setObjectName("GuiProjectSettings")
|
||||
|
||||
self.mainGui = mainGui
|
||||
SHARED.project.countStatus()
|
||||
self.setWindowTitle(self.tr("Project Settings"))
|
||||
|
||||
wW = CONFIG.pxInt(570)
|
||||
wH = CONFIG.pxInt(375)
|
||||
pOptions = SHARED.project.options
|
||||
|
||||
self.setMinimumWidth(wW)
|
||||
self.setMinimumHeight(wH)
|
||||
options = SHARED.project.options
|
||||
self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400))
|
||||
self.resize(
|
||||
CONFIG.pxInt(pOptions.getInt("GuiProjectSettings", "winWidth", wW)),
|
||||
CONFIG.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
|
||||
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winWidth", CONFIG.pxInt(650))),
|
||||
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winHeight", CONFIG.pxInt(500)))
|
||||
)
|
||||
|
||||
self.tabMain = GuiProjectEditMain(self)
|
||||
self.tabStatus = GuiProjectEditStatus(self, True)
|
||||
self.tabImport = GuiProjectEditStatus(self, False)
|
||||
self.tabReplace = GuiProjectEditReplace(self)
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Project Settings"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
|
||||
self.addTab(self.tabMain, self.tr("Settings"))
|
||||
self.addTab(self.tabStatus, self.tr("Status"))
|
||||
self.addTab(self.tabImport, self.tr("Importance"))
|
||||
self.addTab(self.tabReplace, self.tr("Auto-Replace"))
|
||||
# SideBar
|
||||
self.sidebar = NPagedSideBar(self)
|
||||
self.sidebar.setLabelColor(SHARED.theme.helpText)
|
||||
self.sidebar.addButton(self.tr("Settings"), self.PAGE_SETTINGS)
|
||||
self.sidebar.addButton(self.tr("Status"), self.PAGE_STATUS)
|
||||
self.sidebar.addButton(self.tr("Importance"), self.PAGE_IMPORT)
|
||||
self.sidebar.addButton(self.tr("Auto-Replace"), self.PAGE_REPLACE)
|
||||
self.sidebar.setSelected(gotoPage)
|
||||
self.sidebar.buttonClicked.connect(self._sidebarClicked)
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox(
|
||||
QDialogButtonBox.StandardButton.Save | QDialogButtonBox.StandardButton.Cancel
|
||||
)
|
||||
self.buttonBox.accepted.connect(self._doSave)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.rejected.connect(self.close)
|
||||
self.addControls(self.buttonBox)
|
||||
|
||||
# Focus Tab
|
||||
self._focusTab(focusTab)
|
||||
# Content
|
||||
SHARED.project.countStatus()
|
||||
|
||||
self.settingsPage = _SettingsPage(self)
|
||||
self.statusPage = _StatusPage(self, True)
|
||||
self.importPage = _StatusPage(self, False)
|
||||
self.replacePage = _ReplacePage(self)
|
||||
|
||||
self.mainStack = QStackedWidget(self)
|
||||
self.mainStack.addWidget(self.settingsPage)
|
||||
self.mainStack.addWidget(self.statusPage)
|
||||
self.mainStack.addWidget(self.importPage)
|
||||
self.mainStack.addWidget(self.replacePage)
|
||||
|
||||
# Assemble
|
||||
self.topBox = QHBoxLayout()
|
||||
self.topBox.addWidget(self.titleLabel)
|
||||
self.topBox.addStretch(1)
|
||||
|
||||
self.mainBox = QHBoxLayout()
|
||||
self.mainBox.addWidget(self.sidebar)
|
||||
self.mainBox.addWidget(self.mainStack)
|
||||
self.mainBox.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addLayout(self.topBox)
|
||||
self.outerBox.addLayout(self.mainBox)
|
||||
self.outerBox.addWidget(self.buttonBox)
|
||||
self.outerBox.setSpacing(CONFIG.pxInt(8))
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.setSizeGripEnabled(True)
|
||||
|
||||
logger.debug("Ready: GuiProjectSettings")
|
||||
|
||||
@@ -103,9 +130,13 @@ class GuiProjectSettings(NPagedDialog):
|
||||
logger.debug("Delete: GuiProjectSettings")
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
|
||||
def closeEvent(self, event: QCloseEvent) -> None:
|
||||
"""Capture the close event and perform cleanup."""
|
||||
self._saveGuiSettings()
|
||||
"""Capture the user closing the window and save settings."""
|
||||
self._saveSettings()
|
||||
event.accept()
|
||||
self.deleteLater()
|
||||
return
|
||||
@@ -114,40 +145,52 @@ class GuiProjectSettings(NPagedDialog):
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot(int)
|
||||
def _sidebarClicked(self, pageId: int) -> None:
|
||||
"""Process a user request to switch page."""
|
||||
if pageId == self.PAGE_SETTINGS:
|
||||
self.mainStack.setCurrentWidget(self.settingsPage)
|
||||
elif pageId == self.PAGE_STATUS:
|
||||
self.mainStack.setCurrentWidget(self.statusPage)
|
||||
elif pageId == self.PAGE_IMPORT:
|
||||
self.mainStack.setCurrentWidget(self.importPage)
|
||||
elif pageId == self.PAGE_REPLACE:
|
||||
self.mainStack.setCurrentWidget(self.replacePage)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Save settings and close dialog."""
|
||||
project = SHARED.project
|
||||
projName = self.tabMain.editName.text()
|
||||
bookTitle = self.tabMain.editTitle.text()
|
||||
bookAuthor = self.tabMain.editAuthor.text()
|
||||
projLang = self.tabMain.projLang.currentData()
|
||||
spellLang = self.tabMain.spellLang.currentData()
|
||||
doBackup = not self.tabMain.doBackup.isChecked()
|
||||
projName = self.settingsPage.projName.text()
|
||||
projAuthor = self.settingsPage.projAuthor.text()
|
||||
projLang = self.settingsPage.projLang.currentData()
|
||||
spellLang = self.settingsPage.spellLang.currentData()
|
||||
doBackup = not self.settingsPage.doBackup.isChecked()
|
||||
|
||||
project.data.setName(projName)
|
||||
project.data.setTitle(bookTitle)
|
||||
project.data.setAuthor(bookAuthor)
|
||||
project.data.setAuthor(projAuthor)
|
||||
project.data.setDoBackup(doBackup)
|
||||
project.data.setSpellLang(spellLang)
|
||||
project.setProjectLang(projLang)
|
||||
|
||||
if self.tabStatus.colChanged:
|
||||
newList, delList = self.tabStatus.getNewList()
|
||||
rebuildTrees = False
|
||||
|
||||
if self.statusPage.wasChanged:
|
||||
newList, delList = self.statusPage.getNewList()
|
||||
project.setStatusColours(newList, delList)
|
||||
rebuildTrees = True
|
||||
|
||||
if self.tabImport.colChanged:
|
||||
newList, delList = self.tabImport.getNewList()
|
||||
if self.importPage.wasChanged:
|
||||
newList, delList = self.importPage.getNewList()
|
||||
project.setImportColours(newList, delList)
|
||||
rebuildTrees = True
|
||||
|
||||
if self.tabStatus.colChanged or self.tabImport.colChanged:
|
||||
self.mainGui.rebuildTrees()
|
||||
|
||||
if self.tabReplace.arChanged:
|
||||
newList = self.tabReplace.getNewList()
|
||||
if self.replacePage.wasChanged:
|
||||
newList = self.replacePage.getNewList()
|
||||
project.data.setAutoReplace(newList)
|
||||
|
||||
self.newProjectSettingsReady.emit()
|
||||
self.newProjectSettingsReady.emit(rebuildTrees)
|
||||
qApp.processEvents()
|
||||
self.close()
|
||||
|
||||
@@ -157,134 +200,103 @@ class GuiProjectSettings(NPagedDialog):
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _focusTab(self, tab: int) -> None:
|
||||
"""Change which is the focused tab."""
|
||||
if tab == self.TAB_MAIN:
|
||||
self.setCurrentWidget(self.tabMain)
|
||||
elif tab == self.TAB_STATUS:
|
||||
self.setCurrentWidget(self.tabStatus)
|
||||
elif tab == self.TAB_IMPORT:
|
||||
self.setCurrentWidget(self.tabImport)
|
||||
elif tab == self.TAB_REPLACE:
|
||||
self.setCurrentWidget(self.tabReplace)
|
||||
return
|
||||
|
||||
def _saveGuiSettings(self) -> None:
|
||||
def _saveSettings(self) -> None:
|
||||
"""Save GUI settings."""
|
||||
winWidth = CONFIG.rpxInt(self.width())
|
||||
winHeight = CONFIG.rpxInt(self.height())
|
||||
replaceColW = CONFIG.rpxInt(self.tabReplace.listBox.columnWidth(0))
|
||||
statusColW = CONFIG.rpxInt(self.tabStatus.listBox.columnWidth(0))
|
||||
importColW = CONFIG.rpxInt(self.tabImport.listBox.columnWidth(0))
|
||||
statusColW = CONFIG.rpxInt(self.statusPage.columnWidth())
|
||||
importColW = CONFIG.rpxInt(self.importPage.columnWidth())
|
||||
replaceColW = CONFIG.rpxInt(self.replacePage.columnWidth())
|
||||
|
||||
logger.debug("Saving State: GuiProjectSettings")
|
||||
pOptions = SHARED.project.options
|
||||
pOptions.setValue("GuiProjectSettings", "winWidth", winWidth)
|
||||
pOptions.setValue("GuiProjectSettings", "winHeight", winHeight)
|
||||
pOptions.setValue("GuiProjectSettings", "replaceColW", replaceColW)
|
||||
pOptions.setValue("GuiProjectSettings", "statusColW", statusColW)
|
||||
pOptions.setValue("GuiProjectSettings", "importColW", importColW)
|
||||
options = SHARED.project.options
|
||||
options.setValue("GuiProjectSettings", "winWidth", winWidth)
|
||||
options.setValue("GuiProjectSettings", "winHeight", winHeight)
|
||||
options.setValue("GuiProjectSettings", "statusColW", statusColW)
|
||||
options.setValue("GuiProjectSettings", "importColW", importColW)
|
||||
options.setValue("GuiProjectSettings", "replaceColW", replaceColW)
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiProjectSettings
|
||||
|
||||
|
||||
class GuiProjectEditMain(QWidget):
|
||||
class _SettingsPage(NScrollableForm):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
# The Form
|
||||
self.mainForm = NConfigLayout()
|
||||
self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
|
||||
self.setLayout(self.mainForm)
|
||||
xW = CONFIG.pxInt(200)
|
||||
data = SHARED.project.data
|
||||
self.setHelpTextStyle(SHARED.theme.helpText)
|
||||
self.setRowIndent(0)
|
||||
|
||||
self.mainForm.addGroupLabel(self.tr("Project Settings"))
|
||||
|
||||
xW = CONFIG.pxInt(250)
|
||||
pData = SHARED.project.data
|
||||
|
||||
self.editName = QLineEdit()
|
||||
self.editName.setMaxLength(200)
|
||||
self.editName.setMaximumWidth(xW)
|
||||
self.editName.setText(pData.name)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Project name"),
|
||||
self.editName,
|
||||
self.tr("Should be set only once.")
|
||||
# Project Name
|
||||
self.projName = QLineEdit(self)
|
||||
self.projName.setMaxLength(200)
|
||||
self.projName.setMinimumWidth(xW)
|
||||
self.projName.setText(data.name)
|
||||
self.addRow(
|
||||
self.tr("Project name"), self.projName,
|
||||
self.tr("Changing this will affect the backup path."),
|
||||
stretch=(3, 2)
|
||||
)
|
||||
|
||||
self.editTitle = QLineEdit()
|
||||
self.editTitle.setMaxLength(200)
|
||||
self.editTitle.setMaximumWidth(xW)
|
||||
self.editTitle.setText(pData.title)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Novel title"),
|
||||
self.editTitle,
|
||||
self.tr("Change whenever you want!")
|
||||
)
|
||||
|
||||
self.editAuthor = QLineEdit()
|
||||
self.editAuthor.setMaxLength(200)
|
||||
self.editAuthor.setMaximumWidth(xW)
|
||||
self.editAuthor.setText(pData.author)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Author(s)"),
|
||||
self.editAuthor,
|
||||
self.tr("Change whenever you want!")
|
||||
# Project Author
|
||||
self.projAuthor = QLineEdit(self)
|
||||
self.projAuthor.setMaxLength(200)
|
||||
self.projAuthor.setMinimumWidth(xW)
|
||||
self.projAuthor.setText(data.author)
|
||||
self.addRow(
|
||||
self.tr("Author(s)"), self.projAuthor,
|
||||
self.tr("Only used when building the manuscript."),
|
||||
stretch=(3, 2)
|
||||
)
|
||||
|
||||
# Project Language
|
||||
self.projLang = QComboBox(self)
|
||||
self.projLang.setMaximumWidth(xW)
|
||||
self.projLang.setMinimumWidth(xW)
|
||||
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
||||
self.projLang.addItem(language, tag)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Project language"),
|
||||
self.projLang,
|
||||
self.tr("Used when building the manuscript.")
|
||||
self.addRow(
|
||||
self.tr("Project language"), self.projLang,
|
||||
self.tr("Only used when building the manuscript."),
|
||||
stretch=(3, 2)
|
||||
)
|
||||
if (idx := self.projLang.findData(data.language)) != -1:
|
||||
self.projLang.setCurrentIndex(idx)
|
||||
|
||||
langIdx = 0
|
||||
if pData.language is not None:
|
||||
langIdx = self.projLang.findData(pData.language)
|
||||
if langIdx == -1:
|
||||
langIdx = self.projLang.findData("en_GB")
|
||||
if langIdx != -1:
|
||||
self.projLang.setCurrentIndex(langIdx)
|
||||
|
||||
# Spell Check Language
|
||||
self.spellLang = QComboBox(self)
|
||||
self.spellLang.setMaximumWidth(xW)
|
||||
self.spellLang.setMinimumWidth(xW)
|
||||
self.spellLang.addItem(self.tr("Default"), "None")
|
||||
if CONFIG.hasEnchant:
|
||||
for tag, language in SHARED.spelling.listDictionaries():
|
||||
self.spellLang.addItem(language, tag)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Spell check language"),
|
||||
self.spellLang,
|
||||
self.tr("Overrides main preferences.")
|
||||
self.addRow(
|
||||
self.tr("Spell check language"), self.spellLang,
|
||||
self.tr("Overrides main preferences."),
|
||||
stretch=(3, 2)
|
||||
)
|
||||
if (idx := self.spellLang.findData(data.spellLang)) != -1:
|
||||
self.spellLang.setCurrentIndex(idx)
|
||||
|
||||
langIdx = 0
|
||||
if pData.spellLang is not None:
|
||||
langIdx = self.spellLang.findData(pData.spellLang)
|
||||
if langIdx != -1:
|
||||
self.spellLang.setCurrentIndex(langIdx)
|
||||
|
||||
# Backup on Close
|
||||
self.doBackup = NSwitch(self)
|
||||
self.doBackup.setChecked(not pData.doBackup)
|
||||
self.mainForm.addRow(
|
||||
self.tr("No backup on close"),
|
||||
self.doBackup,
|
||||
self.doBackup.setChecked(not data.doBackup)
|
||||
self.addRow(
|
||||
self.tr("Disable backup on close"), self.doBackup,
|
||||
self.tr("Overrides main preferences.")
|
||||
)
|
||||
|
||||
self.finalise()
|
||||
|
||||
return
|
||||
|
||||
# END Class GuiProjectEditMain
|
||||
# END Class _SettingsPage
|
||||
|
||||
|
||||
class GuiProjectEditStatus(QWidget):
|
||||
class _StatusPage(NFixedPage):
|
||||
|
||||
COL_LABEL = 0
|
||||
COL_USAGE = 1
|
||||
@@ -297,56 +309,54 @@ class GuiProjectEditStatus(QWidget):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
if isStatus:
|
||||
self.theStatus = SHARED.project.data.itemStatus
|
||||
pageLabel = self.tr("Novel File Status Levels")
|
||||
status = SHARED.project.data.itemStatus
|
||||
pageLabel = self.tr("Novel Document Status Levels")
|
||||
colSetting = "statusColW"
|
||||
else:
|
||||
self.theStatus = SHARED.project.data.itemImport
|
||||
pageLabel = self.tr("Note File Importance Levels")
|
||||
status = SHARED.project.data.itemImport
|
||||
pageLabel = self.tr("Project Note Importance Levels")
|
||||
colSetting = "importColW"
|
||||
|
||||
wCol0 = CONFIG.pxInt(
|
||||
SHARED.project.options.getInt("GuiProjectSettings", colSetting, 130)
|
||||
)
|
||||
|
||||
self.colDeleted = []
|
||||
self.colChanged = False
|
||||
self.selColour = QColor(100, 100, 100)
|
||||
self._changed = False
|
||||
self._colDeleted = []
|
||||
self._selColour = QColor(100, 100, 100)
|
||||
|
||||
self.iPx = SHARED.theme.baseIconSize
|
||||
|
||||
# The List
|
||||
# ========
|
||||
# Title
|
||||
self.pageTitle = NColourLabel(
|
||||
pageLabel, SHARED.theme.helpText, parent=self,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
self.listBox = QTreeWidget()
|
||||
self.listBox.setHeaderLabels([
|
||||
self.tr("Label"), self.tr("Usage"),
|
||||
])
|
||||
# List Box
|
||||
self.listBox = QTreeWidget(self)
|
||||
self.listBox.setHeaderLabels([self.tr("Label"), self.tr("Usage")])
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
self.listBox.setColumnWidth(self.COL_LABEL, wCol0)
|
||||
self.listBox.setIndentation(0)
|
||||
|
||||
for key, entry in self.theStatus.items():
|
||||
for key, entry in status.items():
|
||||
self._addItem(key, entry["name"], entry["cols"], entry["count"])
|
||||
|
||||
# List Controls
|
||||
# =============
|
||||
|
||||
self.addButton = QPushButton(SHARED.theme.getIcon("add"), "")
|
||||
self.addButton = QPushButton(SHARED.theme.getIcon("add"), "", self)
|
||||
self.addButton.clicked.connect(self._newItem)
|
||||
|
||||
self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "")
|
||||
self.delButton = QPushButton(SHARED.theme.getIcon("remove"), "", self)
|
||||
self.delButton.clicked.connect(self._delItem)
|
||||
|
||||
self.upButton = QPushButton(SHARED.theme.getIcon("up"), "")
|
||||
self.upButton = QPushButton(SHARED.theme.getIcon("up"), "", self)
|
||||
self.upButton.clicked.connect(lambda: self._moveItem(-1))
|
||||
|
||||
self.dnButton = QPushButton(SHARED.theme.getIcon("down"), "")
|
||||
self.dnButton = QPushButton(SHARED.theme.getIcon("down"), "", self)
|
||||
self.dnButton.clicked.connect(lambda: self._moveItem(1))
|
||||
|
||||
# Edit Form
|
||||
# =========
|
||||
|
||||
self.editName = QLineEdit()
|
||||
self.editName.setMaxLength(40)
|
||||
self.editName.setPlaceholderText(self.tr("Select item to edit"))
|
||||
@@ -364,8 +374,6 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.saveButton.clicked.connect(self._saveItem)
|
||||
|
||||
# Assemble
|
||||
# ========
|
||||
|
||||
self.listControls = QVBoxLayout()
|
||||
self.listControls.addWidget(self.addButton)
|
||||
self.listControls.addWidget(self.delButton)
|
||||
@@ -387,16 +395,25 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.innerBox.addLayout(self.listControls)
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addWidget(QLabel("<b>%s</b>" % pageLabel))
|
||||
self.outerBox.addWidget(self.pageTitle)
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.setCentralLayout(self.outerBox)
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
def wasChanged(self) -> bool:
|
||||
"""The user changed these settings."""
|
||||
return self._changed
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getNewList(self) -> tuple[list, list]:
|
||||
"""Return list of entries."""
|
||||
if self.colChanged:
|
||||
if self._changed:
|
||||
newList = []
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
item = self.listBox.topLevelItem(n)
|
||||
@@ -406,10 +423,13 @@ class GuiProjectEditStatus(QWidget):
|
||||
"name": item.text(self.COL_LABEL),
|
||||
"cols": item.data(self.COL_LABEL, self.COL_ROLE),
|
||||
})
|
||||
return newList, self.colDeleted
|
||||
|
||||
return newList, self._colDeleted
|
||||
return [], []
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
"""Return the size of the header column."""
|
||||
return self.listBox.columnWidth(0)
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
@@ -417,12 +437,12 @@ class GuiProjectEditStatus(QWidget):
|
||||
@pyqtSlot()
|
||||
def _selectColour(self) -> None:
|
||||
"""Open a dialog to select the status icon colour."""
|
||||
if self.selColour is not None:
|
||||
if self._selColour is not None:
|
||||
newCol = QColorDialog.getColor(
|
||||
self.selColour, self, self.tr("Select Colour")
|
||||
self._selColour, self, self.tr("Select Colour")
|
||||
)
|
||||
if newCol.isValid():
|
||||
self.selColour = newCol
|
||||
self._selColour = newCol
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(newCol)
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
@@ -433,7 +453,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
def _newItem(self) -> None:
|
||||
"""Create a new status item."""
|
||||
self._addItem(None, self.tr("New Item"), (100, 100, 100), 0)
|
||||
self.colChanged = True
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -446,8 +466,8 @@ class GuiProjectEditStatus(QWidget):
|
||||
SHARED.error(self.tr("Cannot delete a status item that is in use."))
|
||||
else:
|
||||
self.listBox.takeTopLevelItem(iRow)
|
||||
self.colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
|
||||
self.colChanged = True
|
||||
self._colDeleted.append(selItem.data(self.COL_LABEL, self.KEY_ROLE))
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -458,9 +478,9 @@ class GuiProjectEditStatus(QWidget):
|
||||
selItem.setText(self.COL_LABEL, simplified(self.editName.text()))
|
||||
selItem.setIcon(self.COL_LABEL, self.colButton.icon())
|
||||
selItem.setData(self.COL_LABEL, self.COL_ROLE, (
|
||||
self.selColour.red(), self.selColour.green(), self.selColour.blue()
|
||||
self._selColour.red(), self._selColour.green(), self._selColour.blue()
|
||||
))
|
||||
self.colChanged = True
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
@@ -474,7 +494,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
name = selItem.text(self.COL_LABEL)
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*cols))
|
||||
self.selColour = QColor(*cols)
|
||||
self._selColour = QColor(*cols)
|
||||
self.editName.setText(name)
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
self.editName.selectAll()
|
||||
@@ -485,7 +505,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
else:
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(100, 100, 100))
|
||||
self.selColour = QColor(100, 100, 100)
|
||||
self._selColour = QColor(100, 100, 100)
|
||||
self.editName.setText("")
|
||||
self.colButton.setIcon(QIcon(pixmap))
|
||||
self.editName.setEnabled(False)
|
||||
@@ -498,16 +518,16 @@ class GuiProjectEditStatus(QWidget):
|
||||
##
|
||||
|
||||
def _addItem(self, key: str | None, name: str,
|
||||
cols: tuple[int, int, int], count: int) -> None:
|
||||
colour: tuple[int, int, int], count: int) -> None:
|
||||
"""Add a status item to the list."""
|
||||
pixmap = QPixmap(self.iPx, self.iPx)
|
||||
pixmap.fill(QColor(*cols))
|
||||
pixmap.fill(QColor(*colour))
|
||||
|
||||
item = QTreeWidgetItem()
|
||||
item.setText(self.COL_LABEL, name)
|
||||
item.setIcon(self.COL_LABEL, QIcon(pixmap))
|
||||
item.setData(self.COL_LABEL, self.KEY_ROLE, key)
|
||||
item.setData(self.COL_LABEL, self.COL_ROLE, cols)
|
||||
item.setData(self.COL_LABEL, self.COL_ROLE, colour)
|
||||
item.setData(self.COL_LABEL, self.NUM_ROLE, count)
|
||||
item.setText(self.COL_USAGE, self._usageString(count))
|
||||
|
||||
@@ -533,30 +553,29 @@ class GuiProjectEditStatus(QWidget):
|
||||
|
||||
if cItem is not None:
|
||||
cItem.setSelected(True)
|
||||
self.colChanged = True
|
||||
self._changed = True
|
||||
|
||||
return
|
||||
|
||||
def _getSelectedItem(self) -> QTreeWidgetItem | None:
|
||||
"""Get the currently selected item."""
|
||||
selItem = self.listBox.selectedItems()
|
||||
if len(selItem) > 0:
|
||||
return selItem[0]
|
||||
if items := self.listBox.selectedItems():
|
||||
return items[0]
|
||||
return None
|
||||
|
||||
def _usageString(self, nUse: int) -> str:
|
||||
def _usageString(self, count: int) -> str:
|
||||
"""Generate usage string."""
|
||||
if nUse == 0:
|
||||
if count == 0:
|
||||
return self.tr("Not in use")
|
||||
elif nUse == 1:
|
||||
elif count == 1:
|
||||
return self.tr("Used once")
|
||||
else:
|
||||
return self.tr("Used by {0} items").format(nUse)
|
||||
return self.tr("Used by {0} items").format(count)
|
||||
|
||||
# END Class GuiProjectEditStatus
|
||||
# END Class _StatusPage
|
||||
|
||||
|
||||
class GuiProjectEditReplace(QWidget):
|
||||
class _ReplacePage(NFixedPage):
|
||||
|
||||
COL_KEY = 0
|
||||
COL_REPL = 1
|
||||
@@ -564,24 +583,24 @@ class GuiProjectEditReplace(QWidget):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.arChanged = False
|
||||
self._changed = False
|
||||
|
||||
wCol0 = CONFIG.pxInt(
|
||||
SHARED.project.options.getInt("GuiProjectSettings", "replaceColW", 130)
|
||||
)
|
||||
pageLabel = self.tr("Text Replace List for Preview and Export")
|
||||
|
||||
# Title
|
||||
self.pageTitle = NColourLabel(
|
||||
self.tr("Text Auto-Replace for Preview and Build"),
|
||||
SHARED.theme.helpText, parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
# List Box
|
||||
# ========
|
||||
|
||||
self.listBox = QTreeWidget()
|
||||
self.listBox.setHeaderLabels([
|
||||
self.tr("Keyword"),
|
||||
self.tr("Replace With"),
|
||||
])
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
self.listBox.setHeaderLabels([self.tr("Keyword"), self.tr("Replace With")])
|
||||
self.listBox.setColumnWidth(self.COL_KEY, wCol0)
|
||||
self.listBox.setIndentation(0)
|
||||
self.listBox.itemSelectionChanged.connect(self._selectedItem)
|
||||
|
||||
for aKey, aVal in SHARED.project.data.autoReplace.items():
|
||||
newItem = QTreeWidgetItem(["<%s>" % aKey, aVal])
|
||||
@@ -591,8 +610,6 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.listBox.setSortingEnabled(True)
|
||||
|
||||
# List Controls
|
||||
# =============
|
||||
|
||||
self.addButton = QPushButton(SHARED.theme.getIcon("add"), "")
|
||||
self.addButton.clicked.connect(self._addEntry)
|
||||
|
||||
@@ -600,8 +617,6 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.delButton.clicked.connect(self._delEntry)
|
||||
|
||||
# Edit Form
|
||||
# =========
|
||||
|
||||
self.editKey = QLineEdit()
|
||||
self.editKey.setPlaceholderText(self.tr("Select item to edit"))
|
||||
self.editKey.setEnabled(False)
|
||||
@@ -615,8 +630,6 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.saveButton.clicked.connect(self._saveEntry)
|
||||
|
||||
# Assemble
|
||||
# ========
|
||||
|
||||
self.listControls = QVBoxLayout()
|
||||
self.listControls.addWidget(self.addButton)
|
||||
self.listControls.addWidget(self.delButton)
|
||||
@@ -636,50 +649,61 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.innerBox.addLayout(self.listControls)
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addWidget(QLabel("<b>%s</b>" % pageLabel))
|
||||
self.outerBox.addWidget(self.pageTitle)
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.setCentralLayout(self.outerBox)
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
def wasChanged(self) -> bool:
|
||||
"""The user changed these settings."""
|
||||
return self._changed
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def getNewList(self) -> dict:
|
||||
"""Extract the list from the widget."""
|
||||
new = {}
|
||||
for n in range(self.listBox.topLevelItemCount()):
|
||||
tItem = self.listBox.topLevelItem(n)
|
||||
if tItem is not None:
|
||||
if tItem := self.listBox.topLevelItem(n):
|
||||
aKey = self._stripNotAllowed(tItem.text(0))
|
||||
aVal = tItem.text(1)
|
||||
if len(aKey) > 0:
|
||||
new[aKey] = aVal
|
||||
return new
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
"""Return the size of the header column."""
|
||||
return self.listBox.columnWidth(0)
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
def _selectedItem(self) -> bool:
|
||||
@pyqtSlot()
|
||||
def _selectedItem(self) -> None:
|
||||
"""Extract the details from the selected item and populate the
|
||||
edit form.
|
||||
"""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem is None:
|
||||
return False
|
||||
editKey = self._stripNotAllowed(selItem.text(0))
|
||||
editVal = selItem.text(1)
|
||||
self.editKey.setText(editKey)
|
||||
self.editValue.setText(editVal)
|
||||
self.editKey.setEnabled(True)
|
||||
self.editValue.setEnabled(True)
|
||||
self.editKey.selectAll()
|
||||
self.editKey.setFocus()
|
||||
return True
|
||||
if selItem := self._getSelectedItem():
|
||||
editKey = self._stripNotAllowed(selItem.text(0))
|
||||
editVal = selItem.text(1)
|
||||
self.editKey.setText(editKey)
|
||||
self.editValue.setText(editVal)
|
||||
self.editKey.setEnabled(True)
|
||||
self.editValue.setEnabled(True)
|
||||
self.editKey.selectAll()
|
||||
self.editKey.setFocus()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _saveEntry(self) -> None:
|
||||
"""Save the form data into the list widget."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem:
|
||||
if selItem := self._getSelectedItem():
|
||||
newKey = self.editKey.text()
|
||||
newVal = self.editValue.text()
|
||||
saveKey = self._stripNotAllowed(newKey)
|
||||
@@ -691,38 +715,36 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.editKey.setEnabled(False)
|
||||
self.editValue.setEnabled(False)
|
||||
self.listBox.clearSelection()
|
||||
self.arChanged = True
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _addEntry(self) -> None:
|
||||
"""Add a new list entry."""
|
||||
saveKey = "<keyword%d>" % (self.listBox.topLevelItemCount() + 1)
|
||||
newVal = ""
|
||||
newItem = QTreeWidgetItem([saveKey, newVal])
|
||||
self.listBox.addTopLevelItem(newItem)
|
||||
self.listBox.addTopLevelItem(QTreeWidgetItem([saveKey, ""]))
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _delEntry(self) -> None:
|
||||
"""Delete the selected entry."""
|
||||
selItem = self._getSelectedItem()
|
||||
if selItem:
|
||||
if selItem := self._getSelectedItem():
|
||||
self.listBox.takeTopLevelItem(self.listBox.indexOfTopLevelItem(selItem))
|
||||
self.arChanged = True
|
||||
self._changed = True
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _getSelectedItem(self) -> QTreeWidgetItem | None:
|
||||
"""Extract the currently selected item."""
|
||||
selItem = self.listBox.selectedItems()
|
||||
if len(selItem) == 0:
|
||||
return None
|
||||
return selItem[0]
|
||||
if items := self.listBox.selectedItems():
|
||||
return items[0]
|
||||
return None
|
||||
|
||||
def _stripNotAllowed(self, key: str) -> str:
|
||||
"""Clean up the replace key string."""
|
||||
result = ""
|
||||
for c in key:
|
||||
if c.isalnum():
|
||||
result += c
|
||||
return result
|
||||
return "".join(c for c in key if c.isalnum())
|
||||
|
||||
# END Class GuiProjectEditReplace
|
||||
# END Class _ReplacePage
|
||||
@@ -6,6 +6,8 @@ File History:
|
||||
Created: 2020-05-03 [0.4.5] NConfigLayout, NColourLabel
|
||||
Created: 2023-05-23 [2.1b1] NSimpleLayout
|
||||
Created: 2024-01-08 [2.3b1] NScrollableForm
|
||||
Created: 2024-01-26 [2.3b1] NScrollablePage
|
||||
Created: 2024-01-26 [2.3b1] NFixedPage
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2024, Veronica Berglyd Olsen
|
||||
@@ -28,17 +30,59 @@ from __future__ import annotations
|
||||
from PyQt5.QtGui import QColor, QPalette
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea, QSizePolicy,
|
||||
QVBoxLayout, QWidget
|
||||
QAbstractButton, QFrame, QGridLayout, QHBoxLayout, QLabel, QLayout,
|
||||
QScrollArea, QSizePolicy, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG
|
||||
|
||||
FONT_SCALE = 0.9
|
||||
DEFAULT_SCALE = 0.9
|
||||
RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
|
||||
LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
|
||||
|
||||
|
||||
class NFixedPage(QFrame):
|
||||
"""Extension: Fixed Page Widget
|
||||
|
||||
A custom widget that holds a layout. This is just a wrapper around a
|
||||
QFrame that sets the same frame style as the other Page widgets.
|
||||
"""
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self.setFrameShadow(QFrame.Shadow.Sunken)
|
||||
self.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.setCentralLayout = self.setLayout
|
||||
return
|
||||
|
||||
# END Class NFixedPage
|
||||
|
||||
|
||||
class NScrollablePage(QScrollArea):
|
||||
"""Extension: Scrollable Page Widget
|
||||
|
||||
A custom widget that holds a layout within a scrollable area.
|
||||
"""
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self._widget = QWidget(self)
|
||||
self.setWidget(self._widget)
|
||||
self.setWidgetResizable(True)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setFrameShadow(QFrame.Shadow.Sunken)
|
||||
self.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
return
|
||||
|
||||
def setCentralLayout(self, layout: QLayout) -> None:
|
||||
"""Set the central layout of the scroll page."""
|
||||
self._widget.setLayout(layout)
|
||||
return
|
||||
|
||||
# END Class NScrollablePage
|
||||
|
||||
|
||||
class NScrollableForm(QScrollArea):
|
||||
"""Extension: Scrollable Form Widget
|
||||
|
||||
@@ -48,8 +92,9 @@ class NScrollableForm(QScrollArea):
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self._helpCol = QColor(0, 0, 0)
|
||||
self._fontScale = FONT_SCALE
|
||||
self._fontScale = DEFAULT_SCALE
|
||||
self._first = True
|
||||
self._indent = CONFIG.pxInt(12)
|
||||
|
||||
self._sections: dict[int, QLabel] = {}
|
||||
self._editable: dict[str, NColourLabel] = {}
|
||||
@@ -65,6 +110,8 @@ class NScrollableForm(QScrollArea):
|
||||
self.setWidgetResizable(True)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setFrameShadow(QFrame.Shadow.Sunken)
|
||||
self.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
|
||||
return
|
||||
|
||||
@@ -80,7 +127,7 @@ class NScrollableForm(QScrollArea):
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setHelpTextStyle(self, color: QColor, scale: float = FONT_SCALE) -> None:
|
||||
def setHelpTextStyle(self, color: QColor, scale: float = DEFAULT_SCALE) -> None:
|
||||
"""Set the text color for the help text."""
|
||||
self._helpCol = color
|
||||
self._fontScale = scale
|
||||
@@ -92,6 +139,11 @@ class NScrollableForm(QScrollArea):
|
||||
qHelp.setText(text)
|
||||
return
|
||||
|
||||
def setRowIndent(self, indent: int) -> None:
|
||||
"""Set the indentation of each row."""
|
||||
self._indent = max(indent, 0)
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
@@ -123,37 +175,43 @@ class NScrollableForm(QScrollArea):
|
||||
return
|
||||
|
||||
def addRow(self, label: str, widget: QWidget, helpText: str = "", unit: str | None = None,
|
||||
button: QWidget | None = None, editable: str | None = None) -> None:
|
||||
button: QWidget | None = None, editable: str | None = None,
|
||||
stretch: tuple[int, int] = (0, 0)) -> None:
|
||||
"""Add a label and a widget as a new row of the form."""
|
||||
row = QHBoxLayout()
|
||||
row.setSpacing(CONFIG.pxInt(4))
|
||||
row.setSpacing(CONFIG.pxInt(12))
|
||||
|
||||
mPx = CONFIG.pxInt(12)
|
||||
qLabel = QLabel(label, self)
|
||||
qLabel.setIndent(mPx)
|
||||
qLabel.setIndent(self._indent)
|
||||
qLabel.setBuddy(widget)
|
||||
|
||||
if helpText:
|
||||
qHelp = NColourLabel(str(helpText), self._helpCol, scale=self._fontScale, wrap=True)
|
||||
qHelp.setIndent(mPx)
|
||||
qHelp = NColourLabel(
|
||||
str(helpText), self._helpCol, parent=self,
|
||||
scale=self._fontScale, wrap=True, indent=self._indent
|
||||
)
|
||||
labelBox = QVBoxLayout()
|
||||
labelBox.addWidget(qLabel)
|
||||
labelBox.addWidget(qHelp)
|
||||
labelBox.setSpacing(0)
|
||||
labelBox.addStretch(1)
|
||||
row.addLayout(labelBox)
|
||||
row.addLayout(labelBox, stretch[0])
|
||||
if editable:
|
||||
self._editable[editable] = qHelp
|
||||
else:
|
||||
row.addWidget(qLabel)
|
||||
|
||||
row.addSpacing(mPx)
|
||||
row.addWidget(widget)
|
||||
row.addWidget(qLabel, stretch[0])
|
||||
|
||||
if isinstance(unit, str):
|
||||
row.addWidget(QLabel(unit, self))
|
||||
box = QHBoxLayout()
|
||||
box.addWidget(widget)
|
||||
box.addWidget(QLabel(unit, self))
|
||||
row.addLayout(box, stretch[1])
|
||||
elif isinstance(button, QAbstractButton):
|
||||
row.addWidget(button)
|
||||
box = QHBoxLayout()
|
||||
box.addWidget(widget)
|
||||
box.addWidget(button)
|
||||
row.addLayout(box, stretch[1])
|
||||
else:
|
||||
row.addWidget(widget, stretch[1])
|
||||
|
||||
self._layout.addLayout(row)
|
||||
self._index[label.strip()] = widget
|
||||
@@ -177,7 +235,7 @@ class NConfigLayout(QGridLayout):
|
||||
|
||||
self._nextRow = 0
|
||||
self._helpCol = QColor(0, 0, 0)
|
||||
self._fontScale = FONT_SCALE
|
||||
self._fontScale = DEFAULT_SCALE
|
||||
self._itemMap = {}
|
||||
|
||||
wSp = CONFIG.pxInt(8)
|
||||
@@ -187,16 +245,6 @@ class NConfigLayout(QGridLayout):
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Getters and Setters
|
||||
##
|
||||
|
||||
def setHelpTextStyle(self, color: QColor, 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
|
||||
|
||||
##
|
||||
# Class Methods
|
||||
##
|
||||
@@ -212,8 +260,8 @@ class NConfigLayout(QGridLayout):
|
||||
self._nextRow += 1
|
||||
return
|
||||
|
||||
def addRow(self, label: str, widget: QWidget, helpText: str | None = None,
|
||||
unit: str | None = None, button: QWidget | None = None) -> int:
|
||||
def addRow(self, label: str, widget: QWidget, unit: str | None = None,
|
||||
button: QWidget | None = None) -> int:
|
||||
"""Add a label and a widget as a new row of the grid."""
|
||||
wSp = CONFIG.pxInt(8)
|
||||
qLabel = QLabel(label)
|
||||
@@ -221,17 +269,7 @@ class NConfigLayout(QGridLayout):
|
||||
qLabel.setBuddy(widget)
|
||||
|
||||
qHelp = None
|
||||
if helpText is not None:
|
||||
qHelp = NColourLabel(str(helpText), self._helpCol, scale=self._fontScale, wrap=True)
|
||||
qHelp.setIndent(wSp)
|
||||
labelBox = QVBoxLayout()
|
||||
labelBox.addWidget(qLabel)
|
||||
labelBox.addWidget(qHelp)
|
||||
labelBox.setSpacing(0)
|
||||
labelBox.addStretch(1)
|
||||
self.addLayout(labelBox, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
else:
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
|
||||
if isinstance(unit, str):
|
||||
controlBox = QHBoxLayout()
|
||||
@@ -248,12 +286,7 @@ class NConfigLayout(QGridLayout):
|
||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
|
||||
else:
|
||||
if isinstance(widget, QLineEdit):
|
||||
qLayout = QHBoxLayout()
|
||||
qLayout.addWidget(widget)
|
||||
self.addLayout(qLayout, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
else:
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
|
||||
self.setRowStretch(self._nextRow, 0)
|
||||
self.setRowStretch(self._nextRow+1, 1)
|
||||
@@ -321,17 +354,21 @@ class NColourLabel(QLabel):
|
||||
optionally at a specific size, and word wrapped.
|
||||
"""
|
||||
|
||||
HELP_SCALE = DEFAULT_SCALE
|
||||
HEADER_SCALE = 1.25
|
||||
|
||||
def __init__(self, text: str, color: QColor, parent: QWidget | None = None,
|
||||
scale: float = FONT_SCALE, wrap: bool = False) -> None:
|
||||
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0) -> None:
|
||||
super().__init__(text, parent=parent)
|
||||
|
||||
lblCol = self.palette()
|
||||
lblCol.setColor(QPalette.WindowText, color)
|
||||
self.setPalette(lblCol)
|
||||
font = self.font()
|
||||
font.setPointSizeF(scale*font.pointSizeF())
|
||||
colour = self.palette()
|
||||
colour.setColor(QPalette.WindowText, color)
|
||||
|
||||
lblFont = self.font()
|
||||
lblFont.setPointSizeF(scale*lblFont.pointSizeF())
|
||||
self.setFont(lblFont)
|
||||
self.setPalette(colour)
|
||||
self.setFont(font)
|
||||
self.setIndent(indent)
|
||||
|
||||
if wrap:
|
||||
self.setWordWrap(True)
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
"""
|
||||
novelWriter – Custom Widget: Paged Dialog
|
||||
=========================================
|
||||
|
||||
File History:
|
||||
Created: 2020-05-17 [0.5.1]
|
||||
|
||||
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.QtGui import QPaintEvent
|
||||
from PyQt5.QtCore import QRect, QPoint, QSize
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QHBoxLayout, QStyle, QStyleOptionTab, QStylePainter, QTabBar,
|
||||
QTabWidget, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG
|
||||
|
||||
|
||||
class NPagedDialog(QDialog):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self._tabBar = NVerticalTabBar(self)
|
||||
self._tabBar.setExpanding(False)
|
||||
|
||||
self._tabBox = QTabWidget(self)
|
||||
self._tabBox.setTabBar(self._tabBar)
|
||||
self._tabBox.setTabPosition(QTabWidget.West)
|
||||
|
||||
self._buttonBox = QHBoxLayout()
|
||||
|
||||
self._outerBox = QVBoxLayout()
|
||||
self._outerBox.addWidget(self._tabBox)
|
||||
self._outerBox.addLayout(self._buttonBox)
|
||||
|
||||
# Default Margins
|
||||
thisStyle = self.style()
|
||||
mL = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
||||
mR = thisStyle.pixelMetric(QStyle.PM_LayoutRightMargin)
|
||||
mT = thisStyle.pixelMetric(QStyle.PM_LayoutLeftMargin)
|
||||
mB = thisStyle.pixelMetric(QStyle.PM_LayoutBottomMargin)
|
||||
|
||||
# Set Margins
|
||||
self.setContentsMargins(0, 0, 0, 0)
|
||||
self._outerBox.setContentsMargins(0, 0, 0, mB)
|
||||
self._buttonBox.setContentsMargins(mL, 0, mR, 0)
|
||||
self._outerBox.setSpacing(mT)
|
||||
|
||||
self.setLayout(self._outerBox)
|
||||
|
||||
return
|
||||
|
||||
def addTab(self, widget: QWidget, label: str) -> None:
|
||||
"""Forward the adding of tabs to the QTabWidget."""
|
||||
self._tabBox.addTab(widget, label)
|
||||
return
|
||||
|
||||
def addControls(self, buttonBar: QWidget) -> None:
|
||||
"""Add a button bar to the dialog."""
|
||||
self._buttonBox.addWidget(buttonBar)
|
||||
return
|
||||
|
||||
def setCurrentWidget(self, widget: QWidget) -> None:
|
||||
"""Forward the changing of tab to the QTabWidget."""
|
||||
self._tabBox.setCurrentWidget(widget)
|
||||
return
|
||||
|
||||
# END Class NPagedDialog
|
||||
|
||||
|
||||
class NVerticalTabBar(QTabBar):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self._mW = CONFIG.pxInt(150)
|
||||
return
|
||||
|
||||
def tabSizeHint(self, index: int) -> QSize:
|
||||
"""Return a transposed size hint for the rotated bar."""
|
||||
tSize = super().tabSizeHint(index)
|
||||
tSize.transpose()
|
||||
tSize.setWidth(min(tSize.width(), self._mW))
|
||||
return tSize
|
||||
|
||||
def paintEvent(self, event: QPaintEvent) -> None:
|
||||
"""Custom implementation of the label painter that rotates the
|
||||
label 90 degrees.
|
||||
"""
|
||||
pObj = QStylePainter(self)
|
||||
oObj = QStyleOptionTab()
|
||||
|
||||
for i in range(self.count()):
|
||||
self.initStyleOption(oObj, i)
|
||||
pObj.drawControl(QStyle.CE_TabBarTabShape, oObj)
|
||||
pObj.save()
|
||||
|
||||
oSize = oObj.rect.size()
|
||||
oSize.transpose()
|
||||
oRect = QRect(QPoint(), oSize)
|
||||
oRect.moveCenter(oObj.rect.center())
|
||||
oObj.rect = oRect
|
||||
|
||||
oCenter = self.tabRect(i).center()
|
||||
pObj.translate(oCenter)
|
||||
pObj.rotate(90)
|
||||
pObj.translate(-oCenter)
|
||||
pObj.drawControl(QStyle.CE_TabBarTabLabel, oObj)
|
||||
pObj.restore()
|
||||
|
||||
return
|
||||
|
||||
# END Class NVerticalTabBar
|
||||
@@ -48,7 +48,7 @@ from novelwriter.core.coretools import DocDuplicator, DocMerger, DocSplitter
|
||||
from novelwriter.dialogs.docmerge import GuiDocMerge
|
||||
from novelwriter.dialogs.docsplit import GuiDocSplit
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
from novelwriter.enum import (
|
||||
nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwWidget
|
||||
)
|
||||
@@ -1748,7 +1748,7 @@ class _TreeContextMenu(QMenu):
|
||||
menu.addSeparator()
|
||||
action = menu.addAction(self.tr("Manage Labels ..."))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_STATUS)
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_STATUS)
|
||||
)
|
||||
else:
|
||||
menu = self.addMenu(self.tr("Set Importance to ..."))
|
||||
@@ -1765,7 +1765,7 @@ class _TreeContextMenu(QMenu):
|
||||
menu.addSeparator()
|
||||
action = menu.addAction(self.tr("Manage Labels ..."))
|
||||
action.triggered.connect(
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.TAB_IMPORT)
|
||||
lambda: self.projView.projectSettingsRequest.emit(GuiProjectSettings.PAGE_IMPORT)
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ from novelwriter.dialogs.about import GuiAbout
|
||||
from novelwriter.dialogs.updates import GuiUpdates
|
||||
from novelwriter.dialogs.wordlist import GuiWordList
|
||||
from novelwriter.dialogs.preferences import GuiPreferences
|
||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
from novelwriter.tools.welcome import GuiWelcome
|
||||
from novelwriter.tools.manuscript import GuiManuscript
|
||||
from novelwriter.tools.dictionaries import GuiDictionaries
|
||||
@@ -809,10 +809,10 @@ class GuiMain(QMainWindow):
|
||||
|
||||
@pyqtSlot()
|
||||
@pyqtSlot(int)
|
||||
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.TAB_MAIN) -> None:
|
||||
def showProjectSettingsDialog(self, focusTab: int = GuiProjectSettings.PAGE_SETTINGS) -> None:
|
||||
"""Open the project settings dialog."""
|
||||
if SHARED.hasProject:
|
||||
dialog = GuiProjectSettings(self, focusTab=focusTab)
|
||||
dialog = GuiProjectSettings(self, gotoPage=focusTab)
|
||||
dialog.newProjectSettingsReady.connect(self._processProjectSettingsChanges)
|
||||
dialog.exec_()
|
||||
return
|
||||
@@ -1111,13 +1111,15 @@ class GuiMain(QMainWindow):
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _processProjectSettingsChanges(self) -> None:
|
||||
@pyqtSlot(bool)
|
||||
def _processProjectSettingsChanges(self, rebuildTrees: bool) -> None:
|
||||
"""Refresh data dependent on project settings."""
|
||||
logger.debug("Applying new project settings")
|
||||
SHARED.updateSpellCheckLanguage()
|
||||
self.itemDetails.refreshDetails()
|
||||
self._updateWindowTitle(SHARED.project.data.name)
|
||||
if rebuildTrees:
|
||||
self.rebuildTrees()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
|
||||
@@ -38,14 +38,12 @@ from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import formatTime, numberToRoman
|
||||
from novelwriter.constants import nwUnicode
|
||||
from novelwriter.extensions.switch import NSwitch
|
||||
from novelwriter.extensions.configlayout import NColourLabel
|
||||
from novelwriter.extensions.configlayout import NColourLabel, NFixedPage, NScrollablePage
|
||||
from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
||||
from novelwriter.extensions.novelselector import NovelSelector
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HEADER_SIZE = 1.4
|
||||
|
||||
|
||||
class GuiNovelDetails(QDialog):
|
||||
|
||||
@@ -59,21 +57,18 @@ class GuiNovelDetails(QDialog):
|
||||
self.setObjectName("GuiNovelDetails")
|
||||
self.setWindowTitle(self.tr("Novel Details"))
|
||||
|
||||
wW = CONFIG.pxInt(500)
|
||||
wH = CONFIG.pxInt(400)
|
||||
options = SHARED.project.options
|
||||
|
||||
self.setMinimumSize(wW, wH)
|
||||
self.setMinimumSize(CONFIG.pxInt(500), CONFIG.pxInt(400))
|
||||
self.resize(
|
||||
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", wW)),
|
||||
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", wH))
|
||||
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winWidth", CONFIG.pxInt(650))),
|
||||
CONFIG.pxInt(options.getInt("GuiNovelDetails", "winHeight", CONFIG.pxInt(500)))
|
||||
)
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Novel Details"), SHARED.theme.helpText, parent=self, scale=1.25
|
||||
self.tr("Novel Details"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
self.titleLabel.setIndent(CONFIG.pxInt(4))
|
||||
|
||||
# Novel Selector
|
||||
self.novelSelector = NovelSelector(self)
|
||||
@@ -192,7 +187,7 @@ class GuiNovelDetails(QDialog):
|
||||
# END Class GuiNovelDetails
|
||||
|
||||
|
||||
class _OverviewPage(QWidget):
|
||||
class _OverviewPage(NScrollablePage):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
@@ -204,7 +199,8 @@ class _OverviewPage(QWidget):
|
||||
|
||||
# Project Info
|
||||
self.projLabel = NColourLabel(
|
||||
self.tr("Project"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE
|
||||
self.tr("Project"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
self.projName = QLabel("", self)
|
||||
@@ -227,7 +223,8 @@ class _OverviewPage(QWidget):
|
||||
|
||||
# Novel Info
|
||||
self.novelLabel = NColourLabel(
|
||||
self.tr("Selected Novel"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE
|
||||
self.tr("Selected Novel"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
self.novelName = QLabel("", self)
|
||||
@@ -250,11 +247,10 @@ class _OverviewPage(QWidget):
|
||||
self.outerBox.addLayout(self.projForm)
|
||||
self.outerBox.addWidget(self.novelLabel)
|
||||
self.outerBox.addLayout(self.novelForm)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
self.outerBox.setSpacing(sPx)
|
||||
self.outerBox.addStretch(1)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.setCentralLayout(self.outerBox)
|
||||
|
||||
return
|
||||
|
||||
@@ -299,7 +295,7 @@ class _OverviewPage(QWidget):
|
||||
# END Class _OverviewPage
|
||||
|
||||
|
||||
class _ContentsPage(QWidget):
|
||||
class _ContentsPage(NFixedPage):
|
||||
|
||||
C_TITLE = 0
|
||||
C_WORDS = 1
|
||||
@@ -320,7 +316,8 @@ class _ContentsPage(QWidget):
|
||||
|
||||
# Title
|
||||
self.contentLabel = NColourLabel(
|
||||
self.tr("Table of Contents"), SHARED.theme.helpText, parent=self, scale=HEADER_SIZE
|
||||
self.tr("Table of Contents"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
# Contents Tree
|
||||
@@ -407,20 +404,25 @@ class _ContentsPage(QWidget):
|
||||
self.outerBox.addWidget(self.contentLabel)
|
||||
self.outerBox.addWidget(self.tocTree)
|
||||
self.outerBox.addLayout(self.optionsBox)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.setCentralLayout(self.outerBox)
|
||||
|
||||
return
|
||||
|
||||
def saveSettings(self) -> None:
|
||||
"""Save the user GUI settings."""
|
||||
widthCol0 = CONFIG.rpxInt(self.tocTree.columnWidth(0))
|
||||
widthCol1 = CONFIG.rpxInt(self.tocTree.columnWidth(1))
|
||||
widthCol2 = CONFIG.rpxInt(self.tocTree.columnWidth(2))
|
||||
widthCol3 = CONFIG.rpxInt(self.tocTree.columnWidth(3))
|
||||
widthCol4 = CONFIG.rpxInt(self.tocTree.columnWidth(4))
|
||||
|
||||
options = SHARED.project.options
|
||||
options.setValue("GuiNovelDetails", "widthCol0", self.tocTree.columnWidth(0))
|
||||
options.setValue("GuiNovelDetails", "widthCol1", self.tocTree.columnWidth(1))
|
||||
options.setValue("GuiNovelDetails", "widthCol2", self.tocTree.columnWidth(2))
|
||||
options.setValue("GuiNovelDetails", "widthCol3", self.tocTree.columnWidth(3))
|
||||
options.setValue("GuiNovelDetails", "widthCol4", self.tocTree.columnWidth(4))
|
||||
options.setValue("GuiNovelDetails", "widthCol0", widthCol0)
|
||||
options.setValue("GuiNovelDetails", "widthCol1", widthCol1)
|
||||
options.setValue("GuiNovelDetails", "widthCol2", widthCol2)
|
||||
options.setValue("GuiNovelDetails", "widthCol3", widthCol3)
|
||||
options.setValue("GuiNovelDetails", "widthCol4", widthCol4)
|
||||
options.setValue("GuiNovelDetails", "wordsPerPage", self.wpValue.value())
|
||||
options.setValue("GuiNovelDetails", "countFrom", self.poValue.value())
|
||||
options.setValue("GuiNovelDetails", "clearDouble", self.dblValue.isChecked())
|
||||
|
||||
+135
-150
@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import QDialog, QAction, QColorDialog
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.enum import nwItemType
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.dialogs.projsettings import GuiProjectSettings
|
||||
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
|
||||
KEY_DELAY = 1
|
||||
|
||||
@@ -63,17 +63,17 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||
qtbot.addWidget(projSettings)
|
||||
|
||||
# Switch Tabs
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_REPLACE)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabReplace
|
||||
projSettings.sidebar.button(GuiProjectSettings.PAGE_SETTINGS).click()
|
||||
assert projSettings.mainStack.currentWidget() == projSettings.settingsPage
|
||||
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabImport
|
||||
projSettings.sidebar.button(GuiProjectSettings.PAGE_STATUS).click()
|
||||
assert projSettings.mainStack.currentWidget() == projSettings.statusPage
|
||||
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_STATUS)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabStatus
|
||||
projSettings.sidebar.button(GuiProjectSettings.PAGE_IMPORT).click()
|
||||
assert projSettings.mainStack.currentWidget() == projSettings.importPage
|
||||
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_MAIN)
|
||||
assert projSettings._tabBox.currentWidget() == projSettings.tabMain
|
||||
projSettings.sidebar.button(GuiProjectSettings.PAGE_REPLACE).click()
|
||||
assert projSettings.mainStack.currentWidget() == projSettings.replacePage
|
||||
|
||||
# Clean Up
|
||||
projSettings.close()
|
||||
@@ -83,9 +83,11 @@ def testDlgProjSettings_Dialog(qtbot, monkeypatch, nwGUI):
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
||||
"""Test the main tab of the project settings dialog."""
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
def testDlgProjSettings_SettingsPage(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
||||
"""Test the settings page of the dialog."""
|
||||
languages = [("en", "English"), ("de", "German")]
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda *a: languages)
|
||||
monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages)
|
||||
|
||||
# Create new project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
@@ -94,72 +96,57 @@ def testDlgProjSettings_Main(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockR
|
||||
|
||||
# Set some values
|
||||
project = SHARED.project
|
||||
project.data.setLanguage("en")
|
||||
project.data.setSpellLang("en")
|
||||
project.data.setAuthor("Jane Smith")
|
||||
project.data.setAutoReplace({"A": "B", "C": "D"})
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_MAIN)
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_SETTINGS)
|
||||
projSettings.show()
|
||||
qtbot.addWidget(projSettings)
|
||||
|
||||
# Settings Tab
|
||||
# ============
|
||||
settings = projSettings.settingsPage
|
||||
|
||||
tabMain = projSettings.tabMain
|
||||
assert settings.projName.text() == "New Project"
|
||||
assert settings.projAuthor.text() == "Jane Smith"
|
||||
assert settings.projLang.currentData() == "en"
|
||||
assert settings.spellLang.currentData() == "en"
|
||||
assert settings.doBackup.isChecked() is False
|
||||
|
||||
assert tabMain.editName.text() == "New Project"
|
||||
assert tabMain.editTitle.text() == "New Novel"
|
||||
assert tabMain.editAuthor.text() == "Jane Smith"
|
||||
assert tabMain.spellLang.currentData() == "en"
|
||||
assert tabMain.doBackup.isChecked() is False
|
||||
|
||||
tabMain.editName.setText("")
|
||||
for c in "Project Name":
|
||||
qtbot.keyClick(tabMain.editName, c, delay=KEY_DELAY)
|
||||
tabMain.editTitle.setText("")
|
||||
for c in "Project Title":
|
||||
qtbot.keyClick(tabMain.editTitle, c, delay=KEY_DELAY)
|
||||
|
||||
tabMain.editAuthor.clear()
|
||||
for c in "Jane Doe":
|
||||
qtbot.keyClick(tabMain.editAuthor, c, delay=KEY_DELAY)
|
||||
|
||||
assert tabMain.editName.text() == "Project Name"
|
||||
assert tabMain.editTitle.text() == "Project Title"
|
||||
assert tabMain.editAuthor.text() == "Jane Doe"
|
||||
settings.projName.setText("Project Name")
|
||||
settings.projAuthor.setText("Jane Doe")
|
||||
settings.projLang.setCurrentIndex(settings.projLang.findData("de"))
|
||||
settings.spellLang.setCurrentIndex(settings.spellLang.findData("de"))
|
||||
settings.doBackup.setChecked(True)
|
||||
|
||||
projSettings._doSave()
|
||||
assert project.data.name == "Project Name"
|
||||
assert project.data.title == "Project Title"
|
||||
assert project.data.author == "Jane Doe"
|
||||
assert project.data.language == "de"
|
||||
assert project.data.spellLang == "de"
|
||||
assert project.data.doBackup is False
|
||||
|
||||
nwGUI._processProjectSettingsChanges()
|
||||
nwGUI._processProjectSettingsChanges(False)
|
||||
assert nwGUI.windowTitle() == "novelWriter - Project Name"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testDlgProjSettings_Main
|
||||
# END Test testDlgProjSettings_SettingsPage
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
||||
"""Test the status and importance tabs of the project settings
|
||||
dialog.
|
||||
"""
|
||||
def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test the status and importance pages of the dialog."""
|
||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
|
||||
# Create new project
|
||||
mockRnd.reset()
|
||||
buildTestProject(nwGUI, projPath)
|
||||
CONFIG.setBackupPath(fncPath)
|
||||
|
||||
# Set some values
|
||||
theProject = SHARED.project
|
||||
theProject.tree[C.hTitlePage].setStatus(C.sFinished) # type: ignore
|
||||
theProject.tree[C.hChapterDoc].setStatus(C.sDraft) # type: ignore
|
||||
theProject.tree[C.hSceneDoc].setStatus(C.sDraft) # type: ignore
|
||||
project = SHARED.project
|
||||
project.tree[C.hTitlePage].setStatus(C.sFinished) # type: ignore
|
||||
project.tree[C.hChapterDoc].setStatus(C.sDraft) # type: ignore
|
||||
project.tree[C.hSceneDoc].setStatus(C.sDraft) # type: ignore
|
||||
|
||||
nwGUI.projView.projTree.setSelectedHandle(C.hPlotRoot)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||
@@ -172,51 +159,54 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
hCharNote = "0000000000011"
|
||||
hWorldNote = "0000000000012"
|
||||
|
||||
theProject.tree[hPlotNote].setImport(C.iMajor) # type: ignore
|
||||
theProject.tree[hCharNote].setImport(C.iMajor) # type: ignore
|
||||
theProject.tree[hWorldNote].setImport(C.iMain) # type: ignore
|
||||
project.tree[hPlotNote].setImport(C.iMajor) # type: ignore
|
||||
project.tree[hCharNote].setImport(C.iMajor) # type: ignore
|
||||
project.tree[hWorldNote].setImport(C.iMain) # type: ignore
|
||||
|
||||
nwGUI.rebuildTrees()
|
||||
project.countStatus()
|
||||
|
||||
assert [e["count"] for _, e in project.data.itemStatus.items()] == [2, 0, 2, 1]
|
||||
assert [e["count"] for _, e in project.data.itemImport.items()] == [3, 0, 2, 1]
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_STATUS)
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_STATUS)
|
||||
projSettings.show()
|
||||
qtbot.addWidget(projSettings)
|
||||
|
||||
# Status Tab
|
||||
# ==========
|
||||
|
||||
tabStatus = projSettings.tabStatus
|
||||
status = projSettings.statusPage
|
||||
|
||||
assert tabStatus.colChanged is False
|
||||
assert tabStatus.getNewList() == ([], [])
|
||||
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||
assert status.wasChanged is False
|
||||
assert status.getNewList() == ([], [])
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
# Can't delete the first item (it's in use)
|
||||
tabStatus.listBox.clearSelection()
|
||||
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
|
||||
qtbot.mouseClick(status.delButton, Qt.LeftButton)
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
# Can delete the second item
|
||||
tabStatus.listBox.clearSelection()
|
||||
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(tabStatus.delButton, Qt.LeftButton)
|
||||
assert tabStatus.listBox.topLevelItemCount() == 3
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(status.delButton, Qt.LeftButton)
|
||||
assert status.listBox.topLevelItemCount() == 3
|
||||
|
||||
# Add a new item
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||
qtbot.mouseClick(tabStatus.addButton, Qt.LeftButton)
|
||||
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||
for _ in range(8):
|
||||
qtbot.keyClick(tabStatus.editName, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||
for c in "Final":
|
||||
qtbot.keyClick(tabStatus.editName, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(tabStatus.colButton, Qt.LeftButton)
|
||||
qtbot.mouseClick(tabStatus.saveButton, Qt.LeftButton)
|
||||
assert tabStatus.listBox.topLevelItemCount() == 4
|
||||
status.addButton.click()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
|
||||
status.editName.setText("Final")
|
||||
status.colButton.click()
|
||||
status.saveButton.click()
|
||||
assert status.listBox.topLevelItemCount() == 4
|
||||
|
||||
assert tabStatus.colChanged is True
|
||||
assert tabStatus.getNewList() == (
|
||||
assert status.wasChanged is True
|
||||
assert status.getNewList() == (
|
||||
[
|
||||
{
|
||||
"key": C.sNew,
|
||||
@@ -241,62 +231,62 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
)
|
||||
|
||||
# Move items, none selected -> no change
|
||||
tabStatus.listBox.clearSelection()
|
||||
tabStatus._moveItem(1)
|
||||
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||
status.listBox.clearSelection()
|
||||
status._moveItem(1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
# Move items, first selected, move up -> no change
|
||||
tabStatus.listBox.clearSelection()
|
||||
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(0))
|
||||
tabStatus._moveItem(-1)
|
||||
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(0))
|
||||
status._moveItem(-1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
# Move items, last selected, move up -> allowed
|
||||
tabStatus.listBox.clearSelection()
|
||||
tabStatus.listBox.setCurrentItem(tabStatus.listBox.topLevelItem(3))
|
||||
tabStatus._moveItem(-1)
|
||||
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||
status.listBox.clearSelection()
|
||||
status.listBox.setCurrentItem(status.listBox.topLevelItem(3))
|
||||
status._moveItem(-1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
C.sNew, C.sDraft, None, C.sFinished
|
||||
]
|
||||
|
||||
# Move items, same selected, move down -> allowed
|
||||
tabStatus._moveItem(1)
|
||||
assert [x["key"] for x in tabStatus.getNewList()[0]] == [
|
||||
status._moveItem(1)
|
||||
assert [x["key"] for x in status.getNewList()[0]] == [
|
||||
C.sNew, C.sDraft, C.sFinished, None
|
||||
]
|
||||
|
||||
# Importance Tab
|
||||
# ==============
|
||||
|
||||
tabImport = projSettings.tabImport
|
||||
projSettings._focusTab(GuiProjectSettings.TAB_IMPORT)
|
||||
importance = projSettings.importPage
|
||||
projSettings._sidebarClicked(GuiProjectSettings.PAGE_IMPORT)
|
||||
|
||||
# Delete unused entry
|
||||
tabImport.listBox.clearSelection()
|
||||
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(tabImport.delButton, Qt.LeftButton)
|
||||
assert tabImport.listBox.topLevelItemCount() == 3
|
||||
importance.listBox.clearSelection()
|
||||
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(1))
|
||||
qtbot.mouseClick(importance.delButton, Qt.LeftButton)
|
||||
assert importance.listBox.topLevelItemCount() == 3
|
||||
|
||||
# Add a new entry
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QColorDialog, "getColor", lambda *a: QColor(20, 30, 40))
|
||||
qtbot.mouseClick(tabImport.addButton, Qt.LeftButton)
|
||||
tabImport.listBox.clearSelection()
|
||||
tabImport.listBox.setCurrentItem(tabImport.listBox.topLevelItem(3))
|
||||
qtbot.mouseClick(importance.addButton, Qt.LeftButton)
|
||||
importance.listBox.clearSelection()
|
||||
importance.listBox.setCurrentItem(importance.listBox.topLevelItem(3))
|
||||
for _ in range(8):
|
||||
qtbot.keyClick(tabImport.editName, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||
qtbot.keyClick(importance.editName, Qt.Key_Backspace, delay=KEY_DELAY)
|
||||
for c in "Final":
|
||||
qtbot.keyClick(tabImport.editName, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(tabImport.colButton, Qt.LeftButton)
|
||||
qtbot.mouseClick(tabImport.saveButton, Qt.LeftButton)
|
||||
assert tabImport.listBox.topLevelItemCount() == 4
|
||||
qtbot.keyClick(importance.editName, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(importance.colButton, Qt.LeftButton)
|
||||
qtbot.mouseClick(importance.saveButton, Qt.LeftButton)
|
||||
assert importance.listBox.topLevelItemCount() == 4
|
||||
|
||||
assert tabImport.colChanged is True
|
||||
assert tabImport.getNewList() == (
|
||||
assert importance.wasChanged is True
|
||||
assert importance.getNewList() == (
|
||||
[
|
||||
{
|
||||
"key": C.iNew,
|
||||
@@ -323,13 +313,13 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
# Check Project
|
||||
projSettings._doSave()
|
||||
|
||||
statusItems = dict(theProject.data.itemStatus.items())
|
||||
statusItems = dict(project.data.itemStatus.items())
|
||||
assert statusItems[C.sNew]["name"] == "New"
|
||||
assert statusItems[C.sDraft]["name"] == "Draft"
|
||||
assert statusItems[C.sFinished]["name"] == "Finished"
|
||||
assert statusItems["s000013"]["name"] == "Final"
|
||||
|
||||
importItems = dict(theProject.data.itemImport.items())
|
||||
importItems = dict(project.data.itemImport.items())
|
||||
assert importItems[C.iNew]["name"] == "New"
|
||||
assert importItems[C.iMajor]["name"] == "Major"
|
||||
assert importItems[C.iMain]["name"] == "Main"
|
||||
@@ -341,83 +331,78 @@ def testDlgProjSettings_StatusImport(qtbot, monkeypatch, nwGUI, fncPath, projPat
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd):
|
||||
"""Test the auto-replace tab of the project settings dialog."""
|
||||
def testDlgProjSettings_Replace(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test the auto-replace page of the dialog."""
|
||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
|
||||
monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: [("en", "English [en]")])
|
||||
|
||||
# Create new project
|
||||
mockRnd.reset()
|
||||
buildTestProject(nwGUI, projPath)
|
||||
CONFIG.setBackupPath(fncPath)
|
||||
|
||||
# Set some values
|
||||
theProject = SHARED.project
|
||||
theProject.data.setAutoReplace({
|
||||
project = SHARED.project
|
||||
project.data.setAutoReplace({
|
||||
"A": "B", "C": "D"
|
||||
})
|
||||
|
||||
# Create Dialog
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.TAB_REPLACE)
|
||||
projSettings = GuiProjectSettings(nwGUI, GuiProjectSettings.PAGE_REPLACE)
|
||||
projSettings.show()
|
||||
qtbot.addWidget(projSettings)
|
||||
|
||||
# Auto-Replace Tab
|
||||
# ================
|
||||
|
||||
tabReplace = projSettings.tabReplace
|
||||
replace = projSettings.replacePage
|
||||
|
||||
assert tabReplace.listBox.topLevelItem(0).text(0) == "<A>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(0).text(1) == "B" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(1).text(0) == "<C>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(1).text(1) == "D" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||
assert replace.listBox.topLevelItem(0).text(0) == "<A>" # type: ignore
|
||||
assert replace.listBox.topLevelItem(0).text(1) == "B" # type: ignore
|
||||
assert replace.listBox.topLevelItem(1).text(0) == "<C>" # type: ignore
|
||||
assert replace.listBox.topLevelItem(1).text(1) == "D" # type: ignore
|
||||
assert replace.listBox.topLevelItemCount() == 2
|
||||
|
||||
# Nothing to save or delete
|
||||
tabReplace.listBox.clearSelection()
|
||||
tabReplace._saveEntry()
|
||||
tabReplace._delEntry()
|
||||
assert tabReplace.listBox.topLevelItemCount() == 2
|
||||
replace.listBox.clearSelection()
|
||||
replace._saveEntry()
|
||||
replace._delEntry()
|
||||
assert replace.listBox.topLevelItemCount() == 2
|
||||
|
||||
# Create a new entry
|
||||
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<keyword3>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == "" # type: ignore
|
||||
qtbot.mouseClick(replace.addButton, Qt.LeftButton)
|
||||
assert replace.listBox.topLevelItemCount() == 3
|
||||
assert replace.listBox.topLevelItem(2).text(0) == "<keyword3>" # type: ignore
|
||||
assert replace.listBox.topLevelItem(2).text(1) == "" # type: ignore
|
||||
|
||||
# Edit the entry
|
||||
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(2))
|
||||
tabReplace.editKey.setText("")
|
||||
replace.listBox.setCurrentItem(replace.listBox.topLevelItem(2))
|
||||
replace.editKey.setText("")
|
||||
for c in "Th is ":
|
||||
qtbot.keyClick(tabReplace.editKey, c, delay=KEY_DELAY)
|
||||
tabReplace.editValue.setText("")
|
||||
qtbot.keyClick(replace.editKey, c, delay=KEY_DELAY)
|
||||
replace.editValue.setText("")
|
||||
for c in "With This Stuff ":
|
||||
qtbot.keyClick(tabReplace.editValue, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(tabReplace.saveButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
|
||||
assert tabReplace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore
|
||||
qtbot.keyClick(replace.editValue, c, delay=KEY_DELAY)
|
||||
qtbot.mouseClick(replace.saveButton, Qt.LeftButton)
|
||||
assert replace.listBox.topLevelItem(2).text(0) == "<This>" # type: ignore
|
||||
assert replace.listBox.topLevelItem(2).text(1) == "With This Stuff " # type: ignore
|
||||
|
||||
# Create a new entry again
|
||||
tabReplace.listBox.clearSelection()
|
||||
qtbot.mouseClick(tabReplace.addButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItemCount() == 4
|
||||
replace.listBox.clearSelection()
|
||||
qtbot.mouseClick(replace.addButton, Qt.LeftButton)
|
||||
assert replace.listBox.topLevelItemCount() == 4
|
||||
|
||||
# The list is sorted, so we must find it
|
||||
newIdx = -1
|
||||
for i in range(tabReplace.listBox.topLevelItemCount()):
|
||||
if tabReplace.listBox.topLevelItem(i).text(0) == "<keyword4>": # type: ignore
|
||||
for i in range(replace.listBox.topLevelItemCount()):
|
||||
if replace.listBox.topLevelItem(i).text(0) == "<keyword4>": # type: ignore
|
||||
newIdx = i
|
||||
break
|
||||
assert newIdx >= 0
|
||||
|
||||
# Then delete the new item
|
||||
tabReplace.listBox.setCurrentItem(tabReplace.listBox.topLevelItem(newIdx))
|
||||
qtbot.mouseClick(tabReplace.delButton, Qt.LeftButton)
|
||||
assert tabReplace.listBox.topLevelItemCount() == 3
|
||||
replace.listBox.setCurrentItem(replace.listBox.topLevelItem(newIdx))
|
||||
qtbot.mouseClick(replace.delButton, Qt.LeftButton)
|
||||
assert replace.listBox.topLevelItemCount() == 3
|
||||
|
||||
# Check Project
|
||||
projSettings._doSave()
|
||||
assert theProject.data.autoReplace == {
|
||||
assert project.data.autoReplace == {
|
||||
"A": "B", "C": "D", "This": "With This Stuff"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user