Create a scrollable config layout and start adding it to preferences
This commit is contained in:
@@ -28,20 +28,23 @@ import logging
|
||||
from PyQt5.QtGui import QCloseEvent, QFont
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||
QAbstractButton, QDialog, QHBoxLayout, QLabel, QScrollArea, QVBoxLayout, QWidget, QComboBox, QSpinBox, QPushButton, QDialogButtonBox,
|
||||
QLineEdit, QFileDialog, QFontDialog, QDoubleSpinBox, qApp
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.dialogs.quotes import GuiQuoteSelect
|
||||
from novelwriter.extensions.pagedsidebar import NPagedSideBar
|
||||
from novelwriter.extensions.switch import NSwitch
|
||||
from novelwriter.extensions.pageddialog import NPagedDialog
|
||||
from novelwriter.extensions.configlayout import NConfigLayout
|
||||
from novelwriter.extensions.configlayout import NConfigLayout, NScrollableForm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GuiPreferences(NPagedDialog):
|
||||
class GuiPreferences(QDialog):
|
||||
|
||||
NAV_APPEARANCE = 0
|
||||
|
||||
newPreferencesReady = pyqtSignal(bool, bool, bool, bool)
|
||||
|
||||
@@ -50,32 +53,44 @@ class GuiPreferences(NPagedDialog):
|
||||
|
||||
logger.debug("Create: GuiPreferences")
|
||||
self.setObjectName("GuiPreferences")
|
||||
self.setWindowTitle(self.tr("Preferences"))
|
||||
|
||||
self.tabGeneral = GuiPreferencesGeneral(self)
|
||||
self.tabProjects = GuiPreferencesProjects(self)
|
||||
self.tabDocs = GuiPreferencesDocuments(self)
|
||||
self.tabEditor = GuiPreferencesEditor(self)
|
||||
self.tabSyntax = GuiPreferencesSyntax(self)
|
||||
self.tabAuto = GuiPreferencesAutomation(self)
|
||||
self.tabQuote = GuiPreferencesQuotes(self)
|
||||
|
||||
self.addTab(self.tabGeneral, self.tr("General"))
|
||||
self.addTab(self.tabProjects, self.tr("Projects"))
|
||||
self.addTab(self.tabDocs, self.tr("Documents"))
|
||||
self.addTab(self.tabEditor, self.tr("Editor"))
|
||||
self.addTab(self.tabSyntax, self.tr("Highlighting"))
|
||||
self.addTab(self.tabAuto, self.tr("Automation"))
|
||||
self.addTab(self.tabQuote, self.tr("Quotes"))
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
|
||||
self.buttonBox.accepted.connect(self._doSave)
|
||||
self.buttonBox.rejected.connect(self.close)
|
||||
self.rejected.connect(self.close)
|
||||
self.addControls(self.buttonBox)
|
||||
self.setWindowTitle(CONFIG.appName)
|
||||
|
||||
mPx = CONFIG.pxInt(150)
|
||||
self.resize(*CONFIG.preferencesWinSize)
|
||||
|
||||
self.minWidth = CONFIG.pxInt(200)
|
||||
|
||||
# SideBar
|
||||
self.optSideBar = NPagedSideBar(self)
|
||||
self.optSideBar.setMinimumWidth(mPx)
|
||||
self.optSideBar.setMaximumWidth(mPx)
|
||||
self.optSideBar.setLabelColor(SHARED.theme.helpText)
|
||||
self.optSideBar.addLabel(self.tr("Settings"))
|
||||
|
||||
# Form
|
||||
self.mainForm = NScrollableForm(self)
|
||||
self.mainForm.setHelpTextStyle(SHARED.theme.helpText)
|
||||
|
||||
# Buttons
|
||||
self.buttonBox = QDialogButtonBox(
|
||||
QDialogButtonBox.Apply | QDialogButtonBox.Save | QDialogButtonBox.Close
|
||||
)
|
||||
self.buttonBox.clicked.connect(self._dialogButtonClicked)
|
||||
|
||||
# Assemble
|
||||
self.mainBox = QHBoxLayout()
|
||||
self.mainBox.addWidget(self.optSideBar)
|
||||
self.mainBox.addWidget(self.mainForm)
|
||||
self.mainBox.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.outerBox = QVBoxLayout()
|
||||
self.outerBox.addLayout(self.mainBox)
|
||||
self.outerBox.addWidget(self.buttonBox)
|
||||
self.outerBox.setSpacing(CONFIG.pxInt(12))
|
||||
|
||||
self.setLayout(self.outerBox)
|
||||
self.buildForm()
|
||||
|
||||
# Settings
|
||||
self._updateTheme = False
|
||||
self._updateSyntax = False
|
||||
@@ -90,6 +105,16 @@ class GuiPreferences(NPagedDialog):
|
||||
logger.debug("Delete: GuiPreferences")
|
||||
return
|
||||
|
||||
def buildForm(self) -> None:
|
||||
""""""
|
||||
title = self.tr("Appearance")
|
||||
self.optSideBar.addButton(title, self.NAV_APPEARANCE)
|
||||
self.mainForm.addGroupLabel(title, self.NAV_APPEARANCE)
|
||||
self._buildAppearance()
|
||||
|
||||
self.mainForm.finalise()
|
||||
return
|
||||
|
||||
##
|
||||
# Events
|
||||
##
|
||||
@@ -106,22 +131,34 @@ class GuiPreferences(NPagedDialog):
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot("QAbstractButton*")
|
||||
def _dialogButtonClicked(self, button: QAbstractButton) -> None:
|
||||
"""Handle button clicks from the dialog button box."""
|
||||
role = self.buttonBox.buttonRole(button)
|
||||
if role == QDialogButtonBox.ApplyRole:
|
||||
pass
|
||||
elif role == QDialogButtonBox.AcceptRole:
|
||||
self.close()
|
||||
elif role == QDialogButtonBox.RejectRole:
|
||||
self.close()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _doSave(self) -> None:
|
||||
"""Trigger save functions in the tabs and emit ready signal."""
|
||||
self.tabGeneral.saveValues()
|
||||
self.tabProjects.saveValues()
|
||||
self.tabDocs.saveValues()
|
||||
self.tabEditor.saveValues()
|
||||
self.tabSyntax.saveValues()
|
||||
self.tabAuto.saveValues()
|
||||
self.tabQuote.saveValues()
|
||||
# self.tabGeneral.saveValues()
|
||||
# self.tabProjects.saveValues()
|
||||
# self.tabDocs.saveValues()
|
||||
# self.tabEditor.saveValues()
|
||||
# self.tabSyntax.saveValues()
|
||||
# self.tabAuto.saveValues()
|
||||
# self.tabQuote.saveValues()
|
||||
|
||||
CONFIG.saveConfig()
|
||||
self.newPreferencesReady.emit(
|
||||
self._needsRestart, self._refreshTree, self._updateTheme, self._updateSyntax
|
||||
)
|
||||
qApp.processEvents()
|
||||
# CONFIG.saveConfig()
|
||||
# self.newPreferencesReady.emit(
|
||||
# self._needsRestart, self._refreshTree, self._updateTheme, self._updateSyntax
|
||||
# )
|
||||
# qApp.processEvents()
|
||||
self.close()
|
||||
|
||||
return
|
||||
@@ -135,6 +172,99 @@ class GuiPreferences(NPagedDialog):
|
||||
CONFIG.setPreferencesWinSize(self.width(), self.height())
|
||||
return
|
||||
|
||||
def _buildAppearance(self) -> None:
|
||||
"""Build the appearance section."""
|
||||
# Select Locale
|
||||
self.guiLocale = QComboBox(self)
|
||||
self.guiLocale.setMinimumWidth(self.minWidth)
|
||||
theLangs = CONFIG.listLanguages(CONFIG.LANG_NW)
|
||||
for lang, langName in theLangs:
|
||||
self.guiLocale.addItem(langName, lang)
|
||||
langIdx = self.guiLocale.findData(CONFIG.guiLocale)
|
||||
if langIdx < 0:
|
||||
langIdx = self.guiLocale.findData("en_GB")
|
||||
if langIdx != -1:
|
||||
self.guiLocale.setCurrentIndex(langIdx)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Main GUI language"),
|
||||
self.guiLocale,
|
||||
self.tr("Requires restart to take effect.")
|
||||
)
|
||||
|
||||
# Select Theme
|
||||
self.guiTheme = QComboBox(self)
|
||||
self.guiTheme.setMinimumWidth(self.minWidth)
|
||||
self.theThemes = SHARED.theme.listThemes()
|
||||
for themeDir, themeName in self.theThemes:
|
||||
self.guiTheme.addItem(themeName, themeDir)
|
||||
themeIdx = self.guiTheme.findData(CONFIG.guiTheme)
|
||||
if themeIdx != -1:
|
||||
self.guiTheme.setCurrentIndex(themeIdx)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Main GUI theme"),
|
||||
self.guiTheme,
|
||||
self.tr("General colour theme and icons.")
|
||||
)
|
||||
|
||||
# Editor Theme
|
||||
self.guiSyntax = QComboBox(self)
|
||||
self.guiSyntax.setMinimumWidth(self.minWidth)
|
||||
self.theSyntaxes = SHARED.theme.listSyntax()
|
||||
for syntaxFile, syntaxName in self.theSyntaxes:
|
||||
self.guiSyntax.addItem(syntaxName, syntaxFile)
|
||||
syntaxIdx = self.guiSyntax.findData(CONFIG.guiSyntax)
|
||||
if syntaxIdx != -1:
|
||||
self.guiSyntax.setCurrentIndex(syntaxIdx)
|
||||
|
||||
self.mainForm.addRow(
|
||||
self.tr("Editor theme"),
|
||||
self.guiSyntax,
|
||||
self.tr("Colour theme for the editor and viewer.")
|
||||
)
|
||||
|
||||
# Font Family
|
||||
self.guiFont = QLineEdit(self)
|
||||
self.guiFont.setReadOnly(True)
|
||||
self.guiFont.setFixedWidth(CONFIG.pxInt(162))
|
||||
self.guiFont.setText(CONFIG.guiFont)
|
||||
self.fontButton = QPushButton("...", self)
|
||||
self.fontButton.setMaximumWidth(int(2.5*SHARED.theme.getTextWidth("...")))
|
||||
# self.fontButton.clicked.connect(self._selectFont)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Font family"),
|
||||
self.guiFont,
|
||||
self.tr("Requires restart to take effect."),
|
||||
button=self.fontButton
|
||||
)
|
||||
|
||||
# Font Size
|
||||
self.guiFontSize = QSpinBox(self)
|
||||
self.guiFontSize.setMinimum(8)
|
||||
self.guiFontSize.setMaximum(60)
|
||||
self.guiFontSize.setSingleStep(1)
|
||||
self.guiFontSize.setValue(CONFIG.guiFontSize)
|
||||
self.mainForm.addRow(
|
||||
self.tr("Font size"),
|
||||
self.guiFontSize,
|
||||
self.tr("Requires restart to take effect."),
|
||||
unit=self.tr("pt")
|
||||
)
|
||||
|
||||
return
|
||||
@pyqtSlot()
|
||||
def _selectFont(self) -> None:
|
||||
"""Open the QFontDialog and set a font for the font style."""
|
||||
currFont = QFont()
|
||||
currFont.setFamily(CONFIG.guiFont)
|
||||
currFont.setPointSize(CONFIG.guiFontSize)
|
||||
theFont, theStatus = QFontDialog.getFont(currFont, self)
|
||||
if theStatus:
|
||||
self.guiFont.setText(theFont.family())
|
||||
self.guiFontSize.setValue(theFont.pointSize())
|
||||
return
|
||||
|
||||
# END Class GuiPreferences
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ from __future__ import annotations
|
||||
from PyQt5.QtGui import QColor, QPalette
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (
|
||||
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QSizePolicy,
|
||||
QAbstractButton, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QScrollArea, QSizePolicy,
|
||||
QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
@@ -37,6 +37,99 @@ RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
|
||||
LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
|
||||
|
||||
|
||||
class NScrollableForm(QScrollArea):
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
self._helpCol = QColor(0, 0, 0)
|
||||
self._fontScale = FONT_SCALE
|
||||
self._sections: dict[int, QLabel] = {}
|
||||
self._editable: dict[str, NHelpLabel] = {}
|
||||
|
||||
self._layout = QVBoxLayout()
|
||||
self._layout.setSpacing(CONFIG.pxInt(12))
|
||||
|
||||
self._widget = QWidget(self)
|
||||
self._widget.setLayout(self._layout)
|
||||
|
||||
self.setWidget(self._widget)
|
||||
self.setWidgetResizable(True)
|
||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
|
||||
|
||||
return
|
||||
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple, scale: float = FONT_SCALE) -> None:
|
||||
"""Set the text color for the help text."""
|
||||
self._helpCol = color if isinstance(color, QColor) else QColor(*color)
|
||||
self._fontScale = scale
|
||||
return
|
||||
|
||||
def setHelpText(self, key: str, text: str) -> None:
|
||||
"""Set the text for the help label."""
|
||||
if qHelp := self._editable.get(key):
|
||||
qHelp.setText(text)
|
||||
return
|
||||
|
||||
def finalise(self) -> None:
|
||||
"""Finalise the layout when the form is built."""
|
||||
self._layout.addStretch(1)
|
||||
return
|
||||
|
||||
def scrollToSection(self, identifier: int, offset: int = 50) -> None:
|
||||
"""Scroll to the requested section identifier."""
|
||||
if identifier in self._sections:
|
||||
yPos = self._sections[identifier].pos().y() - CONFIG.pxInt(8)
|
||||
self.verticalScrollBar().setValue(yPos)
|
||||
return
|
||||
|
||||
def addGroupLabel(self, label: str, identifier: int) -> None:
|
||||
"""Add a text label to separate groups of settings."""
|
||||
hM = CONFIG.pxInt(4)
|
||||
qLabel = QLabel(f"<b>{label}</b>", self)
|
||||
qLabel.setContentsMargins(0, hM, 0, hM)
|
||||
self._layout.addWidget(qLabel)
|
||||
self._sections[identifier] = qLabel
|
||||
return
|
||||
|
||||
def addRow(self, label: str, widget: QWidget, helpText: str = "", unit: str | None = None,
|
||||
button: QWidget | None = None, editable: str | None = None) -> None:
|
||||
"""Add a label and a widget as a new row of the grid."""
|
||||
row = QHBoxLayout()
|
||||
|
||||
wSp = CONFIG.pxInt(8)
|
||||
qLabel = QLabel(label, self)
|
||||
qLabel.setIndent(wSp)
|
||||
qLabel.setBuddy(widget)
|
||||
|
||||
if helpText:
|
||||
qHelp = NHelpLabel(str(helpText), self._helpCol, self._fontScale)
|
||||
qHelp.setIndent(wSp)
|
||||
labelBox = QVBoxLayout()
|
||||
labelBox.addWidget(qLabel)
|
||||
labelBox.addWidget(qHelp)
|
||||
labelBox.setSpacing(0)
|
||||
labelBox.addStretch(1)
|
||||
row.addLayout(labelBox)
|
||||
if editable:
|
||||
self._editable[editable] = qHelp
|
||||
else:
|
||||
row.addWidget(qLabel)
|
||||
|
||||
row.addWidget(widget)
|
||||
|
||||
if isinstance(unit, str):
|
||||
row.addWidget(QLabel(unit, self))
|
||||
elif isinstance(button, QAbstractButton):
|
||||
row.addWidget(button)
|
||||
|
||||
self._layout.addLayout(row)
|
||||
|
||||
return
|
||||
|
||||
# END Class NScrollableForm
|
||||
|
||||
|
||||
class NConfigLayout(QGridLayout):
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -58,11 +151,10 @@ class NConfigLayout(QGridLayout):
|
||||
# Getters and Setters
|
||||
##
|
||||
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple,
|
||||
fontScale: float = FONT_SCALE) -> None:
|
||||
def setHelpTextStyle(self, color: QColor | list | tuple, scale: float = FONT_SCALE) -> None:
|
||||
"""Set the text color for the help text."""
|
||||
self._helpCol = color if isinstance(color, QColor) else QColor(*color)
|
||||
self._fontScale = fontScale
|
||||
self._fontScale = scale
|
||||
return
|
||||
|
||||
def setHelpText(self, row: int, text: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user