From f26b405f427d2e1d843863b182fefdca4e3e3bcb Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:30:11 +0100 Subject: [PATCH] Add new project page to welcome dialog --- .../assets/icons/typicons_dark/icons.conf | 1 + .../assets/icons/typicons_light/icons.conf | 1 + novelwriter/gui/theme.py | 3 +- novelwriter/tools/welcome.py | 157 ++++++++++++++++-- 4 files changed, 150 insertions(+), 12 deletions(-) diff --git a/novelwriter/assets/icons/typicons_dark/icons.conf b/novelwriter/assets/icons/typicons_dark/icons.conf index 0adec011..12354128 100644 --- a/novelwriter/assets/icons/typicons_dark/icons.conf +++ b/novelwriter/assets/icons/typicons_dark/icons.conf @@ -83,6 +83,7 @@ search_regex = nw_search-regex.svg search_replace = mixed_search-replace.svg search_word = nw_search-word.svg settings = typ_cog.svg +star = typ_star.svg status_idle = typ_media-pause-grey.svg status_lang = typ_globe-grey.svg status_lines = typ_th-list-grey.svg diff --git a/novelwriter/assets/icons/typicons_light/icons.conf b/novelwriter/assets/icons/typicons_light/icons.conf index 69273d2f..584c5bab 100644 --- a/novelwriter/assets/icons/typicons_light/icons.conf +++ b/novelwriter/assets/icons/typicons_light/icons.conf @@ -83,6 +83,7 @@ search_regex = nw_search-regex.svg search_replace = mixed_search-replace.svg search_word = nw_search-word.svg settings = typ_cog.svg +star = typ_star.svg status_idle = typ_media-pause-grey.svg status_lang = typ_globe-grey.svg status_lines = typ_th-list-grey.svg diff --git a/novelwriter/gui/theme.py b/novelwriter/gui/theme.py index 1c8572d3..205ebe54 100644 --- a/novelwriter/gui/theme.py +++ b/novelwriter/gui/theme.py @@ -468,7 +468,8 @@ class GuiIcons: # General Button Icons "add", "backward", "bookmark", "browse", "checked", "close", "cross", "down", "edit", "export", "forward", "maximise", "menu", "minimise", "noncheckable", "panel", "refresh", - "remove", "revert", "search_replace", "search", "settings", "unchecked", "up", "view", + "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 1800706e..70647ad7 100644 --- a/novelwriter/tools/welcome.py +++ b/novelwriter/tools/welcome.py @@ -26,16 +26,19 @@ from __future__ import annotations import logging from typing import TYPE_CHECKING +from pathlib import Path from datetime import datetime from PyQt5.QtGui import QCloseEvent, QPaintEvent, QPainter -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot from PyQt5.QtWidgets import ( - QDialog, QDialogButtonBox, QHBoxLayout, QLabel, QStackedWidget, + QComboBox, QDialog, QDialogButtonBox, QFileDialog, QFormLayout, + QHBoxLayout, QLabel, QLineEdit, QPushButton, QScrollArea, QStackedWidget, QTreeWidget, QVBoxLayout, QWidget ) from novelwriter import CONFIG, SHARED, __version__, __date__ +from novelwriter.common import makeFileNameSafe from novelwriter.constants import nwUnicode if TYPE_CHECKING: # pragma: no cover @@ -77,8 +80,13 @@ class GuiWelcome(QDialog): __version__, nwUnicode.U_ENDASH, datetime.strptime(__date__, "%Y-%m-%d").strftime("%x") )) + self.tabOpen = _OpenProjectPage(self) + self.tabNew = _NewProjectPage(self) + self.tabNew.cancelNewProject.connect(self._showOpenProjectPage) + self.mainStack = QStackedWidget() - self.mainStack.addWidget(_ProjectsPage(self)) + self.mainStack.addWidget(self.tabOpen) + self.mainStack.addWidget(self.tabNew) # Buttons # ======= @@ -88,6 +96,7 @@ class GuiWelcome(QDialog): 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.browseButton = self.btnBox.addButton(self.tr("Browse"), QDialogButtonBox.ActionRole) self.browseButton.setIcon(SHARED.theme.getIcon("browse")) @@ -141,6 +150,22 @@ class GuiWelcome(QDialog): self.deleteLater() return + ## + # Private Slots + ## + + @pyqtSlot() + def _showNewProjectPage(self) -> None: + """Show the create new project page.""" + self.mainStack.setCurrentWidget(self.tabNew) + return + + @pyqtSlot() + def _showOpenProjectPage(self) -> None: + """Show the open exiting project page.""" + self.mainStack.setCurrentWidget(self.tabOpen) + return + ## # Internal Functions ## @@ -154,13 +179,19 @@ class GuiWelcome(QDialog): # END Class GuiWelcome -class _ProjectsPage(QWidget): +class _OpenProjectPage(QWidget): def __init__(self, parent: QWidget) -> None: super().__init__(parent=parent) + baseCol = self.palette().base().color() + listStyle = ( + "QTreeWidget {{border: none; background: rgba({0},{1},{2},0.5);}}" + ).format(baseCol.red(), baseCol.green(), baseCol.blue()) + self.listWidget = QTreeWidget(self) self.listWidget.setHeaderHidden(True) + self.listWidget.setStyleSheet(listStyle) self.outerBox = QVBoxLayout() self.outerBox.addWidget(self.listWidget) @@ -168,17 +199,121 @@ class _ProjectsPage(QWidget): self.setLayout(self.outerBox) - self.updateTheme() - return - def updateTheme(self) -> None: - """""" +# END Class _OpenProjectPage + + +class _NewProjectPage(QWidget): + + cancelNewProject = pyqtSignal() + + def __init__(self, parent: QWidget) -> None: + super().__init__(parent=parent) + baseCol = self.palette().base().color() listStyle = ( - "QTreeWidget {{border: none; background: rgba({0},{1},{2},0.5);}}" + "QScrollArea {{border: none; background: rgba({0},{1},{2},0.5);}}" ).format(baseCol.red(), baseCol.green(), baseCol.blue()) - self.listWidget.setStyleSheet(listStyle) + + self.centralScroll = QScrollArea(self) + self.centralScroll.setStyleSheet(listStyle) + + # Controls + # ======== + + self.createButton = QPushButton(self.tr("Create Project"), self) + self.createButton.setIcon(SHARED.theme.getIcon("star")) + + self.cancelButton = QPushButton(self.tr("Go Back"), self) + self.cancelButton.setIcon(SHARED.theme.getIcon("backward")) + self.cancelButton.clicked.connect(lambda: self.cancelNewProject.emit()) + + 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.centralScroll) + self.outerBox.addLayout(self.buttonBox) + self.outerBox.setContentsMargins(0, 0, 0, 0) + + self.setLayout(self.outerBox) + + self._buildNewProjectForm() + return -# END Class _ProjectsPage + def _buildNewProjectForm(self) -> None: + """Generate the new project form.""" + + self.projName = QLineEdit() + self.projName.setMaxLength(200) + self.projName.setPlaceholderText(self.tr("Required")) + + self.projAuthor = QLineEdit() + self.projAuthor.setMaxLength(200) + self.projAuthor.setPlaceholderText(self.tr("Optional")) + + self.projLang = QComboBox(self) + for tag, language in CONFIG.listLanguages(CONFIG.LANG_PROJ): + self.projLang.addItem(language, tag) + + langIdx = self.projLang.findData(CONFIG.guiLocale) + if langIdx == -1: + langIdx = self.projLang.findData("en_GB") + if langIdx != -1: + self.projLang.setCurrentIndex(langIdx) + + self.projPath = QLineEdit("") + self.projPath.setPlaceholderText(self.tr("Required")) + + self.browsePath = QPushButton(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) + + self.projectForm = QFormLayout() + self.projectForm.addRow(self.tr("Project Name"), self.projName) + self.projectForm.addRow(self.tr("Author(s)"), self.projAuthor) + self.projectForm.addRow(self.tr("Language"), self.projLang) + self.projectForm.addRow(self.tr("Project Path"), self.pathBox) + # self.projectForm.setVerticalSpacing(fS) + + # Assemble + # ======== + + self.newForm = QVBoxLayout() + self.newForm.addLayout(self.projectForm) + + self.centralScroll.setLayout(self.newForm) + + return + + ## + # Private Slots + ## + + @pyqtSlot() + def _doBrowse(self) -> None: + """Select a project folder.""" + lastPath = CONFIG.lastPath() + projDir = Path(QFileDialog.getExistingDirectory( + self, self.tr("Select Project Folder"), str(lastPath), options=QFileDialog.ShowDirsOnly + )) + if projDir and (projName := makeFileNameSafe(self.projName.text())): + self.projPath.setText(str(projDir / projName)) + elif projDir: + self.projPath.setText(str(projDir)) + else: + self.projPath.setText("") + return + +# END Class _NewProjectPage