Update the design on the Project Settings dialog
This commit is contained in:
@@ -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,102 @@ 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, QLabel,
|
||||
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, 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
|
||||
wW = CONFIG.pxInt(500)
|
||||
wH = CONFIG.pxInt(400)
|
||||
options = SHARED.project.options
|
||||
|
||||
self.setMinimumWidth(wW)
|
||||
self.setMinimumHeight(wH)
|
||||
self.setMinimumSize(wW, wH)
|
||||
self.resize(
|
||||
CONFIG.pxInt(pOptions.getInt("GuiProjectSettings", "winWidth", wW)),
|
||||
CONFIG.pxInt(pOptions.getInt("GuiProjectSettings", "winHeight", wH))
|
||||
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winWidth", wW)),
|
||||
CONFIG.pxInt(options.getInt("GuiProjectSettings", "winHeight", wH))
|
||||
)
|
||||
|
||||
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=1.25
|
||||
)
|
||||
self.titleLabel.setIndent(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
|
||||
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 +131,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 +146,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.colChanged:
|
||||
newList, delList = self.statusPage.getNewList()
|
||||
project.setStatusColours(newList, delList)
|
||||
rebuildTrees = True
|
||||
|
||||
if self.tabImport.colChanged:
|
||||
newList, delList = self.tabImport.getNewList()
|
||||
if self.importPage.colChanged:
|
||||
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.arChanged:
|
||||
newList = self.replacePage.getNewList()
|
||||
project.data.setAutoReplace(newList)
|
||||
|
||||
self.newProjectSettingsReady.emit()
|
||||
self.newProjectSettingsReady.emit(rebuildTrees)
|
||||
qApp.processEvents()
|
||||
self.close()
|
||||
|
||||
@@ -157,134 +201,104 @@ 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)
|
||||
|
||||
self.mainForm.addGroupLabel(self.tr("Project Settings"))
|
||||
|
||||
xW = CONFIG.pxInt(250)
|
||||
pData = SHARED.project.data
|
||||
data = SHARED.project.data
|
||||
self.setHelpTextStyle(SHARED.theme.helpText)
|
||||
self.setRowIndent(0)
|
||||
|
||||
self.editName = QLineEdit()
|
||||
self.editName.setMaxLength(200)
|
||||
self.editName.setMaximumWidth(xW)
|
||||
self.editName.setText(pData.name)
|
||||
self.mainForm.addRow(
|
||||
# Project Name
|
||||
self.projName = QLineEdit(self)
|
||||
self.projName.setMaxLength(200)
|
||||
self.projName.setMaximumWidth(xW)
|
||||
self.projName.setText(data.name)
|
||||
self.addRow(
|
||||
self.tr("Project name"),
|
||||
self.editName,
|
||||
self.tr("Should be set only once.")
|
||||
self.projName,
|
||||
self.tr("Changing this will affect the backup path.")
|
||||
)
|
||||
|
||||
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(
|
||||
# Project Author
|
||||
self.projAuthor = QLineEdit(self)
|
||||
self.projAuthor.setMaxLength(200)
|
||||
self.projAuthor.setMaximumWidth(xW)
|
||||
self.projAuthor.setText(data.author)
|
||||
self.addRow(
|
||||
self.tr("Author(s)"),
|
||||
self.editAuthor,
|
||||
self.tr("Change whenever you want!")
|
||||
self.projAuthor,
|
||||
self.tr("Only used when building the manuscript.")
|
||||
)
|
||||
|
||||
# Project Language
|
||||
self.projLang = QComboBox(self)
|
||||
self.projLang.setMaximumWidth(xW)
|
||||
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
||||
self.projLang.addItem(language, tag)
|
||||
self.mainForm.addRow(
|
||||
self.addRow(
|
||||
self.tr("Project language"),
|
||||
self.projLang,
|
||||
self.tr("Used when building the manuscript.")
|
||||
self.tr("Only used when building the manuscript.")
|
||||
)
|
||||
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.addItem(self.tr("Default"), "None")
|
||||
if CONFIG.hasEnchant:
|
||||
for tag, language in SHARED.spelling.listDictionaries():
|
||||
self.spellLang.addItem(language, tag)
|
||||
self.mainForm.addRow(
|
||||
self.addRow(
|
||||
self.tr("Spell check language"),
|
||||
self.spellLang,
|
||||
self.tr("Overrides main preferences.")
|
||||
)
|
||||
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.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(QWidget):
|
||||
|
||||
COL_LABEL = 0
|
||||
COL_USAGE = 1
|
||||
@@ -318,7 +332,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
# The List
|
||||
# ========
|
||||
|
||||
self.listBox = QTreeWidget()
|
||||
self.listBox = QTreeWidget(self)
|
||||
self.listBox.setHeaderLabels([
|
||||
self.tr("Label"), self.tr("Usage"),
|
||||
])
|
||||
@@ -332,16 +346,16 @@ class GuiProjectEditStatus(QWidget):
|
||||
# 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
|
||||
@@ -389,6 +403,7 @@ class GuiProjectEditStatus(QWidget):
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addWidget(QLabel("<b>%s</b>" % pageLabel))
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
@@ -410,6 +425,10 @@ class GuiProjectEditStatus(QWidget):
|
||||
|
||||
return [], []
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
"""Return the size of the header column."""
|
||||
return self.listBox.columnWidth(0)
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
@@ -553,10 +572,10 @@ class GuiProjectEditStatus(QWidget):
|
||||
else:
|
||||
return self.tr("Used by {0} items").format(nUse)
|
||||
|
||||
# END Class GuiProjectEditStatus
|
||||
# END Class _StatusPage
|
||||
|
||||
|
||||
class GuiProjectEditReplace(QWidget):
|
||||
class _ReplacePage(QWidget):
|
||||
|
||||
COL_KEY = 0
|
||||
COL_REPL = 1
|
||||
@@ -638,6 +657,7 @@ class GuiProjectEditReplace(QWidget):
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addWidget(QLabel("<b>%s</b>" % pageLabel))
|
||||
self.outerBox.addLayout(self.innerBox)
|
||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
|
||||
@@ -655,6 +675,10 @@ class GuiProjectEditReplace(QWidget):
|
||||
new[aKey] = aVal
|
||||
return new
|
||||
|
||||
def columnWidth(self) -> int:
|
||||
"""Return the size of the header column."""
|
||||
return self.listBox.columnWidth(0)
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
@@ -725,4 +749,4 @@ class GuiProjectEditReplace(QWidget):
|
||||
result += c
|
||||
return result
|
||||
|
||||
# END Class GuiProjectEditReplace
|
||||
# END Class _ReplacePage
|
||||
Reference in New Issue
Block a user