Update buttons and manuscript build (#2553)
This commit is contained in:
@@ -56,7 +56,7 @@ VALID_MAP: dict[str, set[str]] = {
|
|||||||
"GuiWordList": {"winWidth", "winHeight"},
|
"GuiWordList": {"winWidth", "winHeight"},
|
||||||
"GuiNovelView": {"lastCol", "lastColSize"},
|
"GuiNovelView": {"lastCol", "lastColSize"},
|
||||||
"GuiBuildSettings": {
|
"GuiBuildSettings": {
|
||||||
"winWidth", "winHeight", "treeWidth", "filterWidth",
|
"winWidth", "winHeight", "treeWidth", "filterWidth", "autoPreview",
|
||||||
},
|
},
|
||||||
"GuiManuscript": {
|
"GuiManuscript": {
|
||||||
"winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
|
"winWidth", "winHeight", "optsWidth", "viewWidth", "listHeight",
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ class GuiPreferences(NDialog):
|
|||||||
self.mainForm.addGroupLabel(title, section)
|
self.mainForm.addGroupLabel(title, section)
|
||||||
|
|
||||||
# Display Language
|
# Display Language
|
||||||
self.guiLocale = NComboBox(self, scrollable=True)
|
self.guiLocale = NComboBox(self)
|
||||||
self.guiLocale.setMinimumWidth(200)
|
self.guiLocale.setMinimumWidth(200)
|
||||||
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
|
for lang, name in CONFIG.listLanguages(CONFIG.LANG_NW):
|
||||||
self.guiLocale.addItem(name, lang)
|
self.guiLocale.addItem(name, lang)
|
||||||
@@ -170,9 +170,9 @@ class GuiPreferences(NDialog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Colour Theme
|
# Colour Theme
|
||||||
self.lightTheme = NComboBox(self, scrollable=True)
|
self.lightTheme = NComboBox(self)
|
||||||
self.lightTheme.setMinimumWidth(200)
|
self.lightTheme.setMinimumWidth(200)
|
||||||
self.darkTheme = NComboBox(self, scrollable=True)
|
self.darkTheme = NComboBox(self)
|
||||||
self.darkTheme.setMinimumWidth(200)
|
self.darkTheme.setMinimumWidth(200)
|
||||||
for key, theme in SHARED.theme.colourThemes.items():
|
for key, theme in SHARED.theme.colourThemes.items():
|
||||||
if theme.dark:
|
if theme.dark:
|
||||||
@@ -193,7 +193,7 @@ class GuiPreferences(NDialog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Icon Theme
|
# Icon Theme
|
||||||
self.iconTheme = NComboBox(self, scrollable=True)
|
self.iconTheme = NComboBox(self)
|
||||||
self.iconTheme.setMinimumWidth(200)
|
self.iconTheme.setMinimumWidth(200)
|
||||||
for key, theme in SHARED.theme.iconCache.iconThemes.items():
|
for key, theme in SHARED.theme.iconCache.iconThemes.items():
|
||||||
self.iconTheme.addItem(theme.name, key)
|
self.iconTheme.addItem(theme.name, key)
|
||||||
@@ -513,7 +513,7 @@ class GuiPreferences(NDialog):
|
|||||||
self.mainForm.addGroupLabel(title, section)
|
self.mainForm.addGroupLabel(title, section)
|
||||||
|
|
||||||
# Spell Checking
|
# Spell Checking
|
||||||
self.spellLanguage = NComboBox(self, scrollable=True)
|
self.spellLanguage = NComboBox(self)
|
||||||
self.spellLanguage.setMinimumWidth(200)
|
self.spellLanguage.setMinimumWidth(200)
|
||||||
|
|
||||||
if CONFIG.hasEnchant:
|
if CONFIG.hasEnchant:
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ class _SettingsPage(NScrollableForm):
|
|||||||
|
|
||||||
# Project Language
|
# Project Language
|
||||||
projLang = data.language or CONFIG.guiLocale
|
projLang = data.language or CONFIG.guiLocale
|
||||||
self.projLang = NComboBox(self, scrollable=True)
|
self.projLang = NComboBox(self)
|
||||||
self.projLang.setMinimumWidth(200)
|
self.projLang.setMinimumWidth(200)
|
||||||
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
|
||||||
self.projLang.addItem(language, tag)
|
self.projLang.addItem(language, tag)
|
||||||
@@ -269,7 +269,7 @@ class _SettingsPage(NScrollableForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Spell Check Language
|
# Spell Check Language
|
||||||
self.spellLang = NComboBox(self, scrollable=True)
|
self.spellLang = NComboBox(self)
|
||||||
self.spellLang.setMinimumWidth(200)
|
self.spellLang.setMinimumWidth(200)
|
||||||
self.spellLang.addItem(self.tr("Default"), "None")
|
self.spellLang.addItem(self.tr("Default"), "None")
|
||||||
if CONFIG.hasEnchant:
|
if CONFIG.hasEnchant:
|
||||||
|
|||||||
@@ -125,16 +125,11 @@ class NComboBox(QComboBox):
|
|||||||
window of many widgets.
|
window of many widgets.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, parent: QWidget | None = None, maxItems: int = 15) -> None:
|
||||||
self, parent: QWidget | None = None, maxItems: int = 15, scrollable: bool = False
|
|
||||||
) -> None:
|
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||||
self.setMaxVisibleItems(maxItems)
|
self.setMaxVisibleItems(maxItems)
|
||||||
if scrollable:
|
self.updateStyle()
|
||||||
# The style sheet disables Fusion style pop-up mode on some
|
|
||||||
# platforms and allows for scrolling of long lists of items
|
|
||||||
self.setStyleSheet("QComboBox {combobox-popup: 0;}")
|
|
||||||
|
|
||||||
def wheelEvent(self, event: QWheelEvent) -> None:
|
def wheelEvent(self, event: QWheelEvent) -> None:
|
||||||
"""Only capture the mouse wheel if the widget has focus."""
|
"""Only capture the mouse wheel if the widget has focus."""
|
||||||
@@ -148,6 +143,12 @@ class NComboBox(QComboBox):
|
|||||||
idx = self.findData(data)
|
idx = self.findData(data)
|
||||||
self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
|
self.setCurrentIndex(self.findData(default) if idx < 0 else idx)
|
||||||
|
|
||||||
|
def updateStyle(self) -> None:
|
||||||
|
"""Update the style sheet."""
|
||||||
|
# The style sheet disables Fusion style pop-up mode on some
|
||||||
|
# platforms and allows for scrolling of long lists of items
|
||||||
|
self.setStyleSheet("QComboBox {combobox-popup: 0;}")
|
||||||
|
|
||||||
|
|
||||||
class NSpinBox(QSpinBox):
|
class NSpinBox(QSpinBox):
|
||||||
"""Custom: Modified QSpinBox.
|
"""Custom: Modified QSpinBox.
|
||||||
|
|||||||
@@ -92,10 +92,10 @@ class GuiManuscript(NToolDialog):
|
|||||||
iPx = SHARED.theme.baseIconHeight
|
iPx = SHARED.theme.baseIconHeight
|
||||||
iSz = SHARED.theme.baseIconSize
|
iSz = SHARED.theme.baseIconSize
|
||||||
|
|
||||||
pOptions = SHARED.project.options
|
options = SHARED.project.options
|
||||||
self.resize(
|
self.resize(
|
||||||
pOptions.getInt("GuiManuscript", "winWidth", 900),
|
options.getInt("GuiManuscript", "winWidth", 900),
|
||||||
pOptions.getInt("GuiManuscript", "winHeight", 600),
|
options.getInt("GuiManuscript", "winHeight", 600),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build Controls
|
# Build Controls
|
||||||
@@ -142,7 +142,7 @@ class GuiManuscript(NToolDialog):
|
|||||||
# ============
|
# ============
|
||||||
|
|
||||||
self.buildDetails = _DetailsWidget(self)
|
self.buildDetails = _DetailsWidget(self)
|
||||||
self.buildDetails.setColumnWidth(pOptions.getInt("GuiManuscript", "detailsWidth", 100))
|
self.buildDetails.setColumnWidth(options.getInt("GuiManuscript", "detailsWidth", 100))
|
||||||
|
|
||||||
self.buildOutline = _OutlineWidget(self)
|
self.buildOutline = _OutlineWidget(self)
|
||||||
|
|
||||||
@@ -154,8 +154,8 @@ class GuiManuscript(NToolDialog):
|
|||||||
self.buildSplit.addWidget(self.buildList)
|
self.buildSplit.addWidget(self.buildList)
|
||||||
self.buildSplit.addWidget(self.detailsTabs)
|
self.buildSplit.addWidget(self.detailsTabs)
|
||||||
self.buildSplit.setSizes([
|
self.buildSplit.setSizes([
|
||||||
pOptions.getInt("GuiManuscript", "listHeight", 50),
|
options.getInt("GuiManuscript", "listHeight", 50),
|
||||||
pOptions.getInt("GuiManuscript", "detailsHeight", 50),
|
options.getInt("GuiManuscript", "detailsHeight", 50),
|
||||||
])
|
])
|
||||||
|
|
||||||
# Process Controls
|
# Process Controls
|
||||||
@@ -183,7 +183,7 @@ class GuiManuscript(NToolDialog):
|
|||||||
# ===============
|
# ===============
|
||||||
|
|
||||||
self.swtNewPage = NSwitch(self, height=iPx)
|
self.swtNewPage = NSwitch(self, height=iPx)
|
||||||
self.swtNewPage.setChecked(pOptions.getBool("GuiManuscript", "showNewPage", True))
|
self.swtNewPage.setChecked(options.getBool("GuiManuscript", "showNewPage", True))
|
||||||
self.swtNewPage.clicked.connect(self._generatePreview)
|
self.swtNewPage.clicked.connect(self._generatePreview)
|
||||||
|
|
||||||
self.lblNewPage = QLabel(self.tr("Show Page Breaks"), self)
|
self.lblNewPage = QLabel(self.tr("Show Page Breaks"), self)
|
||||||
@@ -226,8 +226,8 @@ class GuiManuscript(NToolDialog):
|
|||||||
self.mainSplit.setStretchFactor(0, 0)
|
self.mainSplit.setStretchFactor(0, 0)
|
||||||
self.mainSplit.setStretchFactor(1, 1)
|
self.mainSplit.setStretchFactor(1, 1)
|
||||||
self.mainSplit.setSizes([
|
self.mainSplit.setSizes([
|
||||||
pOptions.getInt("GuiManuscript", "optsWidth", 225),
|
options.getInt("GuiManuscript", "optsWidth", 225),
|
||||||
pOptions.getInt("GuiManuscript", "viewWidth", 675),
|
options.getInt("GuiManuscript", "viewWidth", 675),
|
||||||
])
|
])
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
@@ -273,7 +273,7 @@ class GuiManuscript(NToolDialog):
|
|||||||
|
|
||||||
self.tbAdd.setThemeIcon("add", "add")
|
self.tbAdd.setThemeIcon("add", "add")
|
||||||
self.tbDel.setThemeIcon("remove", "remove")
|
self.tbDel.setThemeIcon("remove", "remove")
|
||||||
self.tbCopy.setThemeIcon("copy", "accept")
|
self.tbCopy.setThemeIcon("copy", "action")
|
||||||
self.tbEdit.setThemeIcon("edit", "change")
|
self.tbEdit.setThemeIcon("edit", "change")
|
||||||
|
|
||||||
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
buttonStyle = SHARED.theme.getStyleSheet(STYLES_MIN_TOOLBUTTON)
|
||||||
@@ -352,13 +352,16 @@ class GuiManuscript(NToolDialog):
|
|||||||
self._builds.removeBuild(build.buildID)
|
self._builds.removeBuild(build.buildID)
|
||||||
self._updateBuildsList()
|
self._updateBuildsList()
|
||||||
|
|
||||||
@pyqtSlot(BuildSettings)
|
@pyqtSlot(BuildSettings, bool)
|
||||||
def _processNewSettings(self, build: BuildSettings) -> None:
|
def _processNewSettings(self, build: BuildSettings, refreshPreview: bool) -> None:
|
||||||
"""Process new build settings from the settings dialog."""
|
"""Process new build settings from the settings dialog."""
|
||||||
self._builds.setBuild(build)
|
self._builds.setBuild(build)
|
||||||
self._updateBuildItem(build)
|
self._updateBuildItem(build)
|
||||||
if (current := self.buildList.currentItem()) and current.data(self.D_KEY) == build.buildID:
|
if refreshPreview:
|
||||||
self._updateBuildDetails(current, current)
|
self.buildList.setCurrentItem(self._buildMap[build.buildID])
|
||||||
|
self._generatePreview()
|
||||||
|
elif (item := self.buildList.currentItem()) and item.data(self.D_KEY) == build.buildID:
|
||||||
|
self._updateBuildDetails(item, item)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def _generatePreview(self) -> None:
|
def _generatePreview(self) -> None:
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
OPT_HEADINGS = 2
|
OPT_HEADINGS = 2
|
||||||
OPT_FORMATTING = 10
|
OPT_FORMATTING = 10
|
||||||
|
|
||||||
newSettingsReady = pyqtSignal(BuildSettings)
|
newSettingsReady = pyqtSignal(BuildSettings, bool)
|
||||||
|
|
||||||
def __init__(self, parent: GuiMain, build: BuildSettings) -> None:
|
def __init__(self, parent: GuiMain, build: BuildSettings) -> None:
|
||||||
super().__init__(parent=parent)
|
super().__init__(parent=parent)
|
||||||
@@ -86,6 +86,7 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
self.setWindowTitle(self.tr("Manuscript Build Settings"))
|
self.setWindowTitle(self.tr("Manuscript Build Settings"))
|
||||||
self.setMinimumSize(700, 400)
|
self.setMinimumSize(700, 400)
|
||||||
|
|
||||||
|
iPx = SHARED.theme.baseIconHeight
|
||||||
options = SHARED.project.options
|
options = SHARED.project.options
|
||||||
self.resize(
|
self.resize(
|
||||||
options.getInt("GuiBuildSettings", "winWidth", 750),
|
options.getInt("GuiBuildSettings", "winWidth", 750),
|
||||||
@@ -124,6 +125,13 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
self.toolStack.addWidget(self.optTabHeadings)
|
self.toolStack.addWidget(self.optTabHeadings)
|
||||||
self.toolStack.addWidget(self.optTabFormatting)
|
self.toolStack.addWidget(self.optTabFormatting)
|
||||||
|
|
||||||
|
# Preview
|
||||||
|
self.swtAutoPreview = NSwitch(self, height=iPx)
|
||||||
|
self.swtAutoPreview.setChecked(options.getBool("GuiBuildSettings", "autoPreview", True))
|
||||||
|
|
||||||
|
self.lblAutoPreview = QLabel(self.tr("Auro-Update Preview"), self)
|
||||||
|
self.lblAutoPreview.setBuddy(self.swtAutoPreview)
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
self.btnApply = SHARED.theme.getStandardButton(nwStandardButton.APPLY, self)
|
self.btnApply = SHARED.theme.getStandardButton(nwStandardButton.APPLY, self)
|
||||||
self.btnSave = SHARED.theme.getStandardButton(nwStandardButton.SAVE, self)
|
self.btnSave = SHARED.theme.getStandardButton(nwStandardButton.SAVE, self)
|
||||||
@@ -147,10 +155,17 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
self.mainBox.addWidget(self.toolStack)
|
self.mainBox.addWidget(self.toolStack)
|
||||||
self.mainBox.setContentsMargins(0, 0, 0, 0)
|
self.mainBox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
|
self.bottomBox = QHBoxLayout()
|
||||||
|
self.bottomBox.addWidget(self.lblAutoPreview, 0)
|
||||||
|
self.bottomBox.addWidget(self.swtAutoPreview, 0)
|
||||||
|
self.bottomBox.addSpacing(8)
|
||||||
|
self.bottomBox.addWidget(self.btnBox, 1)
|
||||||
|
self.bottomBox.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addLayout(self.topBox)
|
self.outerBox.addLayout(self.topBox)
|
||||||
self.outerBox.addLayout(self.mainBox)
|
self.outerBox.addLayout(self.mainBox)
|
||||||
self.outerBox.addWidget(self.btnBox)
|
self.outerBox.addLayout(self.bottomBox)
|
||||||
self.outerBox.setSpacing(12)
|
self.outerBox.setSpacing(12)
|
||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
@@ -259,12 +274,13 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
"""Save the various user settings."""
|
"""Save the various user settings."""
|
||||||
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
|
treeWidth, filterWidth = self.optTabSelect.mainSplitSizes()
|
||||||
logger.debug("Saving State: GuiBuildSettings")
|
logger.debug("Saving State: GuiBuildSettings")
|
||||||
pOptions = SHARED.project.options
|
options = SHARED.project.options
|
||||||
pOptions.setValue("GuiBuildSettings", "winWidth", self.width())
|
options.setValue("GuiBuildSettings", "winWidth", self.width())
|
||||||
pOptions.setValue("GuiBuildSettings", "winHeight", self.height())
|
options.setValue("GuiBuildSettings", "winHeight", self.height())
|
||||||
pOptions.setValue("GuiBuildSettings", "treeWidth", treeWidth)
|
options.setValue("GuiBuildSettings", "treeWidth", treeWidth)
|
||||||
pOptions.setValue("GuiBuildSettings", "filterWidth", filterWidth)
|
options.setValue("GuiBuildSettings", "filterWidth", filterWidth)
|
||||||
pOptions.saveSettings()
|
options.setValue("GuiBuildSettings", "autoPreview", self.swtAutoPreview.isChecked())
|
||||||
|
options.saveSettings()
|
||||||
|
|
||||||
def _applyChanges(self) -> None:
|
def _applyChanges(self) -> None:
|
||||||
"""Apply all settings changes to the build object."""
|
"""Apply all settings changes to the build object."""
|
||||||
@@ -274,7 +290,7 @@ class GuiBuildSettings(NToolDialog):
|
|||||||
|
|
||||||
def _emitBuildData(self) -> None:
|
def _emitBuildData(self) -> None:
|
||||||
"""Assemble the build data and emit the signal."""
|
"""Assemble the build data and emit the signal."""
|
||||||
self.newSettingsReady.emit(self._build)
|
self.newSettingsReady.emit(self._build, self.swtAutoPreview.isChecked())
|
||||||
self._build.resetChangedState()
|
self._build.resetChangedState()
|
||||||
|
|
||||||
|
|
||||||
@@ -1329,6 +1345,9 @@ class _FormattingTab(NScrollableForm):
|
|||||||
self.pixH.setPixmap(SHARED.theme.getPixmap("fit_height", (iPx, iPx)))
|
self.pixH.setPixmap(SHARED.theme.getPixmap("fit_height", (iPx, iPx)))
|
||||||
self.pixW.setPixmap(SHARED.theme.getPixmap("fit_width", (iPx, iPx)))
|
self.pixW.setPixmap(SHARED.theme.getPixmap("fit_width", (iPx, iPx)))
|
||||||
|
|
||||||
|
self.pageSize.updateStyle()
|
||||||
|
self.pageUnit.updateStyle()
|
||||||
|
|
||||||
def loadContent(self) -> None:
|
def loadContent(self) -> None:
|
||||||
"""Populate the widgets."""
|
"""Populate the widgets."""
|
||||||
# Text Content
|
# Text Content
|
||||||
|
|||||||
@@ -108,10 +108,11 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
|
|||||||
bSettings = SHARED.findTopLevelWidget(GuiBuildSettings)
|
bSettings = SHARED.findTopLevelWidget(GuiBuildSettings)
|
||||||
assert isinstance(bSettings, GuiBuildSettings)
|
assert isinstance(bSettings, GuiBuildSettings)
|
||||||
bSettings.editBuildName.setText("Test Build")
|
bSettings.editBuildName.setText("Test Build")
|
||||||
|
bSettings.swtAutoPreview.setChecked(False)
|
||||||
build = None
|
build = None
|
||||||
|
|
||||||
@pyqtSlot(BuildSettings)
|
@pyqtSlot(BuildSettings)
|
||||||
def _testNewSettingsReady(new: BuildSettings):
|
def _testNewSettingsReady(new: BuildSettings, refresh: bool):
|
||||||
nonlocal build
|
nonlocal build
|
||||||
build = new
|
build = new
|
||||||
|
|
||||||
@@ -151,6 +152,22 @@ def testToolManuscript_Builds(qtbot, nwGUI, projPath):
|
|||||||
assert new is not None
|
assert new is not None
|
||||||
assert new.name == "Test Build 2"
|
assert new.name == "Test Build 2"
|
||||||
|
|
||||||
|
# Processing first build with refresh should change selection
|
||||||
|
manus.docPreview._docTime = 0
|
||||||
|
manus._processNewSettings(build, True)
|
||||||
|
assert manus.docPreview._docTime > 0 # Refreshed
|
||||||
|
current = manus._getSelectedBuild()
|
||||||
|
assert current is not None
|
||||||
|
assert current.name == "Test Build"
|
||||||
|
|
||||||
|
# Processing new build without refresh should keep selection
|
||||||
|
manus.docPreview._docTime = 0
|
||||||
|
manus._processNewSettings(new, False)
|
||||||
|
assert manus.docPreview._docTime == 0 # No refresh
|
||||||
|
current = manus._getSelectedBuild()
|
||||||
|
assert current is not None
|
||||||
|
assert current.name == "Test Build"
|
||||||
|
|
||||||
# Trigger a theme update, which should propagate to settings
|
# Trigger a theme update, which should propagate to settings
|
||||||
nwGUI.refreshThemeColors()
|
nwGUI.refreshThemeColors()
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ def testToolBuildSettings_Init(qtbot, nwGUI, projPath, mockRnd):
|
|||||||
triggered = False
|
triggered = False
|
||||||
|
|
||||||
@pyqtSlot(BuildSettings)
|
@pyqtSlot(BuildSettings)
|
||||||
def _testNewSettingsReady(new: BuildSettings):
|
def _testNewSettingsReady(new: BuildSettings, refresh: bool):
|
||||||
nonlocal triggered
|
nonlocal triggered
|
||||||
assert new.buildID == build.buildID
|
assert new.buildID == build.buildID
|
||||||
triggered = True
|
triggered = True
|
||||||
|
|||||||
Reference in New Issue
Block a user