Move the decision logic of where to put new items to the NWTree class and improve (#2260)

This commit is contained in:
Veronica Berglyd Olsen
2025-04-18 17:38:55 +02:00
parent 1522b6b682
commit bec414a405
2 changed files with 62 additions and 37 deletions
+24 -1
View File
@@ -33,7 +33,7 @@ from typing import TYPE_CHECKING, Literal, overload
from PyQt6.QtCore import QModelIndex from PyQt6.QtCore import QModelIndex
from novelwriter import SHARED from novelwriter import SHARED
from novelwriter.constants import nwFiles, nwLabels, trConst from novelwriter.constants import nwFiles, nwLabels, nwStyles, trConst
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.core.itemmodel import ProjectModel, ProjectNode from novelwriter.core.itemmodel import ProjectModel, ProjectNode
from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
@@ -262,6 +262,29 @@ class NWTree:
return return
def pickParent(self, sNode: ProjectNode, hLevel: int, isNote: bool) -> tuple[str | None, int]:
"""Pick an appropriate parent handle for adding a new item."""
if sNode.item.isFolderType() or sNode.item.isRootType():
# Always add as a direct child of folders
return sNode.item.itemHandle, sNode.childCount()
pNode = sNode.parent()
pLevel = nwStyles.H_LEVEL.get(pNode.item.mainHeading, 0) if pNode else 0
sLevel = 0 if isNote else nwStyles.H_LEVEL.get(sNode.item.mainHeading, 0)
if pNode and pNode.item.isFileType() and pLevel == hLevel and sLevel > hLevel:
# If the selected item is deeper level, but the parent is a document
# of the same level, we make it a sibling of the parent (See #2260)
return pNode.item.itemParent, pNode.row() + 1
if sNode.childCount() > 0 and (sLevel < hLevel or isNote):
# If the item already has child nodes and is of a lower level
# or is a note, we make the new item a child
return sNode.item.itemHandle, sNode.childCount()
# The default behaviour is to make the new item a sibling
return sNode.item.itemParent, sNode.row() + 1
def refreshItems(self, items: list[str]) -> None: def refreshItems(self, items: list[str]) -> None:
"""Refresh these items on the GUI. If they are an ordered range, """Refresh these items on the GUI. If they are an ordered range,
also set the isRange flag to True. also set the isRange flag to True.
+38 -36
View File
@@ -275,9 +275,9 @@ class GuiProjectToolBar(QWidget):
# Add Item Menu # Add Item Menu
self.mAdd = QMenu(self) self.mAdd = QMenu(self)
self.aAddEmpty = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["document"])) self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddEmpty.triggered.connect( self.aAddScene.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
) )
self.aAddChap = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"])) self.aAddChap = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h2"]))
@@ -285,9 +285,14 @@ class GuiProjectToolBar(QWidget):
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=2, isNote=False)
) )
self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"])) self.aAddPart = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h1"]))
self.aAddScene.triggered.connect( self.aAddPart.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False) qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=1, isNote=False)
)
self.aAddEmpty = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["document"]))
self.aAddEmpty.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=0, isNote=False)
) )
self.aAddNote = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["note"])) self.aAddNote = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["note"]))
@@ -366,9 +371,10 @@ class GuiProjectToolBar(QWidget):
self.tbAdd.setThemeIcon("add", "green") self.tbAdd.setThemeIcon("add", "green")
self.tbMore.setThemeIcon("more_vertical") self.tbMore.setThemeIcon("more_vertical")
self.aAddEmpty.setIcon(SHARED.theme.getIcon("prj_document", "file"))
self.aAddChap.setIcon(SHARED.theme.getIcon("prj_chapter", "chapter"))
self.aAddScene.setIcon(SHARED.theme.getIcon("prj_scene", "scene")) self.aAddScene.setIcon(SHARED.theme.getIcon("prj_scene", "scene"))
self.aAddChap.setIcon(SHARED.theme.getIcon("prj_chapter", "chapter"))
self.aAddPart.setIcon(SHARED.theme.getIcon("prj_title", "title"))
self.aAddEmpty.setIcon(SHARED.theme.getIcon("prj_document", "file"))
self.aAddNote.setIcon(SHARED.theme.getIcon("prj_note", "note")) self.aAddNote.setIcon(SHARED.theme.getIcon("prj_note", "note"))
self.aAddFolder.setIcon(SHARED.theme.getIcon("prj_folder", "folder")) self.aAddFolder.setIcon(SHARED.theme.getIcon("prj_folder", "folder"))
@@ -425,9 +431,10 @@ class GuiProjectToolBar(QWidget):
""" """
nwItem = SHARED.project.tree[tHandle] nwItem = SHARED.project.tree[tHandle]
allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed() allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
self.aAddEmpty.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc)
self.aAddScene.setVisible(allowDoc) self.aAddScene.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc)
self.aAddPart.setVisible(allowDoc)
self.aAddEmpty.setVisible(allowDoc)
return return
## ##
@@ -606,12 +613,12 @@ class GuiProjectTree(QTreeView):
tHandle = None tHandle = None
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass): if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
pos = -1 sPos = -1
if (node := self._getNode(self.currentIndex())) and (itemRoot := node.item.itemRoot): if (node := self._getNode(self.currentIndex())) and (itemRoot := node.item.itemRoot):
if root := SHARED.project.tree.nodes.get(itemRoot): if root := SHARED.project.tree.nodes.get(itemRoot):
pos = root.row() + 1 sPos = root.row() + 1
tHandle = SHARED.project.newRoot(itemClass, pos) tHandle = SHARED.project.newRoot(itemClass, sPos)
self.restoreExpandedState() self.restoreExpandedState()
elif itemType in (nwItemType.FILE, nwItemType.FOLDER): elif itemType in (nwItemType.FILE, nwItemType.FOLDER):
@@ -624,52 +631,46 @@ class GuiProjectTree(QTreeView):
SHARED.error(self.tr("Cannot add new files or folders to the Trash folder.")) SHARED.error(self.tr("Cannot add new files or folders to the Trash folder."))
return return
# Collect some information about the selected item # Set default label and determine where to put the new item
sLevel = nwStyles.H_LEVEL.get(node.item.mainHeading, 0) nNote = isNote
sIsParent = node.childCount() > 0 nLevel = hLevel
# Set default label and determine if new item is to be added
# as child or sibling to the selected item
if itemType == nwItemType.FILE: if itemType == nwItemType.FILE:
if copyDoc and (cItem := SHARED.project.tree[copyDoc]): if copyDoc and (cItem := SHARED.project.tree[copyDoc]):
nNote = cItem.isNoteLayout()
nLevel = nwStyles.H_LEVEL.get(cItem.mainHeading, 0)
newLabel = cItem.itemName newLabel = cItem.itemName
asChild = sIsParent and node.item.isDocumentLayout()
elif isNote: elif isNote:
newLabel = self.tr("New Note") newLabel = self.tr("New Note")
asChild = sIsParent elif hLevel == 1:
newLabel = self.tr("New Part")
elif hLevel == 2: elif hLevel == 2:
newLabel = self.tr("New Chapter") newLabel = self.tr("New Chapter")
asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 2
elif hLevel == 3: elif hLevel == 3:
newLabel = self.tr("New Scene") newLabel = self.tr("New Scene")
asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 3
else: else:
newLabel = self.tr("New Document") newLabel = self.tr("New Document")
asChild = sIsParent and node.item.isDocumentLayout()
else: else:
newLabel = self.tr("New Folder") newLabel = self.tr("New Folder")
asChild = False nLevel = 0
pos = -1 sHandle, sPos = SHARED.project.tree.pickParent(node, nLevel, nNote)
sHandle = None if not sHandle:
if not (asChild or node.item.isFolderType() or node.item.isRootType()): SHARED.error(self.tr("Did not find anywhere to add the file or folder!"))
pos = node.row() + 1 return
sHandle = node.item.itemParent
sHandle = sHandle or node.item.itemHandle
newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel) newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
if dlgOk: if dlgOk:
# Add the file or folder # Add the file or folder
if itemType == nwItemType.FILE: if itemType == nwItemType.FILE:
if tHandle := SHARED.project.newFile(newLabel, sHandle, pos): if tHandle := SHARED.project.newFile(newLabel, sHandle, sPos):
if copyDoc: if copyDoc:
SHARED.project.copyFileContent(tHandle, copyDoc) SHARED.project.copyFileContent(tHandle, copyDoc)
elif hLevel > 0: elif hLevel > 0:
SHARED.project.writeNewFile(tHandle, hLevel, not isNote) SHARED.project.writeNewFile(tHandle, hLevel, not nNote)
SHARED.project.index.reIndexHandle(tHandle) SHARED.project.index.reIndexHandle(tHandle)
SHARED.project.tree.refreshItems([tHandle]) SHARED.project.tree.refreshItems([tHandle])
else: else:
tHandle = SHARED.project.newFolder(newLabel, sHandle, pos) tHandle = SHARED.project.newFolder(newLabel, sHandle, sPos)
# Select the new item automatically # Select the new item automatically
if tHandle: if tHandle:
@@ -1198,9 +1199,10 @@ class _TreeContextMenu(QMenu):
def _itemCreation(self) -> None: def _itemCreation(self) -> None:
"""Add create item actions.""" """Add create item actions."""
menu = qtAddMenu(self, self.tr("Create New ...")) menu = qtAddMenu(self, self.tr("Create New ..."))
menu.addAction(self._view.projBar.aAddEmpty)
menu.addAction(self._view.projBar.aAddChap)
menu.addAction(self._view.projBar.aAddScene) menu.addAction(self._view.projBar.aAddScene)
menu.addAction(self._view.projBar.aAddChap)
menu.addAction(self._view.projBar.aAddPart)
menu.addAction(self._view.projBar.aAddEmpty)
menu.addAction(self._view.projBar.aAddNote) menu.addAction(self._view.projBar.aAddNote)
menu.addAction(self._view.projBar.aAddFolder) menu.addAction(self._view.projBar.aAddFolder)
return return