Remove tree undo and fix drop on root issue

This commit is contained in:
Veronica Berglyd Olsen
2023-11-21 23:13:12 +01:00
parent f75ed33cec
commit 33a30f3875
2 changed files with 24 additions and 120 deletions
+12 -80
View File
@@ -126,11 +126,6 @@ class GuiProjectView(QWidget):
self.keyGoDown.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyGoDown.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyGoDown.activated.connect(lambda: self.projTree.moveToLevel(1)) self.keyGoDown.activated.connect(lambda: self.projTree.moveToLevel(1))
self.keyUndoMv = QShortcut(self.projTree)
self.keyUndoMv.setKey("Ctrl+Shift+Z")
self.keyUndoMv.setContext(Qt.ShortcutContext.WidgetShortcut)
self.keyUndoMv.activated.connect(lambda: self.projTree.undoLastMove())
self.keyContext = QShortcut(self.projTree) self.keyContext = QShortcut(self.projTree)
self.keyContext.setKey("Ctrl+.") self.keyContext.setKey("Ctrl+.")
self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut) self.keyContext.setContext(Qt.ShortcutContext.WidgetShortcut)
@@ -320,9 +315,6 @@ class GuiProjectToolBar(QWidget):
self.aCollapse = self.mMore.addAction(self.tr("Collapse All")) self.aCollapse = self.mMore.addAction(self.tr("Collapse All"))
self.aCollapse.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, False)) self.aCollapse.triggered.connect(lambda: self.projTree.setExpandedFromHandle(None, False))
self.aMoreUndo = self.mMore.addAction(self.tr("Undo Move"))
self.aMoreUndo.triggered.connect(lambda: self.projTree.undoLastMove())
self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash")) self.aEmptyTrash = self.mMore.addAction(self.tr("Empty Trash"))
self.aEmptyTrash.triggered.connect(lambda: self.projTree.emptyTrash()) self.aEmptyTrash.triggered.connect(lambda: self.projTree.emptyTrash())
@@ -475,7 +467,6 @@ class GuiProjectTree(QTreeWidget):
# Internal Variables # Internal Variables
self._treeMap = {} self._treeMap = {}
self._lastMove = {}
self._timeChanged = 0.0 self._timeChanged = 0.0
self._popAlert = None self._popAlert = None
@@ -522,8 +513,8 @@ class GuiProjectTree(QTreeWidget):
# But don't allow drop on root level # But don't allow drop on root level
# Due to a bug, this stops working somewhere between Qt 5.15.3 # Due to a bug, this stops working somewhere between Qt 5.15.3
# and 5.15.8, so this is also blocked in dropEvent (see #1569) # and 5.15.8, so this is also blocked in dropEvent (see #1569)
# trRoot = self.invisibleRootItem() trRoot = self.invisibleRootItem()
# trRoot.setFlags(trRoot.flags() ^ Qt.ItemFlag.ItemIsDropEnabled) trRoot.setFlags(trRoot.flags() ^ Qt.ItemFlag.ItemIsDropEnabled)
# Cached values # Cached values
self._lblActive = self.tr("Active") self._lblActive = self.tr("Active")
@@ -572,7 +563,6 @@ class GuiProjectTree(QTreeWidget):
"""Clear the GUI content and the related map.""" """Clear the GUI content and the related map."""
self.clear() self.clear()
self._treeMap = {} self._treeMap = {}
self._lastMove = {}
self._timeChanged = 0.0 self._timeChanged = 0.0
return return
@@ -743,7 +733,6 @@ class GuiProjectTree(QTreeWidget):
cItem = pItem.takeChild(tIndex) cItem = pItem.takeChild(tIndex)
pItem.insertChild(nIndex, cItem) pItem.insertChild(nIndex, cItem)
self._recordLastMove(cItem, pItem, tIndex)
self._alertTreeChange(tHandle, flush=True) self._alertTreeChange(tHandle, flush=True)
self.setCurrentItem(tItem) self.setCurrentItem(tItem)
@@ -932,7 +921,6 @@ class GuiProjectTree(QTreeWidget):
trItemT.addChild(trItemC) trItemT.addChild(trItemC)
self._postItemMove(tHandle) self._postItemMove(tHandle)
self._recordLastMove(trItemS, trItemP, tIndex)
self._alertTreeChange(tHandle, flush=flush) self._alertTreeChange(tHandle, flush=flush)
logger.debug("Moved item '%s' to Trash", tHandle) logger.debug("Moved item '%s' to Trash", tHandle)
@@ -1093,45 +1081,6 @@ class GuiProjectTree(QTreeWidget):
logger.info("%d item(s) added to the project tree", count) logger.info("%d item(s) added to the project tree", count)
return return
def undoLastMove(self) -> bool:
"""Attempt to undo the last action."""
srcItem = self._lastMove.get("item", None)
dstItem = self._lastMove.get("parent", None)
dstIndex = self._lastMove.get("index", None)
srcOK = isinstance(srcItem, QTreeWidgetItem)
dstOk = isinstance(dstItem, QTreeWidgetItem)
if not srcOK or not dstOk or dstIndex is None:
logger.debug("No tree move to undo")
return False
if srcItem not in self._treeMap.values():
logger.warning("Source item no longer exists")
return False
if dstItem not in self._treeMap.values():
logger.warning("Previous parent item no longer exists")
return False
dstIndex = min(max(0, dstIndex), dstItem.childCount())
sHandle = srcItem.data(self.C_DATA, self.D_HANDLE)
dHandle = dstItem.data(self.C_DATA, self.D_HANDLE)
logger.debug("Moving item '%s' back to '%s', index %d", sHandle, dHandle, dstIndex)
self.propagateCount(sHandle, 0)
parItem = srcItem.parent()
srcIndex = parItem.indexOfChild(srcItem)
movItem = parItem.takeChild(srcIndex)
dstItem.insertChild(dstIndex, movItem)
self._postItemMove(sHandle)
self._alertTreeChange(sHandle, flush=True)
self.setCurrentItem(movItem)
self._lastMove = {}
return True
def getSelectedHandle(self) -> str | None: def getSelectedHandle(self) -> str | None:
"""Get the currently selected handle. If multiple items are """Get the currently selected handle. If multiple items are
selected, return the first. selected, return the first.
@@ -1468,30 +1417,25 @@ class GuiProjectTree(QTreeWidget):
"""Overload the drop item event to ensure the drag and drop """Overload the drop item event to ensure the drag and drop
action is allowed, and update relevant data. action is allowed, and update relevant data.
""" """
if not self.indexAt(event.pos()).isValid(): tItem = self.itemAt(event.pos())
# Make sure nothing can be dropped on invisible root dropOn = self.dropIndicatorPosition() == QAbstractItemView.DropIndicatorPosition.OnItem
# Make sure nothing can be dropped on invisible root (see #1569)
if not tItem or tItem.parent() is None and not dropOn:
logger.error("Invalid drop location") logger.error("Invalid drop location")
event.ignore() event.ignore()
return return
mItems: dict[str, tuple[QTreeWidgetItem, QTreeWidgetItem, bool]] = {} mItems: dict[str, tuple[QTreeWidgetItem, bool]] = {}
sItems = self.selectedItems() sItems = self.selectedItems()
if sItems and (parent := sItems[0].parent()) and all(x.parent() is parent for x in sItems): if sItems and (parent := sItems[0].parent()) and all(x.parent() is parent for x in sItems):
for sItem in sItems: for sItem in sItems:
if (pItem := sItem.parent()): mHandle = str(sItem.data(self.C_DATA, self.D_HANDLE))
mHandle = str(sItem.data(self.C_DATA, self.D_HANDLE)) mItems[mHandle] = (sItem, sItem.isExpanded())
mItems[mHandle] = (sItem, pItem, sItem.isExpanded())
else:
logger.error("Cannot drag and drop a root item")
event.ignore()
return
for mHandle in mItems:
self.propagateCount(mHandle, 0) self.propagateCount(mHandle, 0)
super().dropEvent(event) super().dropEvent(event)
for mHandle, (sItem, pItem, isExpanded) in mItems.items(): for mHandle, (sItem, isExpanded) in mItems.items():
self._postItemMove(mHandle) self._postItemMove(mHandle)
sItem.setExpanded(isExpanded) sItem.setExpanded(isExpanded)
self._alertTreeChange(mHandle, flush=False) self._alertTreeChange(mHandle, flush=False)
@@ -1530,8 +1474,8 @@ class GuiProjectTree(QTreeWidget):
SHARED.project.index.reIndexHandle(mHandle) SHARED.project.index.reIndexHandle(mHandle)
self.setTreeItemValues(mHandle) self.setTreeItemValues(mHandle)
# Trigger dependent updates # Update word count
self.propagateCount(tHandle, nwItemS.wordCount) self.propagateCount(tHandle, nwItemS.wordCount, countChildren=True)
return return
@@ -1884,16 +1828,4 @@ class GuiProjectTree(QTreeWidget):
return return
def _recordLastMove(self, srcItem: QTreeWidgetItem,
parItem: QTreeWidgetItem, parIndex: int) -> None:
"""Record the last action so that it can be undone."""
prevItem = self._lastMove.get("item", None)
if prevItem is None or srcItem != prevItem:
self._lastMove = {
"item": srcItem,
"parent": parItem,
"index": parIndex,
}
return
# END Class GuiProjectTree # END Class GuiProjectTree
+12 -40
View File
@@ -251,11 +251,6 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
C.hChapterDir, C.hChapterDoc, C.hSceneDoc, C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
assert projTree.undoLastMove() is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
"0000000000010", "0000000000011", "0000000000012",
]
# Root Folder # Root Folder
# =========== # ===========
@@ -988,17 +983,14 @@ def testGuiProjTree_DragAndDrop(qtbot, monkeypatch, caplog, nwGUI: GuiMain, proj
mouse = Qt.MouseButton.LeftButton mouse = Qt.MouseButton.LeftButton
modifier = Qt.KeyboardModifier.NoModifier modifier = Qt.KeyboardModifier.NoModifier
# Move an item projTree.saveTreeOrder()
# The actual move is blocked, but the undo history should record treeOrder = SHARED.project.tree._order
# the event, although without an actual move implied
assert projTree._lastMove == {} # Move an item, but no selection
event = QDropEvent(nPos, action, mime, mouse, modifier) event = QDropEvent(nPos, action, mime, mouse, modifier)
projTree.dropEvent(event) projTree.dropEvent(event)
assert projTree._lastMove == { projTree.saveTreeOrder()
"item": projTree._getTreeItem(C.hSceneDoc), assert SHARED.project.tree._order == treeOrder
"parent": projTree._getTreeItem(C.hChapterDir),
"index": 1,
}
# Invalid location # Invalid location
caplog.clear() caplog.clear()
@@ -1006,23 +998,19 @@ def testGuiProjTree_DragAndDrop(qtbot, monkeypatch, caplog, nwGUI: GuiMain, proj
projTree.dropEvent(event) projTree.dropEvent(event)
assert event.isAccepted() is False assert event.isAccepted() is False
assert "Invalid drop location" in caplog.text assert "Invalid drop location" in caplog.text
projTree.saveTreeOrder()
# No item selected assert SHARED.project.tree._order == treeOrder
caplog.clear()
event = QDropEvent(nPos, action, mime, mouse, modifier)
projTree.clearSelection()
projTree.dropEvent(event)
assert event.isAccepted() is False
assert "Invalid drag and drop event" in caplog.text
# Root item selected # Root item selected
caplog.clear() caplog.clear()
event = QDropEvent(nPos, action, mime, mouse, modifier) event = QDropEvent(nPos, action, mime, mouse, modifier)
projTree.clearSelection() projTree.clearSelection()
projTree.setSelectedHandle(C.hNovelRoot, True) projTree._getTreeItem(C.hTitlePage).setSelected(True) # type: ignore
projTree._getTreeItem(C.hNovelRoot).setSelected(True) # type: ignore
projTree.dropEvent(event) projTree.dropEvent(event)
assert event.isAccepted() is False assert event.isAccepted() is False
assert "Invalid drag and drop event" in caplog.text projTree.saveTreeOrder()
assert SHARED.project.tree._order == treeOrder
# qtbot.stop() # qtbot.stop()
@@ -1070,22 +1058,6 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
SHARED.project.tree[nHandle].setParent(C.hInvalid) # type: ignore SHARED.project.tree[nHandle].setParent(C.hInvalid) # type: ignore
assert projTree.revealNewTreeItem(nHandle) is False assert projTree.revealNewTreeItem(nHandle) is False
# Method: undoLastMove
# ====================
# Nothing to move
assert projTree.undoLastMove() is False
projTree._lastMove["item"] = QTreeWidgetItem()
projTree._lastMove["parent"] = QTreeWidgetItem()
projTree._lastMove["index"] = 0
assert projTree.undoLastMove() is False
projTree._lastMove["item"] = projTree._treeMap[C.hTitlePage]
projTree._lastMove["parent"] = QTreeWidgetItem()
projTree._lastMove["index"] = 0
assert projTree.undoLastMove() is False
# Slot: _treeDoubleClick # Slot: _treeDoubleClick
# ====================== # ======================