From eab8bb2351bc1f0d8df80e5c64f87d334b042419 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 12:48:14 +0100
Subject: [PATCH 1/7] Block welcome dialog open button when project list is not
visible
---
novelwriter/tools/welcome.py | 9 ++++++++-
tests/test_tools/test_tools_welcome.py | 12 ++++++++++--
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index d0085f38..ba1f9f9b 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -108,7 +108,7 @@ class GuiWelcome(QDialog):
# =======
self.btnBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel, self)
- self.btnBox.accepted.connect(self.tabOpen.openSelectedItem)
+ self.btnBox.accepted.connect(self._openSelectedItem)
self.btnBox.rejected.connect(self.close)
self.newButton = self.btnBox.addButton(self.tr("New Project"), QDialogButtonBox.ActionRole)
@@ -195,6 +195,13 @@ class GuiWelcome(QDialog):
self._openProjectPath(path)
return
+ @pyqtSlot()
+ def _openSelectedItem(self) -> None:
+ """Open the currently selected project item."""
+ if self.mainStack.currentWidget() == self.tabOpen:
+ self.tabOpen.openSelectedItem()
+ return
+
@pyqtSlot(Path)
def _openProjectPath(self, path: Path) -> None:
"""Emit a project open signal."""
diff --git a/tests/test_tools/test_tools_welcome.py b/tests/test_tools/test_tools_welcome.py
index 96ed814c..b3a566d7 100644
--- a/tests/test_tools/test_tools_welcome.py
+++ b/tests/test_tools/test_tools_welcome.py
@@ -27,7 +27,7 @@ from datetime import datetime
from pytestqt.qtbot import QtBot
from PyQt5.QtCore import QPoint, Qt
-from PyQt5.QtWidgets import QAction, QFileDialog, QMenu
+from PyQt5.QtWidgets import QAction, QDialogButtonBox, QFileDialog, QMenu
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemClass
@@ -103,7 +103,7 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
assert tabOpen.selectedPath.text() == "Path: /stuff/project_one"
- # Double Click item
+ # Double click item
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
with monkeypatch.context() as mp:
mp.setattr(welcome, "close", lambda *a: None)
@@ -111,6 +111,14 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
qtbot.mouseDClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
assert signal.args and signal.args[0] == Path("/stuff/project_one")
+ # Press open button
+ qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
+ with monkeypatch.context() as mp:
+ mp.setattr(welcome, "close", lambda *a: None)
+ with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
+ welcome.btnBox.button(QDialogButtonBox.StandardButton.Open).click()
+ assert signal.args and signal.args[0] == Path("/stuff/project_one")
+
# Context Menu
def getMenuForPos(pos: QPoint) -> QMenu | None:
nonlocal tabOpen
From 4bf82e4df76ace09809cb943a1df8d0f27653c86 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:03:57 +0100
Subject: [PATCH 2/7] Hide unused controls on new project form
---
novelwriter/tools/welcome.py | 30 ++++++++++++++------------
tests/test_tools/test_tools_welcome.py | 21 +++---------------
2 files changed, 19 insertions(+), 32 deletions(-)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index ba1f9f9b..4d9b3955 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -539,6 +539,7 @@ class _NewProjectForm(QWidget):
self._copyPath = None
iPx = SHARED.theme.baseIconSize
+ sPx = CONFIG.pxInt(16)
# Project Settings
# ================
@@ -668,15 +669,23 @@ class _NewProjectForm(QWidget):
# Assemble
# ========
+ self.extraBox = QVBoxLayout()
+ self.extraBox.addWidget(QLabel("{0}".format(self.tr("Chapters and Scenes"))))
+ self.extraBox.addLayout(self.novelForm)
+ self.extraBox.addSpacing(sPx)
+ self.extraBox.addWidget(QLabel("{0}".format(self.tr("Project Notes"))))
+ self.extraBox.addLayout(self.notesForm)
+ self.extraBox.setContentsMargins(0, 0, 0, 0)
+
+ self.extraWidget = QWidget(self)
+ self.extraWidget.setLayout(self.extraBox)
+ self.extraWidget.setContentsMargins(0, 0, 0, 0)
+
self.formBox = QVBoxLayout()
self.formBox.addWidget(QLabel("{0}".format(self.tr("Create New Project"))))
self.formBox.addLayout(self.projectForm)
- self.formBox.addSpacing(16)
- self.formBox.addWidget(QLabel("{0}".format(self.tr("Chapters and Scenes"))))
- self.formBox.addLayout(self.novelForm)
- self.formBox.addSpacing(16)
- self.formBox.addWidget(QLabel("{0}".format(self.tr("Project Notes"))))
- self.formBox.addLayout(self.notesForm)
+ self.formBox.addSpacing(sPx)
+ self.formBox.addWidget(self.extraWidget)
self.formBox.addStretch(1)
self.setLayout(self.formBox)
@@ -782,14 +791,7 @@ class _NewProjectForm(QWidget):
self.projFill.setText(text)
self.projFill.setToolTip(text)
self.projFill.setCursorPosition(0)
-
- isBlank = self._fillMode == self.FILL_BLANK
- self.numChapters.setEnabled(isBlank)
- self.numScenes.setEnabled(isBlank)
- self.addPlot.setEnabled(isBlank)
- self.addChar.setEnabled(isBlank)
- self.addWorld.setEnabled(isBlank)
- self.addNotes.setEnabled(isBlank)
+ self.extraWidget.setVisible(self._fillMode == self.FILL_BLANK)
return
diff --git a/tests/test_tools/test_tools_welcome.py b/tests/test_tools/test_tools_welcome.py
index b3a566d7..85bdabd6 100644
--- a/tests/test_tools/test_tools_welcome.py
+++ b/tests/test_tools/test_tools_welcome.py
@@ -196,12 +196,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
newForm.fillSample.trigger()
assert newForm._fillMode == newForm.FILL_SAMPLE
assert newForm.projFill.text() == "Example Project"
- assert newForm.addNotes.isEnabled() is False
- assert newForm.addPlot.isEnabled() is False
- assert newForm.addChar.isEnabled() is False
- assert newForm.addWorld.isEnabled() is False
- assert newForm.numChapters.isEnabled() is False
- assert newForm.numScenes.isEnabled() is False
+ assert newForm.extraWidget.isVisible() is False
# Change fill info to template
with monkeypatch.context() as mp:
@@ -209,12 +204,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
newForm.fillCopy.trigger()
assert newForm._fillMode == newForm.FILL_COPY
assert newForm.projFill.text() == f"Template: {fncPath}"
- assert newForm.addNotes.isEnabled() is False
- assert newForm.addPlot.isEnabled() is False
- assert newForm.addChar.isEnabled() is False
- assert newForm.addWorld.isEnabled() is False
- assert newForm.numChapters.isEnabled() is False
- assert newForm.numScenes.isEnabled() is False
+ assert newForm.extraWidget.isVisible() is False
# Change back to fill blank using the menu
newForm.browseFill.click()
@@ -223,12 +213,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
newForm.fillMenu.close()
assert newForm._fillMode == newForm.FILL_BLANK
assert newForm.projFill.text() == "Fresh Project"
- assert newForm.addNotes.isEnabled() is True
- assert newForm.addPlot.isEnabled() is True
- assert newForm.addChar.isEnabled() is True
- assert newForm.addWorld.isEnabled() is True
- assert newForm.numChapters.isEnabled() is True
- assert newForm.numScenes.isEnabled() is True
+ assert newForm.extraWidget.isVisible() is True
# Creating a project without a name, pops an error
caplog.clear()
From f0e86dad102154c6e205a1678cb82288ecad638b Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:07:32 +0100
Subject: [PATCH 3/7] Drop language setting on new project form
---
novelwriter/core/coretools.py | 11 +++--------
novelwriter/tools/welcome.py | 19 +++++++------------
.../coreTools_ProjectBuilderA_nwProject.nwx | 4 ++--
.../coreTools_ProjectBuilderB_nwProject.nwx | 4 ++--
tests/test_core/test_core_coretools.py | 9 ++++-----
5 files changed, 18 insertions(+), 29 deletions(-)
diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py
index c77b99fb..0552001d 100644
--- a/novelwriter/core/coretools.py
+++ b/novelwriter/core/coretools.py
@@ -365,14 +365,10 @@ class ProjectBuilder:
lblByAuthors = self.tr("By")
# Settings
- projName = data.get("name", lblNewProject)
- projAuthor = data.get("author", "")
- projLang = data.get("language", "en_GB")
-
project.data.setUuid(None)
- project.data.setName(projName)
- project.data.setAuthor(projAuthor)
- project.data.setLanguage(projLang)
+ project.data.setName(data.get("name", lblNewProject))
+ project.data.setAuthor(data.get("author", ""))
+ project.data.setLanguage(CONFIG.guiLocale)
project.setDefaultStatusImport()
project.session.startSession()
@@ -502,7 +498,6 @@ class ProjectBuilder:
project.data.setUuid("") # Creates a fresh uuid
project.data.setName(data.get("name", "None"))
project.data.setAuthor(data.get("author", ""))
- project.data.setLanguage(data.get("language", "en_GB"))
project.data.setSpellCheck(True)
project.data.setSpellLang(None)
project.data.setDoBackup(True)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index 4d9b3955..e9589f66 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -45,8 +45,9 @@ from novelwriter.enum import nwItemClass
from novelwriter.common import formatInt, makeFileNameSafe
from novelwriter.constants import nwFiles
from novelwriter.core.coretools import ProjectBuilder
+from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.switch import NSwitch
-from novelwriter.extensions.modified import NComboBox, NSpinBox
+from novelwriter.extensions.modified import NSpinBox
from novelwriter.extensions.versioninfo import VersionInfoWidget
logger = logging.getLogger(__name__)
@@ -544,6 +545,10 @@ class _NewProjectForm(QWidget):
# Project Settings
# ================
+ self.projHelp = NColourLabel(self.tr(
+ "These setting can be changed later from Project Settings."
+ ), color=SHARED.theme.helpText, parent=self)
+
# Project Name
self.projName = QLineEdit(self)
self.projName.setMaxLength(200)
@@ -555,15 +560,6 @@ class _NewProjectForm(QWidget):
self.projAuthor.setMaxLength(200)
self.projAuthor.setPlaceholderText(self.tr("Optional"))
- # Project Language
- self.projLang = NComboBox(self)
- for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ):
- self.projLang.addItem(language, tag)
-
- langIdx = self.projLang.findData(CONFIG.guiLocale)
- if langIdx != -1:
- self.projLang.setCurrentIndex(langIdx)
-
# Project Path
self.projPath = QLineEdit(self)
self.projPath.setReadOnly(True)
@@ -609,7 +605,6 @@ class _NewProjectForm(QWidget):
self.projectForm.setAlignment(Qt.AlignmentFlag.AlignLeft)
self.projectForm.addRow(self.tr("Project Name"), self.projName)
self.projectForm.addRow(self.tr("Author"), self.projAuthor)
- self.projectForm.addRow(self.tr("Language"), self.projLang)
self.projectForm.addRow(self.tr("Project Path"), self.pathBox)
self.projectForm.addRow(self.tr("Prefill Project"), self.fillBox)
@@ -683,6 +678,7 @@ class _NewProjectForm(QWidget):
self.formBox = QVBoxLayout()
self.formBox.addWidget(QLabel("{0}".format(self.tr("Create New Project"))))
+ self.formBox.addWidget(self.projHelp)
self.formBox.addLayout(self.projectForm)
self.formBox.addSpacing(sPx)
self.formBox.addWidget(self.extraWidget)
@@ -707,7 +703,6 @@ class _NewProjectForm(QWidget):
return {
"name": self.projName.text().strip(),
"author": self.projAuthor.text().strip(),
- "language": self.projLang.currentData(),
"path": self.projPath.text(),
"blank": self._fillMode == self.FILL_BLANK,
"sample": self._fillMode == self.FILL_SAMPLE,
diff --git a/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx b/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
index ee5eeeea..60c0fce3 100644
--- a/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
+++ b/tests/reference/coreTools_ProjectBuilderA_nwProject.nwx
@@ -1,12 +1,12 @@
-
+
Test Project A
Jane Doe
yes
- None
+ en_GB
None
None
diff --git a/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx b/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
index 209c7809..86980406 100644
--- a/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
+++ b/tests/reference/coreTools_ProjectBuilderB_nwProject.nwx
@@ -1,12 +1,12 @@
-
+
Test Project B
Jane Doe
yes
- None
+ en_GB
None
None
diff --git a/tests/test_core/test_core_coretools.py b/tests/test_core/test_core_coretools.py
index 3c03e048..2309bd99 100644
--- a/tests/test_core/test_core_coretools.py
+++ b/tests/test_core/test_core_coretools.py
@@ -509,7 +509,6 @@ def testCoreTools_ProjectBuilderCopyPlain(monkeypatch, caplog, mockGUI, prjLipsu
data = {
"name": "Test Project",
"author": "Jane Doe",
- "language": "en_US",
"path": dstPath,
"template": srcPath,
}
@@ -556,9 +555,9 @@ def testCoreTools_ProjectBuilderCopyPlain(monkeypatch, caplog, mockGUI, prjLipsu
assert srcProject.data.author == "lipsum.com"
assert dstProject.data.author == "Jane Doe"
- # Language should be different
+ # Language should be the same
assert srcProject.data.language == "en_GB"
- assert dstProject.data.language == "en_US"
+ assert dstProject.data.language == "en_GB"
# Counts should be more or less zeroed
assert dstProject.data.saveCount < 5
@@ -634,9 +633,9 @@ def testCoreTools_ProjectBuilderCopyZipped(monkeypatch, caplog, mockGUI, fncPath
assert srcProject.data.author == "Jane Doe"
assert dstProject.data.author == "Jane Doe"
- # Language should be different
+ # Language should be the same
assert srcProject.data.language is None
- assert dstProject.data.language == "en_US"
+ assert dstProject.data.language is None
# Counts should be more or less zeroed
assert dstProject.data.saveCount < 5
From 576c13d04500e430c4d33d83e5d450bdd396c89a Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:10:08 +0100
Subject: [PATCH 4/7] Redesign button row on welcome dialog
---
.../assets/icons/typicons_dark/icons.conf | 2 +
.../icons/typicons_dark/typ_th-list.svg | 9 ++
.../assets/icons/typicons_light/icons.conf | 2 +
.../icons/typicons_light/typ_th-list.svg | 9 ++
novelwriter/gui/theme.py | 6 +-
novelwriter/tools/welcome.py | 87 +++++++++++--------
6 files changed, 78 insertions(+), 37 deletions(-)
create mode 100644 novelwriter/assets/icons/typicons_dark/typ_th-list.svg
create mode 100644 novelwriter/assets/icons/typicons_light/typ_th-list.svg
diff --git a/novelwriter/assets/icons/typicons_dark/icons.conf b/novelwriter/assets/icons/typicons_dark/icons.conf
index 44504e57..442f0d82 100644
--- a/novelwriter/assets/icons/typicons_dark/icons.conf
+++ b/novelwriter/assets/icons/typicons_dark/icons.conf
@@ -60,11 +60,13 @@ fmt_superscript = nw_tb-superscript.svg
fmt_underline = nw_tb-underline.svg
forward = typ_chevron-right.svg
import = mixed_import.svg
+list = typ_th-list.svg
maximise = typ_arrow-maximise.svg
menu = typ_th-dot-menu.svg
minimise = typ_arrow-minimise.svg
more = typ_th-dot-more.svg
noncheckable = mixed_input-none.svg
+open = typ_folder.svg
panel = nw_panel.svg
proj_chapter = mixed_document-chapter.svg
proj_details = typ_th-list-grey.svg
diff --git a/novelwriter/assets/icons/typicons_dark/typ_th-list.svg b/novelwriter/assets/icons/typicons_dark/typ_th-list.svg
new file mode 100644
index 00000000..85a79c58
--- /dev/null
+++ b/novelwriter/assets/icons/typicons_dark/typ_th-list.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/novelwriter/assets/icons/typicons_light/icons.conf b/novelwriter/assets/icons/typicons_light/icons.conf
index 9af4e5f2..4c6d1890 100644
--- a/novelwriter/assets/icons/typicons_light/icons.conf
+++ b/novelwriter/assets/icons/typicons_light/icons.conf
@@ -60,11 +60,13 @@ fmt_superscript = nw_tb-superscript.svg
fmt_underline = nw_tb-underline.svg
forward = typ_chevron-right.svg
import = mixed_import.svg
+list = typ_th-list.svg
maximise = typ_arrow-maximise.svg
menu = typ_th-dot-menu.svg
minimise = typ_arrow-minimise.svg
more = typ_th-dot-more.svg
noncheckable = mixed_input-none.svg
+open = typ_folder.svg
panel = nw_panel.svg
proj_chapter = mixed_document-chapter.svg
proj_details = typ_th-list-grey.svg
diff --git a/novelwriter/assets/icons/typicons_light/typ_th-list.svg b/novelwriter/assets/icons/typicons_light/typ_th-list.svg
new file mode 100644
index 00000000..d569d85f
--- /dev/null
+++ b/novelwriter/assets/icons/typicons_light/typ_th-list.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py
index 5c38ca33..412a5363 100644
--- a/novelwriter/gui/theme.py
+++ b/novelwriter/gui/theme.py
@@ -453,9 +453,9 @@ class GuiIcons:
# General Button Icons
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
- "document", "down", "edit", "export", "forward", "import", "maximise", "menu", "minimise",
- "more", "noncheckable", "panel", "refresh", "remove", "revert", "search_replace", "search",
- "settings", "star", "unchecked", "up", "view",
+ "document", "down", "edit", "export", "forward", "import", "list", "maximise", "menu",
+ "minimise", "more", "noncheckable", "open", "panel", "refresh", "remove", "revert",
+ "search_replace", "search", "settings", "star", "unchecked", "up", "view",
# Switches
"sticky-on", "sticky-off",
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index e9589f66..b4b18729 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -34,10 +34,10 @@ from PyQt5.QtCore import (
pyqtSignal, pyqtSlot
)
from PyQt5.QtWidgets import (
- QAction, QDialog, QDialogButtonBox, QFileDialog, QFormLayout, QHBoxLayout,
- QLabel, QLineEdit, QListView, QMenu, QPushButton, QScrollArea, QShortcut,
- QStackedWidget, QStyle, QStyleOptionViewItem, QStyledItemDelegate,
- QToolButton, QVBoxLayout, QWidget, qApp
+ QAction, QDialog, QFileDialog, QFormLayout, QHBoxLayout, QLabel, QLineEdit,
+ QListView, QMenu, QPushButton, QScrollArea, QShortcut, QStackedWidget,
+ QStyle, QStyleOptionViewItem, QStyledItemDelegate, QToolButton,
+ QVBoxLayout, QWidget, qApp
)
from novelwriter import CONFIG, SHARED
@@ -98,7 +98,6 @@ class GuiWelcome(QDialog):
self.tabOpen.openProjectRequest.connect(self._openProjectPath)
self.tabNew = _NewProjectPage(self)
- self.tabNew.cancelNewProject.connect(self._showOpenProjectPage)
self.tabNew.openProjectRequest.connect(self._openProjectPath)
self.mainStack = QStackedWidget(self)
@@ -108,17 +107,39 @@ class GuiWelcome(QDialog):
# Buttons
# =======
- self.btnBox = QDialogButtonBox(QDialogButtonBox.Open | QDialogButtonBox.Cancel, self)
- self.btnBox.accepted.connect(self._openSelectedItem)
- self.btnBox.rejected.connect(self.close)
+ self.btnList = QPushButton(self.tr("List Projects"), self)
+ self.btnList.setIcon(SHARED.theme.getIcon("list"))
+ self.btnList.clicked.connect(self._showOpenProjectPage)
- self.newButton = self.btnBox.addButton(self.tr("New Project"), QDialogButtonBox.ActionRole)
- self.newButton.setIcon(SHARED.theme.getIcon("add"))
- self.newButton.clicked.connect(self._showNewProjectPage)
+ self.btnNew = QPushButton(self.tr("New Project"), self)
+ self.btnNew.setIcon(SHARED.theme.getIcon("add"))
+ self.btnNew.clicked.connect(self._showNewProjectPage)
- self.browseButton = self.btnBox.addButton(self.tr("Browse"), QDialogButtonBox.ActionRole)
- self.browseButton.setIcon(SHARED.theme.getIcon("browse"))
- self.browseButton.clicked.connect(self._browseForProject)
+ self.btnBrowse = QPushButton(self.tr("Browse"), self)
+ self.btnBrowse.setIcon(SHARED.theme.getIcon("browse"))
+ self.btnBrowse.clicked.connect(self._browseForProject)
+
+ self.btnCancel = QPushButton(self.tr("Cancel"), self)
+ self.btnCancel.setIcon(SHARED.theme.getIcon("cross"))
+ self.btnCancel.clicked.connect(self.close)
+
+ self.btnCreate = QPushButton(self.tr("Create"), self)
+ self.btnCreate.setIcon(SHARED.theme.getIcon("star"))
+ self.btnCreate.clicked.connect(self.tabNew.createNewProject)
+
+ self.btnOpen = QPushButton(self.tr("Open"), self)
+ self.btnOpen.setIcon(SHARED.theme.getIcon("open"))
+ self.btnOpen.clicked.connect(self._openSelectedItem)
+
+ self.btnBox = QHBoxLayout()
+ self.btnBox.addStretch(1)
+ self.btnBox.addWidget(self.btnList)
+ self.btnBox.addWidget(self.btnNew)
+ self.btnBox.addWidget(self.btnBrowse)
+ self.btnBox.addWidget(self.btnCancel)
+ self.btnBox.addWidget(self.btnCreate)
+ self.btnBox.addWidget(self.btnOpen)
+ self._setButtonVisibility()
# Assemble
# ========
@@ -130,7 +151,7 @@ class GuiWelcome(QDialog):
self.innerBox.addSpacing(hA)
self.innerBox.addWidget(self.mainStack)
self.innerBox.addSpacing(hB)
- self.innerBox.addWidget(self.btnBox)
+ self.innerBox.addLayout(self.btnBox)
topRight = Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignRight
@@ -180,12 +201,14 @@ class GuiWelcome(QDialog):
def _showNewProjectPage(self) -> None:
"""Show the create new project page."""
self.mainStack.setCurrentWidget(self.tabNew)
+ self._setButtonVisibility()
return
@pyqtSlot()
def _showOpenProjectPage(self) -> None:
"""Show the open exiting project page."""
self.mainStack.setCurrentWidget(self.tabOpen)
+ self._setButtonVisibility()
return
@pyqtSlot()
@@ -224,6 +247,20 @@ class GuiWelcome(QDialog):
CONFIG.setWelcomeWinSize(self.width(), self.height())
return
+ def _setButtonVisibility(self) -> None:
+ """Change the visibility of the dialog buttons."""
+ listMode = self.mainStack.currentWidget() == self.tabOpen
+ self.btnList.setVisible(not listMode)
+ self.btnNew.setVisible(listMode)
+ self.btnBrowse.setVisible(listMode)
+ self.btnCreate.setVisible(not listMode)
+ self.btnOpen.setVisible(listMode)
+ if listMode:
+ self.btnOpen.setFocus()
+ else:
+ self.btnCreate.setFocus()
+ return
+
# END Class GuiWelcome
@@ -453,7 +490,6 @@ class _ProjectListModel(QAbstractListModel):
class _NewProjectPage(QWidget):
- cancelNewProject = pyqtSignal()
openProjectRequest = pyqtSignal(Path)
def __init__(self, parent: QWidget) -> None:
@@ -470,28 +506,11 @@ class _NewProjectPage(QWidget):
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
- # Controls
- # ========
-
- self.cancelButton = QPushButton(self.tr("Go Back"), self)
- self.cancelButton.setIcon(SHARED.theme.getIcon("backward"))
- self.cancelButton.clicked.connect(lambda: self.cancelNewProject.emit())
-
- self.createButton = QPushButton(self.tr("Create Project"), self)
- self.createButton.setIcon(SHARED.theme.getIcon("star"))
- self.createButton.clicked.connect(self._createNewProject)
-
- self.buttonBox = QHBoxLayout()
- self.buttonBox.addStretch(1)
- self.buttonBox.addWidget(self.cancelButton, 0)
- self.buttonBox.addWidget(self.createButton, 0)
-
# Assemble
# ========
self.outerBox = QVBoxLayout()
self.outerBox.addWidget(self.scrollArea)
- self.outerBox.addLayout(self.buttonBox)
self.outerBox.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.outerBox)
@@ -512,7 +531,7 @@ class _NewProjectPage(QWidget):
##
@pyqtSlot()
- def _createNewProject(self) -> None:
+ def createNewProject(self) -> None:
"""Create a new project from the data in the form."""
data = self.projectForm.getProjectData()
if not data.get("name"):
From 10764a2041c4beb2a1729332c70182850f96dea8 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:20:49 +0100
Subject: [PATCH 5/7] Block overwriting when creating sample project
---
novelwriter/core/coretools.py | 7 +++++++
tests/test_core/test_core_coretools.py | 5 ++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/novelwriter/core/coretools.py b/novelwriter/core/coretools.py
index 0552001d..a4e3e3aa 100644
--- a/novelwriter/core/coretools.py
+++ b/novelwriter/core/coretools.py
@@ -513,6 +513,13 @@ class ProjectBuilder:
"""Make a copy of the sample project by extracting the
sample.zip file to the new path.
"""
+ if path.exists():
+ SHARED.error(self.tr(
+ "The target folder already exists. "
+ "Please choose another folder."
+ ))
+ return False
+
if (sample := CONFIG.assetPath("sample.zip")).is_file():
try:
shutil.unpack_archive(sample, path)
diff --git a/tests/test_core/test_core_coretools.py b/tests/test_core/test_core_coretools.py
index 2309bd99..85ebedfd 100644
--- a/tests/test_core/test_core_coretools.py
+++ b/tests/test_core/test_core_coretools.py
@@ -651,7 +651,7 @@ def testCoreTools_ProjectBuilderSample(monkeypatch, mockGUI, fncPath, tstPaths):
data = {
"name": "Test Sample",
"author": "Jane Doe",
- "path": fncPath,
+ "path": fncPath / "project",
"sample": True,
}
@@ -684,6 +684,9 @@ def testCoreTools_ProjectBuilderSample(monkeypatch, mockGUI, fncPath, tstPaths):
zipObj.write(docFile, f"content/{docFile.name}")
assert builder.buildProject(data) is True
+
+ # Can't create to the same target again
+ assert builder.buildProject(data) is False
dstSample.unlink()
# END Test testCoreTools_ProjectBuilderSample
From 1c91ba6fd940a4cc92d92aef2675d3ba9610ff3f Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:21:02 +0100
Subject: [PATCH 6/7] Fix welcome dialog tests
---
tests/test_tools/test_tools_welcome.py | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/tests/test_tools/test_tools_welcome.py b/tests/test_tools/test_tools_welcome.py
index 85bdabd6..11d2e6c3 100644
--- a/tests/test_tools/test_tools_welcome.py
+++ b/tests/test_tools/test_tools_welcome.py
@@ -27,7 +27,7 @@ from datetime import datetime
from pytestqt.qtbot import QtBot
from PyQt5.QtCore import QPoint, Qt
-from PyQt5.QtWidgets import QAction, QDialogButtonBox, QFileDialog, QMenu
+from PyQt5.QtWidgets import QAction, QFileDialog, QMenu
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemClass
@@ -47,18 +47,18 @@ def testToolWelcome_Main(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
assert welcome.mainStack.currentIndex() == 0
# Show the new project form
- welcome.newButton.click()
+ welcome.btnNew.click()
assert welcome.mainStack.currentIndex() == 1
# Revert to project lits
- welcome.tabNew.cancelNewProject.emit()
+ welcome.btnList.click()
assert welcome.mainStack.currentIndex() == 0
# Open a project
with monkeypatch.context() as mp:
mp.setattr(SHARED, "getProjectPath", lambda *a, **k: fncPath)
with qtbot.waitSignal(welcome.openProjectRequest) as signal:
- welcome.browseButton.click()
+ welcome.btnBrowse.click()
assert signal.args and signal.args[0] == fncPath
# qtbot.stop()
@@ -116,7 +116,7 @@ def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
with monkeypatch.context() as mp:
mp.setattr(welcome, "close", lambda *a: None)
with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
- welcome.btnBox.button(QDialogButtonBox.StandardButton.Open).click()
+ welcome.btnOpen.click()
assert signal.args and signal.args[0] == Path("/stuff/project_one")
# Context Menu
@@ -173,7 +173,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
with qtbot.waitExposed(welcome):
welcome.show()
- welcome.newButton.click()
+ welcome.btnNew.click()
assert welcome.mainStack.currentIndex() == 1
tabNew = welcome.tabNew
newForm = tabNew.projectForm
@@ -217,7 +217,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
# Creating a project without a name, pops an error
caplog.clear()
- tabNew.createButton.click()
+ welcome.btnCreate.click()
assert "A project name is required." in caplog.text
# Set some more values, and extract data
@@ -233,7 +233,6 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
assert newForm.getProjectData() == {
"name": "Test Project",
"author": "Jane Smith",
- "language": "en_GB",
"path": str(projPath),
"blank": True,
"sample": False,
@@ -246,7 +245,7 @@ def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
# Create a project with these values
with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
- tabNew.createButton.click()
+ welcome.btnCreate.click()
assert signal.args and signal.args[0] == projPath
assert (projPath / nwFiles.PROJ_FILE).exists()
From 996f48a85ffdef16fc3ef340a99dd2e6695a127b Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 20 Feb 2024 23:31:18 +0100
Subject: [PATCH 7/7] Make some minor fixes to the welcome dialog source
---
novelwriter/tools/welcome.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/novelwriter/tools/welcome.py b/novelwriter/tools/welcome.py
index b4b18729..51335306 100644
--- a/novelwriter/tools/welcome.py
+++ b/novelwriter/tools/welcome.py
@@ -45,10 +45,10 @@ from novelwriter.enum import nwItemClass
from novelwriter.common import formatInt, makeFileNameSafe
from novelwriter.constants import nwFiles
from novelwriter.core.coretools import ProjectBuilder
-from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.switch import NSwitch
from novelwriter.extensions.modified import NSpinBox
from novelwriter.extensions.versioninfo import VersionInfoWidget
+from novelwriter.extensions.configlayout import NColourLabel
logger = logging.getLogger(__name__)
@@ -527,7 +527,7 @@ class _NewProjectPage(QWidget):
return
##
- # Private Slots
+ # Public Slots
##
@pyqtSlot()