Re-enable item selection

This commit is contained in:
Veronica Berglyd Olsen
2024-11-17 22:41:45 +01:00
parent ef42e1956c
commit 7991b9b27c
3 changed files with 152 additions and 81 deletions
+17 -1
View File
@@ -154,13 +154,19 @@ class ProjectNode:
class ProjectModel(QAbstractItemModel): class ProjectModel(QAbstractItemModel):
__slots__ = ("_root", "_map") __slots__ = ("_tree", "_root")
def __init__(self, tree: NWTree) -> None: def __init__(self, tree: NWTree) -> None:
super().__init__(None) super().__init__(None)
logger.debug("Create: ProjectModel")
self._tree = tree
self._root = ProjectNode(NWItem(tree._project, "")) self._root = ProjectNode(NWItem(tree._project, ""))
return return
def __del__(self) -> None:
logger.debug("Delete: ProjectModel")
return
@property @property
def root(self) -> ProjectNode: def root(self) -> ProjectNode:
return self._root return self._root
@@ -220,6 +226,16 @@ class ProjectModel(QAbstractItemModel):
return index.internalPointer() return index.internalPointer()
return None return None
def indexFromHandle(self, handle: str | None) -> QModelIndex:
"""Get the index representing a node in the model."""
if handle and (node := self._tree.nodes.get(handle)):
return self.createIndex(node.row(), 0, node)
return QModelIndex()
def indexFromNode(self, node: ProjectNode) -> QModelIndex:
"""Get the index representing a node in the model."""
return self.createIndex(node.row(), 0, node)
## ##
# Methods # Methods
## ##
+4
View File
@@ -98,6 +98,10 @@ class NWTree:
def model(self) -> ProjectModel: def model(self) -> ProjectModel:
return self._model return self._model
@property
def nodes(self) -> dict[str, ProjectNode]:
return self._nodes
## ##
# Class Methods # Class Methods
## ##
+131 -80
View File
@@ -41,7 +41,7 @@ from novelwriter import CONFIG, SHARED
from novelwriter.common import qtLambda from novelwriter.common import qtLambda
from novelwriter.constants import nwLabels, nwUnicode, trConst from novelwriter.constants import nwLabels, nwUnicode, trConst
from novelwriter.core.item import NWItem from novelwriter.core.item import NWItem
from novelwriter.core.itemmodel import ProjectModel from novelwriter.core.itemmodel import ProjectModel, ProjectNode
from novelwriter.dialogs.projectsettings import GuiProjectSettings from novelwriter.dialogs.projectsettings import GuiProjectSettings
from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType from novelwriter.enum import nwDocMode, nwItemClass, nwItemLayout, nwItemType
from novelwriter.extensions.modified import NIconToolButton from novelwriter.extensions.modified import NIconToolButton
@@ -165,13 +165,12 @@ class GuiProjectView(QWidget):
def saveProjectTasks(self) -> None: def saveProjectTasks(self) -> None:
"""Run save project tasks.""" """Run save project tasks."""
self.projTree.saveTreeOrder() # self.projTree.saveTreeOrder()
return return
def populateTree(self) -> None: def populateTree(self) -> None:
"""Build the tree structure from project data.""" """Build the tree structure from project data."""
self.projTree.loadModel() self.projTree.loadModel()
# self.projTree.buildTree()
return return
def setTreeFocus(self) -> None: def setTreeFocus(self) -> None:
@@ -562,8 +561,6 @@ class GuiProjectTree(QTreeView):
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
# Connect signals # Connect signals
# self.itemDoubleClicked.connect(self._treeDoubleClick)
# self.itemSelectionChanged.connect(self._treeSelectionChange)
self.clicked.connect(self._treeSingleClick) self.clicked.connect(self._treeSingleClick)
self.doubleClicked.connect(self._treeDoubleClick) self.doubleClicked.connect(self._treeDoubleClick)
@@ -595,10 +592,26 @@ class GuiProjectTree(QTreeView):
return return
## ##
# Class Methods # External Methods
## ##
def getSelectedHandle(self) -> str | None:
"""Get the currently selected handle."""
if (indexes := self.selectedIndexes()) and (node := self._getNode(indexes[0])):
return node.item.itemHandle
return None
##
# Module Internal Methods
##
def clearTree(self) -> None:
"""Clear the tree view."""
self.setModel(None)
return
def loadModel(self) -> None: def loadModel(self) -> None:
"""Load and prepare a new project model."""
self.setModel(SHARED.project.tree.model) self.setModel(SHARED.project.tree.model)
# Lock the column sizes # Lock the column sizes
@@ -622,12 +635,64 @@ class GuiProjectTree(QTreeView):
return return
def clearTree(self) -> None: def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
"""Clear the GUI content and the related map.""" """Set a specific handle as the selected item."""
# self.clear() if (model := self._getModel()) and (index := model.indexFromHandle(tHandle)).isValid():
# self._treeMap = {} self.setCurrentIndex(index)
if doScroll:
self.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtCenter)
self.projView.selectedItemChanged.emit(tHandle)
return return
##
# Private Slots
##
##
# Private Slots
##
@pyqtSlot(QModelIndex)
def _treeSingleClick(self, index: QModelIndex) -> None:
"""The user changed which item is selected."""
if node := self._getNode(index):
self.projView.selectedItemChanged.emit(node.item.itemHandle)
return
@pyqtSlot(QModelIndex)
def _treeDoubleClick(self, index: QModelIndex) -> None:
"""Capture a double-click event and either request the document
for editing if it is a file, or expand/close the node if not.
"""
if node := self._getNode(index):
if node.item.isFileType():
self.projView.openDocumentRequest.emit(
node.item.itemHandle, nwDocMode.EDIT, "", True
)
else:
self.setExpanded(index, not self.isExpanded(index))
return
##
# Internal Functions
##
def _getModel(self) -> ProjectModel | None:
"""Return a project node corresponding to a model index."""
if isinstance(model := self.model(), ProjectModel):
return model
return None
def _getNode(self, index: QModelIndex) -> ProjectNode | None:
"""Return a project node corresponding to a model index."""
if isinstance(model := self.model(), ProjectModel) and (node := model.node(index)):
return node
return None
# =========================================================================================== #
# Old Code
# =========================================================================================== #
def createNewNote(self, tag: str, itemClass: nwItemClass) -> None: def createNewNote(self, tag: str, itemClass: nwItemClass) -> None:
"""Create a new note. This function is used by the document """Create a new note. This function is used by the document
editor to create note files for unknown tags. editor to create note files for unknown tags.
@@ -838,20 +903,20 @@ class GuiProjectTree(QTreeView):
# self._alertTreeChange(tHandle, flush=False) # self._alertTreeChange(tHandle, flush=False)
return return
def saveTreeOrder(self) -> None: # def saveTreeOrder(self) -> None:
"""Build a list of the items in the project tree and send them # """Build a list of the items in the project tree and send them
to the project class. This syncs up the two versions of the # to the project class. This syncs up the two versions of the
project structure, and must be called before any code that # project structure, and must be called before any code that
depends on this order to be up to date. # depends on this order to be up to date.
""" # """
# items = [] # # items = []
# for i in range(self.topLevelItemCount()): # # for i in range(self.topLevelItemCount()):
# item = self.topLevelItem(i) # # item = self.topLevelItem(i)
# if isinstance(item, QTreeWidgetItem): # # if isinstance(item, QTreeWidgetItem):
# items = self._scanChildren(items, item, i) # # items = self._scanChildren(items, item, i)
# logger.debug("Saving project tree item order") # # logger.debug("Saving project tree item order")
# SHARED.project.setTreeOrder(items) # # SHARED.project.setTreeOrder(items)
return # return
def getTreeFromHandle(self, tHandle: str) -> list[str]: def getTreeFromHandle(self, tHandle: str) -> list[str]:
"""Recursively return all the child items starting from a given """Recursively return all the child items starting from a given
@@ -1140,35 +1205,21 @@ class GuiProjectTree(QTreeView):
return return
def buildTree(self) -> None: # def getSelectedHandle(self) -> str | None:
"""Build the entire project tree from scratch. This depends on # """Get the currently selected handle. If multiple items are
the save project item iterator in the project class which will # selected, return the first.
always make sure items with a parent have had their parent item # """
sent first. # # if items := self.selectedItems():
""" # # return items[0].data(self.C_DATA, self.D_HANDLE)
# logger.debug("Building the project tree ...") # return None
# self.clearTree()
# for nwItem in SHARED.project.iterProjectItems():
# self._addTreeItem(nwItem)
# self.setActiveHandle(self._actHandle)
# logger.info("%d item(s) added to the project tree", len(self._treeMap))
return
def getSelectedHandle(self) -> str | None: # def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
"""Get the currently selected handle. If multiple items are # """Set a specific handle as the selected item."""
selected, return the first. # # if tHandle in self._treeMap:
""" # # self.setCurrentItem(self._treeMap[tHandle])
# if items := self.selectedItems(): # # if (indexes := self.selectedIndexes()) and doScroll:
# return items[0].data(self.C_DATA, self.D_HANDLE) # # self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter)
return None # return
def setSelectedHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
"""Set a specific handle as the selected item."""
# if tHandle in self._treeMap:
# self.setCurrentItem(self._treeMap[tHandle])
# if (indexes := self.selectedIndexes()) and doScroll:
# self.scrollTo(indexes[0], QAbstractItemView.ScrollHint.PositionAtCenter)
return
def setActiveHandle(self, tHandle: str | None) -> None: def setActiveHandle(self, tHandle: str | None) -> None:
"""Highlight the rows associated with a given handle.""" """Highlight the rows associated with a given handle."""
@@ -1239,35 +1290,35 @@ class GuiProjectTree(QTreeView):
# Private Slots # Private Slots
## ##
@pyqtSlot(QModelIndex) # @pyqtSlot(QModelIndex)
def _treeSingleClick(self, index: QModelIndex) -> None: # def _treeSingleClick(self, index: QModelIndex) -> None:
"""The user changed which item is selected.""" # """The user changed which item is selected."""
if isinstance(model := self.model(), ProjectModel) and (node := model.node(index)): # if isinstance(model := self.model(), ProjectModel) and (node := model.node(index)):
self.projView.selectedItemChanged.emit(node.item.itemHandle) # self.projView.selectedItemChanged.emit(node.item.itemHandle)
# # When selecting multiple items, don't allow including root # # # When selecting multiple items, don't allow including root
# # items in the selection and instead deselect them # # # items in the selection and instead deselect them
# items = self.selectedItems() # # items = self.selectedItems()
# if items and len(items) > 1: # # if items and len(items) > 1:
# for item in items: # # for item in items:
# if item.parent() is None: # # if item.parent() is None:
# item.setSelected(False) # # item.setSelected(False)
return # return
@pyqtSlot(QModelIndex) # @pyqtSlot(QModelIndex)
def _treeDoubleClick(self, index: QModelIndex) -> None: # def _treeDoubleClick(self, index: QModelIndex) -> None:
"""Capture a double-click event and either request the document # """Capture a double-click event and either request the document
for editing if it is a file, or expand/close the node if not. # for editing if it is a file, or expand/close the node if not.
""" # """
if isinstance(model := self.model(), ProjectModel) and (node := model.node(index)): # if isinstance(model := self.model(), ProjectModel) and (node := model.node(index)):
if node.item.isFileType(): # if node.item.isFileType():
self.projView.openDocumentRequest.emit( # self.projView.openDocumentRequest.emit(
node.item.itemHandle, nwDocMode.EDIT, "", True # node.item.itemHandle, nwDocMode.EDIT, "", True
) # )
else: # else:
self.setExpanded(index, not self.isExpanded(index)) # self.setExpanded(index, not self.isExpanded(index))
return # return
@pyqtSlot() @pyqtSlot()
def _doAutoScroll(self) -> None: def _doAutoScroll(self) -> None:
@@ -2007,7 +2058,7 @@ class _TreeContextMenu(QMenu):
for tItem in self._items: for tItem in self._items:
if tItem.isFileType() and tItem.itemClass != nwItemClass.TRASH: if tItem.isFileType() and tItem.itemClass != nwItemClass.TRASH:
self.projTree.moveItemToTrash(tItem.itemHandle, askFirst=False, flush=False) self.projTree.moveItemToTrash(tItem.itemHandle, askFirst=False, flush=False)
self.projTree.saveTreeOrder() # self.projTree.saveTreeOrder()
return return
@pyqtSlot() @pyqtSlot()
@@ -2017,7 +2068,7 @@ class _TreeContextMenu(QMenu):
for tItem in self._items: for tItem in self._items:
if tItem.isFileType() and tItem.itemClass == nwItemClass.TRASH: if tItem.isFileType() and tItem.itemClass == nwItemClass.TRASH:
self.projTree.permDeleteItem(tItem.itemHandle, askFirst=False, flush=False) self.projTree.permDeleteItem(tItem.itemHandle, askFirst=False, flush=False)
self.projTree.saveTreeOrder() # self.projTree.saveTreeOrder()
return return
@pyqtSlot() @pyqtSlot()