Add project fill options to new project page on welcome dialog
This commit is contained in:
@@ -44,6 +44,7 @@ cls_timeline = typ_calendar.svg
|
||||
cls_trash = typ_trash.svg
|
||||
cls_world = typ_location.svg
|
||||
cross = typ_times.svg
|
||||
document = typ_document.svg
|
||||
down = typ_chevron-down.svg
|
||||
edit = typ_pencil.svg
|
||||
export = typ_export.svg
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m21.42 6.247-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill="#aeaeae" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 737 B |
@@ -44,6 +44,7 @@ cls_timeline = typ_calendar.svg
|
||||
cls_trash = typ_trash.svg
|
||||
cls_world = typ_location.svg
|
||||
cross = typ_times.svg
|
||||
document = typ_document.svg
|
||||
down = typ_chevron-down.svg
|
||||
edit = typ_pencil.svg
|
||||
export = typ_export.svg
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m21.42 6.247-4.8889-4.8889c-0.22856-0.22978-0.539-0.35811-0.86411-0.35811h-9.7778c-2.0216 0-3.6667 1.6451-3.6667 3.6667v14.667c0 2.0216 1.6451 3.6667 3.6667 3.6667h12.222c2.0216 0 3.6667-1.6451 3.6667-3.6667v-12.222c0-0.32511-0.12833-0.63556-0.35811-0.86411zm-2.5923 0.86411h-1.3273c-1.0108 0-1.8333-0.82256-1.8333-1.8333v-1.3273zm-0.71622 13.444h-12.222c-0.67467 0-1.2222-0.54756-1.2222-1.2222v-14.667c0-0.67467 0.54756-1.2222 1.2222-1.2222h8.5556v1.8333c0 1.6854 1.3701 3.0556 3.0556 3.0556h1.8333v11c0 0.67467-0.54756 1.2222-1.2222 1.2222z" fill-opacity=".78039" stroke-width="1.2222"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 744 B |
+17
-19
@@ -421,10 +421,10 @@ class Config:
|
||||
"""Compile and return error messages from the initialisation of
|
||||
the Config class, and clear the error buffer.
|
||||
"""
|
||||
errMessage = "<br>".join(self._errData)
|
||||
message = "<br>".join(self._errData)
|
||||
self._hasError = False
|
||||
self._errData = []
|
||||
return errMessage
|
||||
return message
|
||||
|
||||
def listLanguages(self, lngSet: int) -> list[tuple[str, str]]:
|
||||
"""List localisation files in the i18n folder. The default GUI
|
||||
@@ -647,7 +647,7 @@ class Config:
|
||||
conf = NWConfigParser()
|
||||
|
||||
conf["Meta"] = {
|
||||
"timestamp": formatTimeStamp(time()),
|
||||
"timestamp": formatTimeStamp(time()),
|
||||
}
|
||||
|
||||
conf["Main"] = {
|
||||
@@ -787,22 +787,20 @@ class RecentProjects:
|
||||
self._data = {}
|
||||
|
||||
cacheFile = self._conf.dataPath(nwFiles.RECENT_FILE)
|
||||
if not cacheFile.is_file():
|
||||
return True
|
||||
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
theData = json.load(inFile)
|
||||
for projPath, theEntry in theData.items():
|
||||
self._data[projPath] = {
|
||||
"title": theEntry.get("title", ""),
|
||||
"words": theEntry.get("words", 0),
|
||||
"time": theEntry.get("time", 0),
|
||||
}
|
||||
except Exception:
|
||||
logger.error("Could not load recent project cache")
|
||||
logException()
|
||||
return False
|
||||
if cacheFile.is_file():
|
||||
try:
|
||||
with open(cacheFile, mode="r", encoding="utf-8") as inFile:
|
||||
data = json.load(inFile)
|
||||
for path, entry in data.items():
|
||||
self._data[path] = {
|
||||
"title": entry.get("title", ""),
|
||||
"words": entry.get("words", 0),
|
||||
"time": entry.get("time", 0),
|
||||
}
|
||||
except Exception:
|
||||
logger.error("Could not load recent project cache")
|
||||
logException()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ from PyQt5.QtWidgets import (
|
||||
from novelwriter import CONFIG
|
||||
|
||||
FONT_SCALE = 0.9
|
||||
RIGHT_TOP = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop
|
||||
LEFT_TOP = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
|
||||
|
||||
|
||||
class NConfigLayout(QGridLayout):
|
||||
@@ -103,31 +105,31 @@ class NConfigLayout(QGridLayout):
|
||||
labelBox.addWidget(qHelp)
|
||||
labelBox.setSpacing(0)
|
||||
labelBox.addStretch(1)
|
||||
self.addLayout(labelBox, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||
self.addLayout(labelBox, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
else:
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
|
||||
if isinstance(unit, str):
|
||||
controlBox = QHBoxLayout()
|
||||
controlBox.addWidget(widget, 0, Qt.AlignVCenter)
|
||||
controlBox.addWidget(QLabel(unit), 0, Qt.AlignVCenter)
|
||||
controlBox.setSpacing(wSp)
|
||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
|
||||
elif isinstance(button, QAbstractButton):
|
||||
controlBox = QHBoxLayout()
|
||||
controlBox.addWidget(widget, 0, Qt.AlignVCenter)
|
||||
controlBox.addWidget(button, 0, Qt.AlignVCenter)
|
||||
controlBox.setSpacing(wSp)
|
||||
self.addLayout(controlBox, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||
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, Qt.AlignRight | Qt.AlignTop)
|
||||
self.addLayout(qLayout, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
else:
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
|
||||
self.setRowStretch(self._nextRow, 0)
|
||||
self.setRowStretch(self._nextRow+1, 1)
|
||||
@@ -145,14 +147,14 @@ class NSimpleLayout(QGridLayout):
|
||||
column layout.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, stretcColumn: int = 0) -> None:
|
||||
super().__init__()
|
||||
self._nextRow = 0
|
||||
|
||||
wSp = CONFIG.pxInt(8)
|
||||
self.setHorizontalSpacing(wSp)
|
||||
self.setVerticalSpacing(wSp)
|
||||
self.setColumnStretch(0, 1)
|
||||
self.setColumnStretch(stretcColumn, 1)
|
||||
|
||||
return
|
||||
|
||||
@@ -176,14 +178,14 @@ class NSimpleLayout(QGridLayout):
|
||||
wSp = CONFIG.pxInt(8)
|
||||
qLabel = QLabel(label)
|
||||
qLabel.setIndent(wSp)
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, Qt.AlignLeft | Qt.AlignTop)
|
||||
self.addWidget(qLabel, self._nextRow, 0, 1, 1, LEFT_TOP)
|
||||
|
||||
if isinstance(widget, QLineEdit):
|
||||
qLayout = QHBoxLayout()
|
||||
qLayout.addWidget(widget)
|
||||
self.addLayout(qLayout, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||
self.addLayout(qLayout, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
else:
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, Qt.AlignRight | Qt.AlignTop)
|
||||
self.addWidget(widget, self._nextRow, 1, 1, 1, RIGHT_TOP)
|
||||
|
||||
qLabel.setBuddy(widget)
|
||||
|
||||
|
||||
@@ -167,8 +167,7 @@ class _PopRightMenu(QMenu):
|
||||
def event(self, event: QEvent) -> bool:
|
||||
"""Overload the show event and move the menu popup location."""
|
||||
if event.type() == QEvent.Show:
|
||||
parent = self.parent()
|
||||
if isinstance(parent, QWidget):
|
||||
if isinstance(parent := self.parent(), QWidget):
|
||||
offset = QPoint(parent.width(), parent.height() - self.height())
|
||||
self.move(parent.mapToGlobal(offset))
|
||||
return super(_PopRightMenu, self).event(event)
|
||||
|
||||
@@ -467,9 +467,9 @@ class GuiIcons:
|
||||
|
||||
# General Button Icons
|
||||
"add", "add_document", "backward", "bookmark", "browse", "checked", "close", "cross",
|
||||
"down", "edit", "export", "forward", "maximise", "menu", "minimise", "noncheckable",
|
||||
"panel", "refresh", "remove", "revert", "search_replace", "search", "settings", "star",
|
||||
"unchecked", "up", "view",
|
||||
"document", "down", "edit", "export", "forward", "maximise", "menu", "minimise",
|
||||
"noncheckable", "panel", "refresh", "remove", "revert", "search_replace", "search",
|
||||
"settings", "star", "unchecked", "up", "view",
|
||||
|
||||
# Switches
|
||||
"sticky-on", "sticky-off",
|
||||
|
||||
+15
-1
@@ -31,8 +31,9 @@ from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
|
||||
from PyQt5.QtWidgets import QMessageBox, QWidget
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
|
||||
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.spellcheck import NWSpellEnchant
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -215,6 +216,19 @@ class SharedData(QObject):
|
||||
QThreadPool.globalInstance().start(runnable, priority=priority)
|
||||
return
|
||||
|
||||
def getProjectPath(self, parent: QWidget, allowZip: bool = False) -> Path | None:
|
||||
"""Open the file dialog and select a novelWriter project file."""
|
||||
ext = []
|
||||
ext.append(self.tr("novelWriter Project File ({0})").format(nwFiles.PROJ_FILE))
|
||||
if allowZip:
|
||||
ext.append(self.tr("Zip Archives ({0})").format("*.zip"))
|
||||
ext.append(self.tr("All files ({0})").format("*"))
|
||||
|
||||
projFile, _ = QFileDialog.getOpenFileName(
|
||||
parent, self.tr("Open Project"), "", filter=";;".join(ext)
|
||||
)
|
||||
return Path(projFile) if projFile else None
|
||||
|
||||
##
|
||||
# Signal Proxy
|
||||
##
|
||||
|
||||
@@ -30,11 +30,11 @@ from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
from PyQt5.QtGui import QCloseEvent, QPaintEvent, QPainter
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtCore import QEvent, QPoint, Qt, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtWidgets import (
|
||||
QComboBox, QDialog, QDialogButtonBox, QFileDialog, QFormLayout,
|
||||
QHBoxLayout, QLabel, QLineEdit, QPushButton, QScrollArea, QSpinBox,
|
||||
QStackedWidget, QTreeWidget, QVBoxLayout, QWidget
|
||||
QHBoxLayout, QLabel, QLineEdit, QMenu, QPushButton, QScrollArea, QSpinBox,
|
||||
QStackedWidget, QToolButton, QTreeWidget, QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG, SHARED, __version__, __date__
|
||||
@@ -265,18 +265,17 @@ class _NewProjectForm(QWidget):
|
||||
|
||||
FILL_BLANK = 0
|
||||
FILL_SAMPLE = 1
|
||||
FILL_TEMPLATE = 2
|
||||
FILL_COPY = 2
|
||||
|
||||
def __init__(self, parent: QWidget) -> None:
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self._basePath = CONFIG.lastPath()
|
||||
self._fillMode = self.FILL_BLANK
|
||||
self._tmplPath = None
|
||||
self._copyPath = None
|
||||
|
||||
# Project Settings
|
||||
# ================
|
||||
posLast = QLineEdit.ActionPosition.TrailingPosition
|
||||
|
||||
# Project Name
|
||||
self.projName = QLineEdit()
|
||||
@@ -303,22 +302,51 @@ class _NewProjectForm(QWidget):
|
||||
# Project Path
|
||||
self.projPath = QLineEdit(self)
|
||||
self.projPath.setReadOnly(True)
|
||||
self.projPath.setPlaceholderText(self.tr("Required"))
|
||||
action = self.projPath.addAction(SHARED.theme.getIcon("browse"), posLast)
|
||||
action.triggered.connect(self._doBrowse)
|
||||
|
||||
self.browsePath = QToolButton(self)
|
||||
self.browsePath.setIcon(SHARED.theme.getIcon("browse"))
|
||||
self.browsePath.clicked.connect(self._doBrowse)
|
||||
|
||||
self.pathBox = QHBoxLayout()
|
||||
self.pathBox.addWidget(self.projPath)
|
||||
self.pathBox.addWidget(self.browsePath)
|
||||
|
||||
# Fill Project
|
||||
self.projFill = QLineEdit(self)
|
||||
self.projFill.setReadOnly(True)
|
||||
action = self.projFill.addAction(SHARED.theme.getIcon("add_document"), posLast)
|
||||
|
||||
self.browseFill = QToolButton(self)
|
||||
self.browseFill.setIcon(SHARED.theme.getIcon("add_document"))
|
||||
|
||||
self.fillMenu = _PopLeftDirectionMenu(self.browseFill)
|
||||
|
||||
self.fillBlank = self.fillMenu.addAction(self.tr("Create a fresh project"))
|
||||
self.fillBlank.setIcon(SHARED.theme.getIcon("document"))
|
||||
self.fillBlank.triggered.connect(self._setFillBlank)
|
||||
|
||||
self.fillSample = self.fillMenu.addAction(self.tr("Create an example project"))
|
||||
self.fillSample.setIcon(SHARED.theme.getIcon("add_document"))
|
||||
self.fillSample.triggered.connect(self._setFillSample)
|
||||
|
||||
self.fillCopy = self.fillMenu.addAction(self.tr("Copy an existing project"))
|
||||
self.fillCopy.setIcon(SHARED.theme.getIcon("browse"))
|
||||
self.fillCopy.triggered.connect(self._setFillCopy)
|
||||
|
||||
self.browseFill.setMenu(self.fillMenu)
|
||||
self.browseFill.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
||||
|
||||
self.fillBox = QHBoxLayout()
|
||||
self.fillBox.addWidget(self.projFill)
|
||||
self.fillBox.addWidget(self.browseFill)
|
||||
|
||||
# Project Form
|
||||
self.projectForm = QFormLayout()
|
||||
self.projectForm.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
self.projectForm.addRow(self.tr("Project Name"), self.projName)
|
||||
self.projectForm.addRow(self.tr("Author(s)"), self.projAuthor)
|
||||
self.projectForm.addRow(self.tr("Author"), self.projAuthor)
|
||||
self.projectForm.addRow(self.tr("Language"), self.projLang)
|
||||
self.projectForm.addRow(self.tr("Project Path"), self.projPath)
|
||||
self.projectForm.addRow(self.tr("Prefill Project"), self.projFill)
|
||||
self.projectForm.addRow(self.tr("Project Path"), self.pathBox)
|
||||
self.projectForm.addRow(self.tr("Prefill Project"), self.fillBox)
|
||||
|
||||
# Chapters and Scenes
|
||||
# ===================
|
||||
@@ -367,6 +395,7 @@ class _NewProjectForm(QWidget):
|
||||
self.addNotes.setChecked(False)
|
||||
|
||||
self.notesForm = QFormLayout()
|
||||
self.notesForm.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
self.notesForm.addRow(self.tr("Add a folder for plot notes"), self.addPlot)
|
||||
self.notesForm.addRow(self.tr("Add a folder for character notes"), self.addChar)
|
||||
self.notesForm.addRow(self.tr("Add a folder for location notes"), self.addWorld)
|
||||
@@ -426,18 +455,45 @@ class _NewProjectForm(QWidget):
|
||||
self.addNotes.setChecked(False)
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _setFillBlank(self) -> None:
|
||||
"""Set fill mode to blank project."""
|
||||
self._fillMode = self.FILL_BLANK
|
||||
self._updateFillInfo()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _setFillSample(self) -> None:
|
||||
"""Set fill mode to sample project."""
|
||||
self._fillMode = self.FILL_SAMPLE
|
||||
self._updateFillInfo()
|
||||
return
|
||||
|
||||
@pyqtSlot()
|
||||
def _setFillCopy(self) -> None:
|
||||
"""Set fill mode to copy project."""
|
||||
self._fillMode = self.FILL_COPY
|
||||
self._copyPath = SHARED.getProjectPath(self, allowZip=True)
|
||||
self._updateFillInfo()
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _updateFillInfo(self) -> None:
|
||||
"""Update the text of the project fill box."""
|
||||
text = ""
|
||||
if self._fillMode == self.FILL_BLANK:
|
||||
self.projFill.setText(self.tr("Fresh Project"))
|
||||
text = self.tr("Fresh Project")
|
||||
elif self._fillMode == self.FILL_SAMPLE:
|
||||
self.projFill.setText(self.tr("Example Project"))
|
||||
elif self._fillMode == self.FILL_TEMPLATE:
|
||||
self.projFill.setText(self.tr("Template: {0}").format(str(self._tmplPath)))
|
||||
text = self.tr("Example Project")
|
||||
elif self._fillMode == self.FILL_COPY:
|
||||
text = self.tr("Template: {0}").format(str(self._copyPath))
|
||||
|
||||
self.projFill.setText(text)
|
||||
self.projFill.setToolTip(text)
|
||||
self.projFill.setCursorPosition(0)
|
||||
|
||||
isBlank = self._fillMode == self.FILL_BLANK
|
||||
self.numChapters.setEnabled(isBlank)
|
||||
@@ -450,3 +506,16 @@ class _NewProjectForm(QWidget):
|
||||
return
|
||||
|
||||
# END Class _NewProjectForm
|
||||
|
||||
|
||||
class _PopLeftDirectionMenu(QMenu):
|
||||
|
||||
def event(self, event: QEvent) -> bool:
|
||||
"""Overload the show event and move the menu popup location."""
|
||||
if event.type() == QEvent.Show:
|
||||
if isinstance(parent := self.parent(), QWidget):
|
||||
offset = QPoint(parent.width() - self.width(), parent.height())
|
||||
self.move(parent.mapToGlobal(offset))
|
||||
return super(_PopLeftDirectionMenu, self).event(event)
|
||||
|
||||
# END Class _PopLeftDirectionMenu
|
||||
|
||||
Reference in New Issue
Block a user