Add move buttons to project tree toolbar

This commit is contained in:
Veronica Berglyd Olsen
2022-06-07 00:11:42 +02:00
parent 272e7f6e24
commit 5a6ef5efd3
6 changed files with 78 additions and 58 deletions
+1 -2
View File
@@ -48,7 +48,7 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)" ":kbd:`Ctrl`:kbd:`H`", "Open the search and replace bar and search for the selected word, if any is selected. (On Mac, this is :kbd:`Cmd`:kbd:`=`.)"
":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)." ":kbd:`Ctrl`:kbd:`I`", "Format selected text, or word under cursor, with emphasis (italic)."
":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`." ":kbd:`Ctrl`:kbd:`K`", "Activate the insert commands. The commands are listed in :ref:`a_kb_ins`."
":kbd:`Ctrl`:kbd:`N`", "Create new document." ":kbd:`Ctrl`:kbd:`N`", "Create new project item."
":kbd:`Ctrl`:kbd:`O`", "Open selected document." ":kbd:`Ctrl`:kbd:`O`", "Open selected document."
":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter." ":kbd:`Ctrl`:kbd:`Q`", "Exit novelWriter."
":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing." ":kbd:`Ctrl`:kbd:`R`", "If in the project tree, open a document for viewing. If the editor has focus, open current document for viewing."
@@ -72,7 +72,6 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`A`", "Select all text in current paragraph."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`G`", "Find previous occurrence of search word in current document."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`I`", "Import text to the current document from a text file."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`N`", "Create new folder."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`O`", "Open a project."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`R`", "Close the document viewer."
":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project." ":kbd:`Ctrl`:kbd:`Shift`:kbd:`S`", "Save the current project."
+2 -2
View File
@@ -25,7 +25,7 @@ from novelwriter.gui.itemdetails import GuiItemDetails
from novelwriter.gui.mainmenu import GuiMainMenu from novelwriter.gui.mainmenu import GuiMainMenu
from novelwriter.gui.noveltree import GuiNovelTree from novelwriter.gui.noveltree import GuiNovelTree
from novelwriter.gui.outline import GuiOutline from novelwriter.gui.outline import GuiOutline
from novelwriter.gui.projtree import GuiProjectWiew from novelwriter.gui.projtree import GuiProjectView
from novelwriter.gui.statusbar import GuiMainStatus from novelwriter.gui.statusbar import GuiMainStatus
from novelwriter.gui.theme import GuiTheme from novelwriter.gui.theme import GuiTheme
from novelwriter.gui.viewsbar import GuiViewsBar from novelwriter.gui.viewsbar import GuiViewsBar
@@ -39,7 +39,7 @@ __all__ = [
"GuiMainStatus", "GuiMainStatus",
"GuiNovelTree", "GuiNovelTree",
"GuiOutline", "GuiOutline",
"GuiProjectWiew", "GuiProjectView",
"GuiTheme", "GuiTheme",
"GuiViewsBar", "GuiViewsBar",
] ]
+1 -21
View File
@@ -33,7 +33,7 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QMenuBar, QAction from PyQt5.QtWidgets import QMenuBar, QAction
from novelwriter.enum import nwItemType, nwDocAction, nwDocInsert, nwWidget from novelwriter.enum import nwDocAction, nwDocInsert, nwWidget
from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode from novelwriter.constants import trConst, nwKeyWords, nwLabels, nwUnicode
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -62,8 +62,6 @@ class GuiMainMenu(QMenuBar):
# Function Pointers # Function Pointers
self._docAction = self.theParent.passDocumentAction self._docAction = self.theParent.passDocumentAction
self._moveTreeItem = self.theParent.treeView.moveTreeItem
self._newTreeItem = self.theParent.treeView.newTreeItem
self._docInsert = self.theParent.docEditor.insertText self._docInsert = self.theParent.docEditor.insertText
self._insertKeyWord = self.theParent.docEditor.insertKeyWord self._insertKeyWord = self.theParent.docEditor.insertKeyWord
@@ -176,18 +174,6 @@ class GuiMainMenu(QMenuBar):
self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None)) self.aDeleteItem.triggered.connect(lambda: self.theParent.treeView.deleteItem(None))
self.projMenu.addAction(self.aDeleteItem) self.projMenu.addAction(self.aDeleteItem)
# Project > Move Up
self.aMoveUp = QAction(self.tr("Move Item Up"), self)
self.aMoveUp.setShortcut("Ctrl+Up")
self.aMoveUp.triggered.connect(lambda: self._moveTreeItem(-1))
self.projMenu.addAction(self.aMoveUp)
# Project > Move Down
self.aMoveDown = QAction(self.tr("Move Item Down"), self)
self.aMoveDown.setShortcut("Ctrl+Down")
self.aMoveDown.triggered.connect(lambda: self._moveTreeItem(1))
self.projMenu.addAction(self.aMoveDown)
# Project > Undo Last Action # Project > Undo Last Action
self.aMoveUndo = QAction(self.tr("Undo Last Move"), self) self.aMoveUndo = QAction(self.tr("Undo Last Move"), self)
self.aMoveUndo.setShortcut("Ctrl+Shift+Z") self.aMoveUndo.setShortcut("Ctrl+Shift+Z")
@@ -217,12 +203,6 @@ class GuiMainMenu(QMenuBar):
# Document # Document
self.docuMenu = self.addMenu(self.tr("&Document")) self.docuMenu = self.addMenu(self.tr("&Document"))
# Document > New
self.aNewDoc = QAction(self.tr("New Document"), self)
self.aNewDoc.setShortcut("Ctrl+N")
self.aNewDoc.triggered.connect(lambda: self._newTreeItem(nwItemType.FILE))
self.docuMenu.addAction(self.aNewDoc)
# Document > Open # Document > Open
self.aOpenDoc = QAction(self.tr("Open Document"), self) self.aOpenDoc = QAction(self.tr("Open Document"), self)
self.aOpenDoc.setShortcut("Ctrl+O") self.aOpenDoc.setShortcut("Ctrl+O")
+49 -9
View File
@@ -6,7 +6,7 @@ GUI classes for the main window project tree
File History: File History:
Created: 2018-09-29 [0.0.1] GuiProjectTree Created: 2018-09-29 [0.0.1] GuiProjectTree
Created: 2020-06-04 [0.7] GuiProjectTreeMenu Created: 2020-06-04 [0.7] GuiProjectTreeMenu
Created: 2022-06-06 [1.7b1] GuiProjectWiew Created: 2022-06-06 [1.7b1] GuiProjectView
Created: 2022-06-06 [1.7b1] GuiProjectToolBar Created: 2022-06-06 [1.7b1] GuiProjectToolBar
This file is a part of novelWriter This file is a part of novelWriter
@@ -35,9 +35,9 @@ from time import time
from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot from PyQt5.QtCore import Qt, QSize, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction, QFrame, qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction,
QDialog, QHeaderView, QWidget, QVBoxLayout, QToolBar, QLabel, QToolButton, QFrame, QDialog, QHeaderView, QWidget, QVBoxLayout, QToolBar, QLabel,
QSizePolicy, QInputDialog QToolButton, QSizePolicy, QInputDialog
) )
from novelwriter.core import NWDoc from novelwriter.core import NWDoc
@@ -48,7 +48,7 @@ from novelwriter.constants import trConst, nwLabels
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class GuiProjectWiew(QWidget): class GuiProjectView(QWidget):
"""This is a wrapper class holding all the elements of the project """This is a wrapper class holding all the elements of the project
tree. The core object is the project tree itself. Most methods tree. The core object is the project tree itself. Most methods
available are mapped through to the project tree class. available are mapped through to the project tree class.
@@ -87,7 +87,6 @@ class GuiProjectWiew(QWidget):
# Function Mappings # Function Mappings
self.newTreeItem = self.projTree.newTreeItem self.newTreeItem = self.projTree.newTreeItem
self.revealNewTreeItem = self.projTree.revealNewTreeItem self.revealNewTreeItem = self.projTree.revealNewTreeItem
self.moveTreeItem = self.projTree.moveTreeItem
self.editTreeItem = self.projTree.editTreeItem self.editTreeItem = self.projTree.editTreeItem
self.getTreeFromHandle = self.projTree.getTreeFromHandle self.getTreeFromHandle = self.projTree.getTreeFromHandle
self.emptyTrash = self.projTree.emptyTrash self.emptyTrash = self.projTree.emptyTrash
@@ -121,9 +120,26 @@ class GuiProjectWiew(QWidget):
self.projTree.buildTree() self.projTree.buildTree()
return return
def setFocus(self):
"""Forward the set focus call to the tree widget.
"""
self.projTree.setFocus()
return
def treeFocus(self): def treeFocus(self):
"""Check if the project tree has focus.
"""
return self.projTree.hasFocus() return self.projTree.hasFocus()
def anyFocus(self):
"""Check if any widget or child widget has focus.
"""
if self.hasFocus():
return True
if self.isAncestorOf(qApp.focusWidget()):
return True
return False
## ##
# Public Solts # Public Solts
## ##
@@ -136,7 +152,7 @@ class GuiProjectWiew(QWidget):
self.wordCountsChanged.emit() self.wordCountsChanged.emit()
return return
# END Class GuiProjectWiew # END Class GuiProjectView
class GuiProjectToolBar(QToolBar): class GuiProjectToolBar(QToolBar):
@@ -171,6 +187,19 @@ class GuiProjectToolBar(QToolBar):
self.projLabel.setContentsMargins(0, 0, mPx, 0) self.projLabel.setContentsMargins(0, 0, mPx, 0)
self.projLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.projLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
# Move Buttons
self.tbMoveU = QToolButton(self)
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
self.tbMoveU.setShortcut("Ctrl+Up")
self.tbMoveU.setIcon(self.theTheme.getIcon("up"))
self.tbMoveU.clicked.connect(lambda: self._forwardMoveItem(-1))
self.tbMoveD = QToolButton(self)
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
self.tbMoveD.setShortcut("Ctrl+Down")
self.tbMoveD.setIcon(self.theTheme.getIcon("down"))
self.tbMoveD.clicked.connect(lambda: self._forwardMoveItem(1))
# Items Menu # Items Menu
self.mItems = QMenu() self.mItems = QMenu()
@@ -207,6 +236,8 @@ class GuiProjectToolBar(QToolBar):
self._addRootFolderEntry(nwItemClass.CUSTOM) self._addRootFolderEntry(nwItemClass.CUSTOM)
self.tbItems = QToolButton(self) self.tbItems = QToolButton(self)
self.tbItems.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
self.tbItems.setShortcut("Ctrl+N")
self.tbItems.setIcon(self.theTheme.getIcon("add")) self.tbItems.setIcon(self.theTheme.getIcon("add"))
self.tbItems.setMenu(self.mItems) self.tbItems.setMenu(self.mItems)
self.tbItems.setPopupMode(QToolButton.InstantPopup) self.tbItems.setPopupMode(QToolButton.InstantPopup)
@@ -219,6 +250,8 @@ class GuiProjectToolBar(QToolBar):
# Assemble # Assemble
self.addWidget(self.projLabel) self.addWidget(self.projLabel)
self.addSeparator() self.addSeparator()
self.addWidget(self.tbMoveU)
self.addWidget(self.tbMoveD)
self.addWidget(self.tbItems) self.addWidget(self.tbItems)
self.addWidget(self.tbSettings) self.addWidget(self.tbSettings)
@@ -253,6 +286,13 @@ class GuiProjectToolBar(QToolBar):
self.theWidget.projTree.newTreeItem(nwItemType.FOLDER) self.theWidget.projTree.newTreeItem(nwItemType.FOLDER)
return return
@pyqtSlot(int)
def _forwardMoveItem(self, steps):
"""Forward the request to move an item up or down.
"""
self.theWidget.projTree.moveTreeItem(steps)
return
## ##
# Internal Functions # Internal Functions
## ##
@@ -492,7 +532,7 @@ class GuiProjectTree(QTreeWidget):
logger.error("No project open") logger.error("No project open")
return False return False
if not self.hasFocus(): if not self.theWidget.anyFocus():
return False return False
tHandle = self.getSelectedHandle() tHandle = self.getSelectedHandle()
@@ -831,7 +871,7 @@ class GuiProjectTree(QTreeWidget):
dstItem = self._lastMove.get("parent", None) dstItem = self._lastMove.get("parent", None)
dstIndex = self._lastMove.get("index", None) dstIndex = self._lastMove.get("index", None)
if not self.hasFocus(): if not self.theWidget.anyFocus():
return False return False
if srcItem is None or dstItem is None or dstIndex is None: if srcItem is None or dstItem is None or dstIndex is None:
+2 -2
View File
@@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
from novelwriter.gui import ( from novelwriter.gui import (
GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu, GuiDocEditor, GuiDocViewDetails, GuiDocViewer, GuiItemDetails, GuiMainMenu,
GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectWiew, GuiTheme, GuiMainStatus, GuiNovelTree, GuiOutline, GuiProjectView, GuiTheme,
GuiViewsBar GuiViewsBar
) )
from novelwriter.dialogs import ( from novelwriter.dialogs import (
@@ -105,7 +105,7 @@ class GuiMain(QMainWindow):
# Main GUI Elements # Main GUI Elements
self.statusBar = GuiMainStatus(self) self.statusBar = GuiMainStatus(self)
self.treeView = GuiProjectWiew(self) self.treeView = GuiProjectView(self)
self.novelView = GuiNovelTree(self) self.novelView = GuiNovelTree(self)
self.docEditor = GuiDocEditor(self) self.docEditor = GuiDocEditor(self)
self.viewMeta = GuiDocViewDetails(self) self.viewMeta = GuiDocViewDetails(self)
+23 -22
View File
@@ -26,7 +26,7 @@ from tools import buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QInputDialog from PyQt5.QtWidgets import QAction, QMessageBox, QInputDialog
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectView, GuiProjectTree
from novelwriter.enum import nwItemType, nwItemClass from novelwriter.enum import nwItemType, nwItemClass
@@ -163,11 +163,12 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
monkeypatch.setattr(GuiProjectView, "anyFocus", lambda *a: True)
nwTree = nwGUI.treeView nwTree = nwGUI.treeView
# Try to move item with no project # Try to move item with no project
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
# Create a project # Create a project
prjDir = os.path.join(fncDir, "project") prjDir = os.path.join(fncDir, "project")
@@ -187,33 +188,33 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
] ]
# Move item without focus # Move item without focus
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: False) with monkeypatch.context() as mp:
assert nwTree.moveTreeItem(1) is False mp.setattr(GuiProjectView, "anyFocus", lambda *a: False)
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.projTree.moveTreeItem(1) is False
"000000000000d", "000000000000e", "000000000000f", assert nwTree.getTreeFromHandle("000000000000d") == [
"0000000000010", "0000000000011", "0000000000012", "000000000000d", "000000000000e", "000000000000f",
] "0000000000010", "0000000000011", "0000000000012",
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) ]
# Move with no selections # Move with no selections
nwTree.projTree.clearSelection() nwTree.projTree.clearSelection()
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
# Move second item up twice (should give same result) # Move second item up twice (should give same result)
nwTree.setSelectedHandle("000000000000f") nwTree.setSelectedHandle("000000000000f")
assert nwTree.moveTreeItem(-1) is True assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e", "000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
] ]
assert nwTree.moveTreeItem(-1) is False assert nwTree.projTree.moveTreeItem(-1) is False
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000f", "000000000000e", "000000000000d", "000000000000f", "000000000000e",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
] ]
# Restore via menu entry # Restore
nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -221,19 +222,19 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Move fifth item down twice (should give same result) # Move fifth item down twice (should give same result)
nwTree.setSelectedHandle("0000000000011") nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
assert nwTree.moveTreeItem(1) is False assert nwTree.projTree.moveTreeItem(1) is False
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
] ]
# Restore via menu entry # Restore
nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) assert nwTree.projTree.moveTreeItem(-1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000011", "0000000000012", "0000000000010", "0000000000011", "0000000000012",
@@ -241,7 +242,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
# Move down again, and restore via undo # Move down again, and restore via undo
nwTree.setSelectedHandle("0000000000011") nwTree.setSelectedHandle("0000000000011")
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwTree.getTreeFromHandle("000000000000d") == [ assert nwTree.getTreeFromHandle("000000000000d") == [
"000000000000d", "000000000000e", "000000000000f", "000000000000d", "000000000000e", "000000000000f",
"0000000000010", "0000000000012", "0000000000011", "0000000000010", "0000000000012", "0000000000011",
@@ -259,15 +260,15 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Move novel folder up # Move novel folder up
assert nwTree.moveTreeItem(-1) is False assert nwTree.projTree.moveTreeItem(-1) is False
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Move novel folder down # Move novel folder down
assert nwTree.moveTreeItem(1) is True assert nwTree.projTree.moveTreeItem(1) is True
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 1
# Move novel folder up again # Move novel folder up again
assert nwTree.moveTreeItem(-1) is True assert nwTree.projTree.moveTreeItem(-1) is True
assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0 assert nwGUI.theProject.tree._treeOrder.index("0000000000008") == 0
# Clean up # Clean up