diff --git a/nw/config.py b/nw/config.py
index 9c232109..b5fb1ff8 100644
--- a/nw/config.py
+++ b/nw/config.py
@@ -95,14 +95,15 @@ class Config:
self.lastNotes = "" # The latest release notes that have been shown
## Sizes
- self.winGeometry = [1200, 650]
- self.treeColWidth = [200, 50, 30]
- self.projColWidth = [200, 60, 140]
- self.mainPanePos = [300, 800]
- self.docPanePos = [400, 400]
- self.viewPanePos = [500, 150]
- self.outlnPanePos = [500, 150]
- self.isFullScreen = False
+ self.winGeometry = [1200, 650]
+ self.treeColWidth = [200, 50, 30]
+ self.novelColWidth = [200, 50]
+ self.projColWidth = [200, 60, 140]
+ self.mainPanePos = [300, 800]
+ self.docPanePos = [400, 400]
+ self.viewPanePos = [500, 150]
+ self.outlnPanePos = [500, 150]
+ self.isFullScreen = False
## Features
self.hideVScroll = False # Hide vertical scroll bars on main widgets
@@ -395,6 +396,9 @@ class Config:
self.treeColWidth = self._parseLine(
cnfParse, cnfSec, "treecols", self.CNF_I_LST, self.treeColWidth
)
+ self.novelColWidth = self._parseLine(
+ cnfParse, cnfSec, "novelcols", self.CNF_I_LST, self.novelColWidth
+ )
self.projColWidth = self._parseLine(
cnfParse, cnfSec, "projcols", self.CNF_I_LST, self.projColWidth
)
@@ -597,6 +601,7 @@ class Config:
cnfParse.add_section(cnfSec)
cnfParse.set(cnfSec, "geometry", self._packList(self.winGeometry))
cnfParse.set(cnfSec, "treecols", self._packList(self.treeColWidth))
+ cnfParse.set(cnfSec, "novelcols", self._packList(self.novelColWidth))
cnfParse.set(cnfSec, "projcols", self._packList(self.projColWidth))
cnfParse.set(cnfSec, "mainpane", self._packList(self.mainPanePos))
cnfParse.set(cnfSec, "docpane", self._packList(self.docPanePos))
@@ -816,6 +821,11 @@ class Config:
self.confChanged = True
return True
+ def setNovelColWidths(self, colWidths):
+ self.novelColWidth = [int(x/self.guiScale) for x in colWidths]
+ self.confChanged = True
+ return True
+
def setProjColWidths(self, colWidths):
self.projColWidth = [int(x/self.guiScale) for x in colWidths]
self.confChanged = True
@@ -872,6 +882,9 @@ class Config:
def getTreeColWidths(self):
return [int(x*self.guiScale) for x in self.treeColWidth]
+ def getNovelColWidths(self):
+ return [int(x*self.guiScale) for x in self.novelColWidth]
+
def getProjColWidths(self):
return [int(x*self.guiScale) for x in self.projColWidth]
diff --git a/nw/gui/__init__.py b/nw/gui/__init__.py
index d40d84fc..6b71867d 100644
--- a/nw/gui/__init__.py
+++ b/nw/gui/__init__.py
@@ -9,6 +9,7 @@ from nw.gui.docviewer import GuiDocViewer, GuiDocViewDetails
from nw.gui.itemdetails import GuiItemDetails
from nw.gui.itemeditor import GuiItemEditor
from nw.gui.mainmenu import GuiMainMenu
+from nw.gui.noveltree import GuiNovelTree
from nw.gui.outline import GuiOutline
from nw.gui.outlinedetails import GuiOutlineDetails
from nw.gui.preferences import GuiPreferences
@@ -31,6 +32,7 @@ __all__ = [
"GuiItemDetails",
"GuiItemEditor",
"GuiMainMenu",
+ "GuiNovelTree",
"GuiMainStatus",
"GuiOutline",
"GuiOutlineDetails",
diff --git a/nw/gui/noveltree.py b/nw/gui/noveltree.py
new file mode 100644
index 00000000..35dd6632
--- /dev/null
+++ b/nw/gui/noveltree.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+"""novelWriter GUI Novel Tree
+
+ novelWriter – GUI Novel Tree
+==============================
+ Class holding the project's novel files tree view
+
+ File History:
+ Created: 2020-12-20 [1.1a0]
+
+ This file is a part of novelWriter
+ Copyright 2018–2020, Veronica Berglyd Olsen
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
+import nw
+import logging
+
+from PyQt5.QtCore import Qt, QSize
+from PyQt5.QtWidgets import QTreeWidget
+
+logger = logging.getLogger(__name__)
+
+class GuiNovelTree(QTreeWidget):
+
+ C_TITLE = 0
+ C_WORDS = 1
+ C_PAGES = 2
+
+ def __init__(self, theParent):
+ QTreeWidget.__init__(self, theParent)
+
+ logger.debug("Initialising GuiNovelTree ...")
+
+ self.mainConf = nw.CONFIG
+ self.theParent = theParent
+ self.theTheme = theParent.theTheme
+ self.theProject = theParent.theProject
+ self.theIndex = theParent.theIndex
+
+ # Build GUI
+ iPx = self.theTheme.baseIconSize
+ self.setIconSize(QSize(iPx, iPx))
+ self.setExpandsOnDoubleClick(True)
+ self.setIndentation(iPx)
+ self.setColumnCount(3)
+ self.setHeaderLabels(["Title", "Words", "Pages"])
+
+ # Get user's column width preferences for NAME and COUNT
+ treeColWidth = self.mainConf.getNovelColWidths()
+ if len(treeColWidth) <= 3:
+ for colN, colW in enumerate(treeColWidth):
+ self.setColumnWidth(colN, colW)
+
+ # The last column should just auto-scale
+ self.resizeColumnToContents(self.C_PAGES)
+
+ # Set custom settings
+ self.initTree()
+
+ logger.debug("GuiNovelTree initialisation complete")
+
+ # Internal Mapping
+ self.makeAlert = self.theParent.makeAlert
+
+ return
+
+ def initTree(self):
+ """Set or update tree widget settings.
+ """
+ # Scroll bars
+ if self.mainConf.hideVScroll:
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ else:
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+
+ if self.mainConf.hideHScroll:
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ else:
+ self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+
+ return
+
+ ##
+ # Class Methods
+ ##
+
+ def clearTree(self):
+ """Clear the GUI content and the related maps.
+ """
+ self.clear()
+ return
+
+ def getColumnSizes(self):
+ """Return the column widths for the tree columns.
+ """
+ retVals = [
+ self.columnWidth(self.C_TITLE),
+ self.columnWidth(self.C_WORDS),
+ ]
+ return retVals
+
+ ##
+ # Slots
+ ##
+
+# END Class GuiNovelTree
diff --git a/nw/gui/projtree.py b/nw/gui/projtree.py
index 2c52c4cb..d9a03c04 100644
--- a/nw/gui/projtree.py
+++ b/nw/gui/projtree.py
@@ -357,9 +357,9 @@ class GuiProjectTree(QTreeWidget):
"""Return the column widths for the tree columns.
"""
retVals = [
- self.columnWidth(0),
- self.columnWidth(1),
- self.columnWidth(2),
+ self.columnWidth(self.C_NAME),
+ self.columnWidth(self.C_COUNT),
+ self.columnWidth(self.C_EXPORT),
]
return retVals
diff --git a/nw/guimain.py b/nw/guimain.py
index 4361ae8f..82a7310a 100644
--- a/nw/guimain.py
+++ b/nw/guimain.py
@@ -42,9 +42,9 @@ from PyQt5.QtWidgets import (
from nw.gui import (
GuiAbout, GuiBuildNovel, GuiDocEditor, GuiDocMerge, GuiDocSplit,
GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiItemEditor,
- GuiMainMenu, GuiMainStatus, GuiOutline, GuiOutlineDetails, GuiPreferences,
- GuiProjectLoad, GuiProjectSettings, GuiProjectTree, GuiProjectWizard,
- GuiTheme, GuiWritingStats
+ GuiMainMenu, GuiMainStatus, GuiNovelTree, GuiOutline, GuiOutlineDetails,
+ GuiPreferences, GuiProjectLoad, GuiProjectSettings, GuiProjectTree,
+ GuiProjectWizard, GuiTheme, GuiWritingStats
)
from nw.core import NWProject, NWDoc, NWIndex
from nw.constants import nwItemType, nwItemClass, nwAlert
@@ -99,6 +99,7 @@ class GuiMain(QMainWindow):
# Main GUI Elements
self.statusBar = GuiMainStatus(self)
self.treeView = GuiProjectTree(self)
+ self.novelView = GuiNovelTree(self)
self.docEditor = GuiDocEditor(self)
self.viewMeta = GuiDocViewDetails(self)
self.docViewer = GuiDocViewer(self)
@@ -111,11 +112,23 @@ class GuiMain(QMainWindow):
self.statusIcons = []
self.importIcons = []
+ # Project Tabs : Project / Novel
+ self.projTabs = QTabWidget()
+ self.projTabs.setTabPosition(QTabWidget.South)
+ self.projTabs.setStyleSheet("QTabWidget::pane {border: 0;};")
+ self.projTabs.addTab(self.treeView, "Project")
+ self.projTabs.addTab(self.novelView, "Novel")
+
+ tabFont = self.projTabs.tabBar().font()
+ tabFont.setPointSize(round(0.9*self.theTheme.fontPointSize))
+ self.projTabs.tabBar().setFont(tabFont)
+
# Project Tree View
self.treePane = QWidget()
self.treeBox = QVBoxLayout()
self.treeBox.setContentsMargins(0, 0, 0, 0)
- self.treeBox.addWidget(self.treeView)
+ self.treeBox.setSpacing(0)
+ self.treeBox.addWidget(self.projTabs)
self.treeBox.addWidget(self.treeMeta)
self.treePane.setLayout(self.treeBox)
@@ -136,31 +149,31 @@ class GuiMain(QMainWindow):
self.splitOutline.addWidget(self.projMeta)
self.splitOutline.setSizes(self.mainConf.getOutlinePanePos())
- # Main Tabs : Edirot / Outline
- self.tabWidget = QTabWidget()
- self.tabWidget.setTabPosition(QTabWidget.East)
- self.tabWidget.setStyleSheet("QTabWidget::pane {border: 0;}")
- self.tabWidget.addTab(self.splitDocs, "Editor")
- self.tabWidget.addTab(self.splitOutline, "Outline")
- self.tabWidget.currentChanged.connect(self._mainTabChanged)
+ # Main Tabs : Editor / Outline
+ self.mainTabs = QTabWidget()
+ self.mainTabs.setTabPosition(QTabWidget.East)
+ self.mainTabs.setStyleSheet("QTabWidget::pane {border: 0;}")
+ self.mainTabs.addTab(self.splitDocs, "Editor")
+ self.mainTabs.addTab(self.splitOutline, "Outline")
+ self.mainTabs.currentChanged.connect(self._mainTabChanged)
# Splitter : Project Tree / Main Tabs
xCM = self.mainConf.pxInt(4)
self.splitMain = QSplitter(Qt.Horizontal)
self.splitMain.setContentsMargins(xCM, xCM, xCM, xCM)
self.splitMain.addWidget(self.treePane)
- self.splitMain.addWidget(self.tabWidget)
+ self.splitMain.addWidget(self.mainTabs)
self.splitMain.setSizes(self.mainConf.getMainPanePos())
# Indices of All Splitter Widgets
self.idxTree = self.splitMain.indexOf(self.treePane)
- self.idxMain = self.splitMain.indexOf(self.tabWidget)
+ self.idxMain = self.splitMain.indexOf(self.mainTabs)
self.idxEditor = self.splitDocs.indexOf(self.docEditor)
self.idxViewer = self.splitDocs.indexOf(self.splitView)
self.idxViewDoc = self.splitView.indexOf(self.docViewer)
self.idxViewMeta = self.splitView.indexOf(self.viewMeta)
- self.idxTabEdit = self.tabWidget.indexOf(self.splitDocs)
- self.idxTabProj = self.tabWidget.indexOf(self.splitOutline)
+ self.idxTabEdit = self.mainTabs.indexOf(self.splitDocs)
+ self.idxTabProj = self.mainTabs.indexOf(self.splitOutline)
# Splitter Behaviour
self.splitMain.setCollapsible(self.idxTree, False)
@@ -355,7 +368,7 @@ class GuiMain(QMainWindow):
self.theIndex.clearIndex()
self.clearGUI()
self.hasProject = False
- self.tabWidget.setCurrentWidget(self.splitDocs)
+ self.mainTabs.setCurrentWidget(self.splitDocs)
return saveOK
@@ -371,7 +384,7 @@ class GuiMain(QMainWindow):
return False
# Switch main tab to editor view
- self.tabWidget.setCurrentWidget(self.splitDocs)
+ self.mainTabs.setCurrentWidget(self.splitDocs)
# Try to open the project
if not self.theProject.openProject(projFile):
@@ -490,7 +503,7 @@ class GuiMain(QMainWindow):
return False
self.closeDocument()
- self.tabWidget.setCurrentWidget(self.splitDocs)
+ self.mainTabs.setCurrentWidget(self.splitDocs)
if self.docEditor.loadText(tHandle, tLine):
if changeFocus:
self.docEditor.setFocus()
@@ -575,7 +588,7 @@ class GuiMain(QMainWindow):
return False
# Make sure main tab is in Editor view
- self.tabWidget.setCurrentWidget(self.splitDocs)
+ self.mainTabs.setCurrentWidget(self.splitDocs)
logger.debug("Viewing document with handle %s" % tHandle)
if self.docViewer.loadText(tHandle):
@@ -797,7 +810,7 @@ class GuiMain(QMainWindow):
return False
logger.verbose("Forcing a rebuild of the Project Outline")
- self.tabWidget.setCurrentWidget(self.splitOutline)
+ self.mainTabs.setCurrentWidget(self.splitOutline)
self.projView.refreshTree(overRide=True)
return True
@@ -1022,6 +1035,7 @@ class GuiMain(QMainWindow):
self.mainConf.setShowRefPanel(self.viewMeta.isVisible())
self.mainConf.setTreeColWidths(self.treeView.getColumnSizes())
+ self.mainConf.setNovelColWidths(self.novelView.getColumnSizes())
if not self.mainConf.isFullScreen:
self.mainConf.setWinSize(self.width(), self.height())
@@ -1078,7 +1092,7 @@ class GuiMain(QMainWindow):
self.mainMenu.aFocusMode.setChecked(self.isFocusMode)
if self.isFocusMode:
logger.debug("Activating Focus Mode")
- self.tabWidget.setCurrentWidget(self.splitDocs)
+ self.mainTabs.setCurrentWidget(self.splitDocs)
else:
logger.debug("Deactivating Focus Mode")
@@ -1086,7 +1100,7 @@ class GuiMain(QMainWindow):
self.treePane.setVisible(isVisible)
self.statusBar.setVisible(isVisible)
self.mainMenu.setVisible(isVisible)
- self.tabWidget.tabBar().setVisible(isVisible)
+ self.mainTabs.tabBar().setVisible(isVisible)
hideDocFooter = self.isFocusMode and self.mainConf.hideFocusFooter
self.docEditor.docFooter.setVisible(not hideDocFooter)
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index 4a7f45d3..c954192c 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,13 +1,13 @@
-
+
Sample Project
Sample Project
Jane Smith
Jay Doh
- 810
- 153
- 39261
+ 836
+ 155
+ 39747
False
diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf
index 5ff6f852..27f2062d 100644
--- a/tests/reference/baseConfig_novelwriter.conf
+++ b/tests/reference/baseConfig_novelwriter.conf
@@ -11,6 +11,7 @@ lastnotes = 1.0
[Sizes]
geometry = 1200, 650
treecols = 200, 50, 30
+novelcols = 200, 50
projcols = 200, 60, 140
mainpane = 300, 800
docpane = 400, 400
diff --git a/tests/reference/guiPreferences_novelwriter.conf b/tests/reference/guiPreferences_novelwriter.conf
index cdbc5337..5b10156f 100644
--- a/tests/reference/guiPreferences_novelwriter.conf
+++ b/tests/reference/guiPreferences_novelwriter.conf
@@ -11,6 +11,7 @@ lastnotes = 1.0
[Sizes]
geometry = 1100, 650
treecols = 120, 30, 50
+novelcols = 200, 50
projcols = 140, 55, 140
mainpane = 300, 800
docpane = 400, 400
diff --git a/tests/test_base_config.py b/tests/test_base_config.py
index fe755646..bc9dd161 100644
--- a/tests/test_base_config.py
+++ b/tests/test_base_config.py
@@ -304,6 +304,19 @@ def testBaseConfig_SettersGetters(tmpConf, tmpDir, outDir, refDir):
assert tmpConf.setTreeColWidths([200, 50, 30])
+ # Novel Tree Columns
+ tmpConf.guiScale = 2.0
+ assert tmpConf.setNovelColWidths([10, 20])
+ assert tmpConf.getNovelColWidths() == [10, 20]
+ assert tmpConf.novelColWidth == [5, 10]
+
+ tmpConf.guiScale = 1.0
+ assert tmpConf.setNovelColWidths([10, 20])
+ assert tmpConf.getNovelColWidths() == [10, 20]
+ assert tmpConf.novelColWidth == [10, 20]
+
+ assert tmpConf.setNovelColWidths([200, 50])
+
# Project Settings Tree Columns
tmpConf.guiScale = 2.0
assert tmpConf.setProjColWidths([10, 20, 30])
diff --git a/tests/test_gui_outline.py b/tests/test_gui_outline.py
index cf93c111..906481c5 100644
--- a/tests/test_gui_outline.py
+++ b/tests/test_gui_outline.py
@@ -24,7 +24,7 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainConf.lastPath = nwLipsum
nwGUI.rebuildIndex()
- nwGUI.tabWidget.setCurrentIndex(nwGUI.idxTabProj)
+ nwGUI.mainTabs.setCurrentIndex(nwGUI.idxTabProj)
assert nwGUI.projView.topLevelItemCount() > 0
diff --git a/tests/test_gui_preferences.py b/tests/test_gui_preferences.py
index 4c8b17ea..9c76d830 100644
--- a/tests/test_gui_preferences.py
+++ b/tests/test_gui_preferences.py
@@ -220,10 +220,11 @@ def testGuiPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf")
copyfile(projFile, testFile)
ignoreLines = [
- 2, # Timestamp
- 9, # Release Notes
- 12, 13, 14, 15, 16, 17, 18, # Window sizes
- 7, 28, # Fonts (depends on system default)
+ 2, # Timestamp
+ 9, # Release Notes
+ 12, 13, 14, 15, # Window sizes
+ 16, 17, 18, 19, # Window sizes
+ 7, 29, # Fonts (depends on system default)
]
assert cmpFiles(testFile, compFile, ignoreLines)