From 55f7a91eaf0203f6f96a18ec455a5fa5f4d360d3 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 22 Nov 2022 12:33:32 +0100
Subject: [PATCH 1/5] Add a button to open the novel tree root selector
---
novelwriter/gui/components.py | 5 +++--
novelwriter/gui/noveltree.py | 26 +++++++++++++++++---------
novelwriter/gui/outline.py | 2 +-
3 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/novelwriter/gui/components.py b/novelwriter/gui/components.py
index 35234d40..57489cf6 100644
--- a/novelwriter/gui/components.py
+++ b/novelwriter/gui/components.py
@@ -40,11 +40,12 @@ class NovelSelector(QComboBox):
novelSelectionChanged = pyqtSignal(str)
- def __init__(self, parent, project, theme):
+ def __init__(self, parent, project, mainGui):
super().__init__(parent=parent)
+ self._mainGui = mainGui
self._project = project
- self._theme = theme
+ self._theme = mainGui.mainTheme
self._blockSignal = False
self.currentIndexChanged.connect(self._indexChanged)
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index d7b38a21..740a7819 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -191,6 +191,7 @@ class GuiNovelToolBar(QWidget):
self.mainConf = novelwriter.CONFIG
self.novelView = novelView
+ self.mainGui = novelView.mainGui
self.theProject = novelView.mainGui.theProject
self.mainTheme = novelView.mainGui.mainTheme
@@ -204,12 +205,17 @@ class GuiNovelToolBar(QWidget):
selFont = self.font()
selFont.setWeight(QFont.Bold)
self.novelPrefix = self.tr("Outline of {0}")
- self.novelValue = NovelSelector(self, self.theProject, self.mainTheme)
+ self.novelValue = NovelSelector(self, self.theProject, self.mainGui)
self.novelValue.setFont(selFont)
self.novelValue.setMinimumWidth(self.mainConf.pxInt(150))
self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)
+ self.tbNovel = QToolButton(self)
+ self.tbNovel.setToolTip(self.tr("Novel Root"))
+ self.tbNovel.setIconSize(QSize(iPx, iPx))
+ self.tbNovel.clicked.connect(self._openNovelSelector)
+
# Refresh Button
self.tbRefresh = QToolButton(self)
self.tbRefresh.setToolTip(self.tr("Refresh"))
@@ -236,6 +242,7 @@ class GuiNovelToolBar(QWidget):
# Assemble
self.outerBox = QHBoxLayout()
self.outerBox.addWidget(self.novelValue)
+ self.outerBox.addWidget(self.tbNovel)
self.outerBox.addWidget(self.tbRefresh)
self.outerBox.addWidget(self.tbMore)
self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
@@ -257,6 +264,7 @@ class GuiNovelToolBar(QWidget):
"""Update theme elements.
"""
# Icons
+ self.tbNovel.setIcon(self.mainTheme.getIcon("cls_novel"))
self.tbRefresh.setIcon(self.mainTheme.getIcon("refresh"))
self.tbMore.setIcon(self.mainTheme.getIcon("menu"))
@@ -271,6 +279,7 @@ class GuiNovelToolBar(QWidget):
"QToolButton:hover {{border: none; background: rgba({1},{2},{3},0.2);}}"
).format(self.mainConf.pxInt(2), fadeCol.red(), fadeCol.green(), fadeCol.blue())
+ self.tbNovel.setStyleSheet(buttonStyle)
self.tbRefresh.setStyleSheet(buttonStyle)
self.tbMore.setStyleSheet(buttonStyle)
@@ -279,10 +288,7 @@ class GuiNovelToolBar(QWidget):
"QComboBox::drop-down {border-style: none}"
)
self.novelValue.updateList(prefix=self.novelPrefix)
- if self.novelValue.count() > 1:
- self.novelValue.setToolTip(self.tr("Click to change root folder"))
- else:
- self.novelValue.setToolTip("")
+ self.tbNovel.setVisible(self.novelValue.count() > 1)
return
@@ -297,10 +303,7 @@ class GuiNovelToolBar(QWidget):
"""Build the novel root menu.
"""
self.novelValue.updateList(prefix=self.novelPrefix)
- if self.novelValue.count() > 1:
- self.novelValue.setToolTip(self.tr("Click to change root folder"))
- else:
- self.novelValue.setToolTip("")
+ self.tbNovel.setVisible(self.novelValue.count() > 1)
return
def setCurrentRoot(self, rootHandle):
@@ -321,6 +324,11 @@ class GuiNovelToolBar(QWidget):
# Private Slots
##
+ @pyqtSlot()
+ def _openNovelSelector(self):
+ self.novelValue.showPopup()
+ return
+
@pyqtSlot()
def _refreshNovelTree(self):
"""Rebuild the current tree.
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index 9cfd3d86..16e8c0fe 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -234,7 +234,7 @@ class GuiOutlineToolBar(QToolBar):
self.novelLabel = QLabel(self.tr("Outline of"))
self.novelLabel.setContentsMargins(0, 0, mPx, 0)
- self.novelValue = NovelSelector(self, self.theProject, self.mainTheme)
+ self.novelValue = NovelSelector(self, self.theProject, self.mainGui)
self.novelValue.setMinimumWidth(self.mainConf.pxInt(200))
self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
From f26a745d0e059e98c26213d69c82e90ac19f57c2 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 22 Nov 2022 12:45:10 +0100
Subject: [PATCH 2/5] Use novel selector for project details
---
novelwriter/dialogs/projdetails.py | 42 ++++++++++--------------------
novelwriter/gui/components.py | 17 ++++++++++++
novelwriter/gui/noveltree.py | 2 ++
3 files changed, 33 insertions(+), 28 deletions(-)
diff --git a/novelwriter/dialogs/projdetails.py b/novelwriter/dialogs/projdetails.py
index 9f3999a4..e41f5400 100644
--- a/novelwriter/dialogs/projdetails.py
+++ b/novelwriter/dialogs/projdetails.py
@@ -30,15 +30,14 @@ import novelwriter
from PyQt5.QtCore import Qt, QSize, pyqtSlot
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
- QAbstractItemView, QComboBox, QDialogButtonBox, QGridLayout, QHBoxLayout,
- QLabel, QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
- QWidget
+ QAbstractItemView, QDialogButtonBox, QGridLayout, QHBoxLayout, QLabel,
+ QLineEdit, QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget
)
-from novelwriter.enum import nwItemClass
from novelwriter.common import numberToRoman
from novelwriter.custom import PagedDialog, QSwitch
-from novelwriter.constants import nwLabels, nwUnicode
+from novelwriter.constants import nwUnicode
+from novelwriter.gui.components import NovelSelector
logger = logging.getLogger(__name__)
@@ -297,9 +296,9 @@ class GuiProjectDetailsContents(QWidget):
self.tocLabel = QLabel("%s" % self.tr("Table of Contents"))
- self.novelValue = QComboBox(self)
+ self.novelValue = NovelSelector(self, self.theProject, self.mainGui)
self.novelValue.setMinimumWidth(self.mainConf.pxInt(200))
- self.novelValue.currentIndexChanged.connect(self._novelValueChanged)
+ self.novelValue.novelSelectionChanged.connect(self._novelValueChanged)
self.headBox = QHBoxLayout()
self.headBox.addWidget(self.tocLabel)
@@ -431,10 +430,9 @@ class GuiProjectDetailsContents(QWidget):
"""Populate the tree.
"""
self._currentRoot = None
- self._populateNovelList()
-
- rootHandle = self.novelValue.currentData()
- self._prepareData(rootHandle)
+ self.novelValue.updateList()
+ self.novelValue.setHandle(self.novelValue.firstHandle)
+ self._prepareData(self.novelValue.firstHandle)
self._populateTree()
return
@@ -443,17 +441,6 @@ class GuiProjectDetailsContents(QWidget):
# Internal Functions
##
- def _populateNovelList(self):
- """Fill the novel combo box with a list of all novel folders.
- """
- self.novelValue.clear()
-
- tIcon = self.mainTheme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
- for tHandle, nwItem in self.theProject.tree.iterRoots(nwItemClass.NOVEL):
- self.novelValue.addItem(tIcon, nwItem.itemName, tHandle)
-
- return
-
def _prepareData(self, rootHandle):
"""Extract the information from the project index.
"""
@@ -466,15 +453,14 @@ class GuiProjectDetailsContents(QWidget):
# Slots
##
- @pyqtSlot()
- def _novelValueChanged(self):
+ @pyqtSlot(str)
+ def _novelValueChanged(self, tHandle):
"""Refresh the tree with another root item.
"""
- rootHandle = self.novelValue.currentData()
- if rootHandle != self._currentRoot:
- self._prepareData(rootHandle)
+ if tHandle != self._currentRoot:
+ self._prepareData(tHandle)
self._populateTree()
- self._currentRoot = rootHandle
+ self._currentRoot = tHandle
return
@pyqtSlot()
diff --git a/novelwriter/gui/components.py b/novelwriter/gui/components.py
index 57489cf6..2d5b4501 100644
--- a/novelwriter/gui/components.py
+++ b/novelwriter/gui/components.py
@@ -47,14 +47,28 @@ class NovelSelector(QComboBox):
self._project = project
self._theme = mainGui.mainTheme
self._blockSignal = False
+ self._firstHandle = None
+
self.currentIndexChanged.connect(self._indexChanged)
return
+ ##
+ # Properties
+ ##
+
@property
def handle(self):
return self.currentData()
+ @property
+ def firstHandle(self):
+ return self._firstHandle
+
+ ##
+ # Methods
+ ##
+
def setHandle(self, tHandle, blockSignal=True):
"""Set the currently selected handle.
"""
@@ -72,6 +86,7 @@ class NovelSelector(QComboBox):
"""Rebuild the list of novel items.
"""
self._blockSignal = True
+ self._firstHandle = None
self.clear()
icon = self._theme.getIcon(nwLabels.CLASS_ICON[nwItemClass.NOVEL])
@@ -83,6 +98,8 @@ class NovelSelector(QComboBox):
else:
name = nwItem.itemName
self.addItem(icon, nwItem.itemName, tHandle)
+ if self._firstHandle is None:
+ self._firstHandle = tHandle
if includeAll:
self.insertSeparator(self.count())
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index 740a7819..cfd103a5 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -326,6 +326,8 @@ class GuiNovelToolBar(QWidget):
@pyqtSlot()
def _openNovelSelector(self):
+ """Trigger the dropdown list of the novel selector.
+ """
self.novelValue.showPopup()
return
From 3a77228aaa5ab63b9082a027a3bfa0e0fbaeaef4 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 22 Nov 2022 12:54:26 +0100
Subject: [PATCH 3/5] Fix retention of selected item of novel tree in the
details panel
---
novelwriter/gui/noveltree.py | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index cfd103a5..25cdc5f9 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -529,13 +529,14 @@ class GuiNovelTree(QTreeWidget):
return
def getSelectedHandle(self):
- """Get the currently selected handle. If multiple items are
- selected, return the first.
+ """Get the currently selected or active handle. If multiple
+ items are selected, return the first.
"""
- selItem = self.selectedItems()
- if selItem:
- tHandle = selItem[0].data(self.C_TITLE, self.D_HANDLE)
- sTitle = selItem[0].data(self.C_TITLE, self.D_TITLE)
+ selList = self.selectedItems()
+ trItem = selList[0] if selList else self.currentItem()
+ if isinstance(trItem, QTreeWidgetItem):
+ tHandle = trItem.data(self.C_TITLE, self.D_HANDLE)
+ sTitle = trItem.data(self.C_TITLE, self.D_TITLE)
return tHandle, sTitle
return None, None
From 4fa6329eb6d62ac52885d7ac279fbf7a3343b923 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 22 Nov 2022 13:05:05 +0100
Subject: [PATCH 4/5] Fix tests and minor issues
---
novelwriter/dialogs/projdetails.py | 2 +-
novelwriter/gui/components.py | 7 +++----
tests/test_dialogs/test_dlg_projdetails.py | 5 +++++
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/novelwriter/dialogs/projdetails.py b/novelwriter/dialogs/projdetails.py
index e41f5400..00d3d4df 100644
--- a/novelwriter/dialogs/projdetails.py
+++ b/novelwriter/dialogs/projdetails.py
@@ -460,7 +460,7 @@ class GuiProjectDetailsContents(QWidget):
if tHandle != self._currentRoot:
self._prepareData(tHandle)
self._populateTree()
- self._currentRoot = tHandle
+ self._currentRoot = self.novelValue.handle
return
@pyqtSlot()
diff --git a/novelwriter/gui/components.py b/novelwriter/gui/components.py
index 2d5b4501..9b1cf2d4 100644
--- a/novelwriter/gui/components.py
+++ b/novelwriter/gui/components.py
@@ -103,10 +103,7 @@ class NovelSelector(QComboBox):
if includeAll:
self.insertSeparator(self.count())
- if prefix:
- self.addItem(prefix.format(self.tr("All Novel Folders")), "")
- else:
- self.addItem(icon, self.tr("All Novel Folders"), "")
+ self.addItem(icon, self.tr("All Novel Folders"), "")
self.setHandle(handle)
self.setEnabled(self.count() > 1)
@@ -120,6 +117,8 @@ class NovelSelector(QComboBox):
@pyqtSlot(int)
def _indexChanged(self, index):
+ """Re-emit the change of selected novel signal, unless blocked.
+ """
if not self._blockSignal:
self.novelSelectionChanged.emit(self.currentData())
return
diff --git a/tests/test_dialogs/test_dlg_projdetails.py b/tests/test_dialogs/test_dlg_projdetails.py
index e8d6053c..92df7f39 100644
--- a/tests/test_dialogs/test_dlg_projdetails.py
+++ b/tests/test_dialogs/test_dlg_projdetails.py
@@ -93,6 +93,11 @@ def testDlgProjDetails_Dialog(qtbot, nwGUI, nwLipsum):
assert tocTree.topLevelItem(i).text(tocTab.C_PAGES) == thePages[i]
assert tocTree.topLevelItem(i).text(tocTab.C_PAGE) == thePage[i]
+ # Re-populate
+ assert tocTab._currentRoot is None
+ tocTab._novelValueChanged("7a992350f3eb6") # Not a root
+ assert tocTab._currentRoot == "b3643d0f92e32" # The actual novel root
+
# qtbot.stop()
# Clean Up
From 0a5fa0d1904872050c8f240097147be60b2eb0c3 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 22 Nov 2022 13:06:56 +0100
Subject: [PATCH 5/5] Update main language file
---
i18n/nw_base.ts | 145 ++++++++++++++++++++++++------------------------
1 file changed, 74 insertions(+), 71 deletions(-)
diff --git a/i18n/nw_base.ts b/i18n/nw_base.ts
index 310b02dc..ddb5b171 100644
--- a/i18n/nw_base.ts
+++ b/i18n/nw_base.ts
@@ -2063,38 +2063,38 @@
GuiMainStatus
-
-
+
+
None
-
+
Editor
-
+
Project
-
+
Session Time
-
+
Words: {0} ({1})
-
+
Project word count (session change)
-
+
Novel word count (session change)
@@ -2102,47 +2102,47 @@
GuiNovelToolBar
-
- Novel Outline
+
+ Outline of {0}
-
- Refresh
-
-
-
-
+
Novel Root
-
+
+ Refresh
+
+
+
+
Last Column
-
+
Hidden
-
+
Point of View Character
-
+
Focus Character
-
+
Novel Plot
-
+
More Options
@@ -2150,7 +2150,7 @@
GuiNovelTree
-
+
No meta data
@@ -2158,65 +2158,65 @@
GuiOutlineDetails
-
-
-
-
+
+
+
+
Title
-
+
Chapter
-
+
Scene
-
+
Section
-
+
Document
-
+
Status
-
+
Characters
-
+
Words
-
+
Paragraphs
-
+
Synopsis
-
+
Title Details
-
+
Reference Tags
@@ -2224,7 +2224,7 @@
GuiOutlineHeaderMenu
-
+
Select Columns
@@ -2232,20 +2232,15 @@
GuiOutlineToolBar
-
+
Outline of
-
+
Refresh
-
-
- All Novel Folders
-
-
GuiPreferences
@@ -2923,17 +2918,17 @@
GuiProjectDetails
-
+
Project Details
-
+
Overview
-
+
Contents
@@ -2941,72 +2936,72 @@
GuiProjectDetailsContents
-
+
Table of Contents
-
+
Title
-
+
Words
-
+
Pages
-
+
Page
-
+
Progress
-
+
Typical word count for a 5 by 8 inch book page with 11 pt font is 350.
-
+
Start counting page numbers from this page.
-
+
Assume a new chapter or partition always start on an odd numbered page.
-
+
Words per page
-
+
Count pages from
-
+
Clear double pages
-
+
END
-
+
Untitled
@@ -3014,42 +3009,42 @@
GuiProjectDetailsMain
-
+
Working Title: {0}
-
+
By {0}
-
+
Words
-
+
Chapters
-
+
Scenes
-
+
Revisions
-
+
Editing Time
-
+
Path
@@ -4004,6 +3999,14 @@
+
+ NovelSelector
+
+
+ All Novel Folders
+
+
+
ProjWizardCustomPage