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 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.itemmodel import ProjectModel, ProjectNode
from novelwriter.enum import nwChange, nwItemClass, nwItemLayout, nwItemType
@@ -262,6 +262,29 @@ class NWTree:
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:
"""Refresh these items on the GUI. If they are an ordered range,
also set the isRange flag to True.
+38 -36
View File
@@ -275,9 +275,9 @@ class GuiProjectToolBar(QWidget):
# Add Item Menu
self.mAdd = QMenu(self)
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.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
)
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)
)
self.aAddScene = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h3"]))
self.aAddScene.triggered.connect(
qtLambda(self.projTree.newTreeItem, nwItemType.FILE, hLevel=3, isNote=False)
self.aAddPart = qtAddAction(self.mAdd, trConst(nwLabels.ITEM_DESCRIPTION["doc_h1"]))
self.aAddPart.triggered.connect(
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"]))
@@ -366,9 +371,10 @@ class GuiProjectToolBar(QWidget):
self.tbAdd.setThemeIcon("add", "green")
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.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.aAddFolder.setIcon(SHARED.theme.getIcon("prj_folder", "folder"))
@@ -425,9 +431,10 @@ class GuiProjectToolBar(QWidget):
"""
nwItem = SHARED.project.tree[tHandle]
allowDoc = isinstance(nwItem, NWItem) and nwItem.documentAllowed()
self.aAddEmpty.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc)
self.aAddScene.setVisible(allowDoc)
self.aAddChap.setVisible(allowDoc)
self.aAddPart.setVisible(allowDoc)
self.aAddEmpty.setVisible(allowDoc)
return
##
@@ -606,12 +613,12 @@ class GuiProjectTree(QTreeView):
tHandle = None
if itemType == nwItemType.ROOT and isinstance(itemClass, nwItemClass):
pos = -1
sPos = -1
if (node := self._getNode(self.currentIndex())) and (itemRoot := node.item.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()
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."))
return
# Collect some information about the selected item
sLevel = nwStyles.H_LEVEL.get(node.item.mainHeading, 0)
sIsParent = node.childCount() > 0
# Set default label and determine if new item is to be added
# as child or sibling to the selected item
# Set default label and determine where to put the new item
nNote = isNote
nLevel = hLevel
if itemType == nwItemType.FILE:
if copyDoc and (cItem := SHARED.project.tree[copyDoc]):
nNote = cItem.isNoteLayout()
nLevel = nwStyles.H_LEVEL.get(cItem.mainHeading, 0)
newLabel = cItem.itemName
asChild = sIsParent and node.item.isDocumentLayout()
elif isNote:
newLabel = self.tr("New Note")
asChild = sIsParent
elif hLevel == 1:
newLabel = self.tr("New Part")
elif hLevel == 2:
newLabel = self.tr("New Chapter")
asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 2
elif hLevel == 3:
newLabel = self.tr("New Scene")
asChild = sIsParent and node.item.isDocumentLayout() and sLevel < 3
else:
newLabel = self.tr("New Document")
asChild = sIsParent and node.item.isDocumentLayout()
else:
newLabel = self.tr("New Folder")
asChild = False
nLevel = 0
pos = -1
sHandle = None
if not (asChild or node.item.isFolderType() or node.item.isRootType()):
pos = node.row() + 1
sHandle = node.item.itemParent
sHandle, sPos = SHARED.project.tree.pickParent(node, nLevel, nNote)
if not sHandle:
SHARED.error(self.tr("Did not find anywhere to add the file or folder!"))
return
sHandle = sHandle or node.item.itemHandle
newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
if dlgOk:
# Add the file or folder
if itemType == nwItemType.FILE:
if tHandle := SHARED.project.newFile(newLabel, sHandle, pos):
if tHandle := SHARED.project.newFile(newLabel, sHandle, sPos):
if copyDoc:
SHARED.project.copyFileContent(tHandle, copyDoc)
elif hLevel > 0:
SHARED.project.writeNewFile(tHandle, hLevel, not isNote)
SHARED.project.writeNewFile(tHandle, hLevel, not nNote)
SHARED.project.index.reIndexHandle(tHandle)
SHARED.project.tree.refreshItems([tHandle])
else:
tHandle = SHARED.project.newFolder(newLabel, sHandle, pos)
tHandle = SHARED.project.newFolder(newLabel, sHandle, sPos)
# Select the new item automatically
if tHandle:
@@ -1198,9 +1199,10 @@ class _TreeContextMenu(QMenu):
def _itemCreation(self) -> None:
"""Add create item actions."""
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.aAddChap)
menu.addAction(self._view.projBar.aAddPart)
menu.addAction(self._view.projBar.aAddEmpty)
menu.addAction(self._view.projBar.aAddNote)
menu.addAction(self._view.projBar.aAddFolder)
return