Tweak project toolbar look, and fix shortcuts
This commit is contained in:
+79
-86
@@ -33,11 +33,11 @@ from enum import Enum
|
|||||||
from time import time
|
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, QPalette
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction,
|
qApp, QTreeWidget, QTreeWidgetItem, QAbstractItemView, QMenu, QAction,
|
||||||
QFrame, QDialog, QHeaderView, QWidget, QVBoxLayout, QToolBar, QLabel,
|
QFrame, QDialog, QHeaderView, QWidget, QVBoxLayout, QLabel, QToolButton,
|
||||||
QToolButton, QSizePolicy, QInputDialog
|
QSizePolicy, QInputDialog, QHBoxLayout, QShortcut
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter.core import NWDoc
|
from novelwriter.core import NWDoc
|
||||||
@@ -75,13 +75,24 @@ class GuiProjectView(QWidget):
|
|||||||
|
|
||||||
# Assemble
|
# Assemble
|
||||||
self.outerBox = QVBoxLayout()
|
self.outerBox = QVBoxLayout()
|
||||||
self.outerBox.addWidget(self.projBar)
|
self.outerBox.addWidget(self.projBar, 0)
|
||||||
self.outerBox.addWidget(self.projTree)
|
self.outerBox.addWidget(self.projTree, 1)
|
||||||
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
self.outerBox.setContentsMargins(0, 0, 0, 0)
|
||||||
self.outerBox.setSpacing(0)
|
self.outerBox.setSpacing(0)
|
||||||
|
|
||||||
self.setLayout(self.outerBox)
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
|
# Keyboard Shortcuts
|
||||||
|
self.keyCtrlUp = QShortcut(self.projTree)
|
||||||
|
self.keyCtrlUp.setKey("Ctrl+Up")
|
||||||
|
self.keyCtrlUp.setContext(Qt.WidgetShortcut)
|
||||||
|
self.keyCtrlUp.activated.connect(lambda: self.projTree.moveTreeItem(-1))
|
||||||
|
|
||||||
|
self.keyCtrlDown = QShortcut(self.projTree)
|
||||||
|
self.keyCtrlDown.setKey("Ctrl+Down")
|
||||||
|
self.keyCtrlDown.setContext(Qt.WidgetShortcut)
|
||||||
|
self.keyCtrlDown.activated.connect(lambda: self.projTree.moveTreeItem(1))
|
||||||
|
|
||||||
# Connect Signals
|
# Connect Signals
|
||||||
|
|
||||||
# Function Mappings
|
# Function Mappings
|
||||||
@@ -155,7 +166,7 @@ class GuiProjectView(QWidget):
|
|||||||
# END Class GuiProjectView
|
# END Class GuiProjectView
|
||||||
|
|
||||||
|
|
||||||
class GuiProjectToolBar(QToolBar):
|
class GuiProjectToolBar(QWidget):
|
||||||
|
|
||||||
ADD_PLAIN = 0
|
ADD_PLAIN = 0
|
||||||
ADD_CHAP = 1
|
ADD_CHAP = 1
|
||||||
@@ -163,42 +174,52 @@ class GuiProjectToolBar(QToolBar):
|
|||||||
ADD_NOTE = 3
|
ADD_NOTE = 3
|
||||||
ADD_FOLDER = 4
|
ADD_FOLDER = 4
|
||||||
|
|
||||||
def __init__(self, theWidget):
|
def __init__(self, projView):
|
||||||
QTreeWidget.__init__(self, theWidget)
|
QTreeWidget.__init__(self, projView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectToolBar ...")
|
logger.debug("Initialising GuiProjectToolBar ...")
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWidget = theWidget
|
self.projView = projView
|
||||||
self.theParent = theWidget.theParent
|
self.theParent = projView.theParent
|
||||||
self.theProject = theWidget.theParent.theProject
|
self.theProject = projView.theParent.theProject
|
||||||
self.theTheme = theWidget.theParent.theTheme
|
self.theTheme = projView.theParent.theTheme
|
||||||
|
|
||||||
iPx = self.theTheme.baseIconSize
|
iPx = self.theTheme.baseIconSize
|
||||||
mPx = self.mainConf.pxInt(12)
|
mPx = self.mainConf.pxInt(4)
|
||||||
|
|
||||||
self.setMovable(False)
|
|
||||||
self.setIconSize(QSize(iPx, iPx))
|
|
||||||
self.setContentsMargins(0, 0, 0, 0)
|
self.setContentsMargins(0, 0, 0, 0)
|
||||||
self.setStyleSheet("QToolBar {border: 0px;}")
|
self.setAutoFillBackground(True)
|
||||||
|
|
||||||
|
qPalette = self.palette()
|
||||||
|
qPalette.setBrush(QPalette.Window, qPalette.base())
|
||||||
|
self.setPalette(qPalette)
|
||||||
|
|
||||||
|
fadeCol = qPalette.text().color()
|
||||||
|
buttonStyle = (
|
||||||
|
"QToolButton {{border: none; background: transparent;}} "
|
||||||
|
"QToolButton:hover {{border: none; background: rgba({0},{1},{2},0.2);}}"
|
||||||
|
).format(fadeCol.red(), fadeCol.green(), fadeCol.blue())
|
||||||
|
|
||||||
# Tree Label
|
# Tree Label
|
||||||
self.projLabel = QLabel("<b>%s</b>" % self.tr("Project Content"))
|
self.projLabel = QLabel("<b>%s</b>" % self.tr("Project Content"))
|
||||||
self.projLabel.setContentsMargins(0, 0, mPx, 0)
|
self.projLabel.setContentsMargins(0, 0, 0, 0)
|
||||||
self.projLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
self.projLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
|
|
||||||
# Move Buttons
|
# Move Buttons
|
||||||
self.tbMoveU = QToolButton(self)
|
self.tbMoveU = QToolButton(self)
|
||||||
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
|
self.tbMoveU.setToolTip("%s [Ctrl+Up]" % self.tr("Move Up"))
|
||||||
self.tbMoveU.setShortcut("Ctrl+Up")
|
|
||||||
self.tbMoveU.setIcon(self.theTheme.getIcon("up"))
|
self.tbMoveU.setIcon(self.theTheme.getIcon("up"))
|
||||||
self.tbMoveU.clicked.connect(lambda: self._forwardMoveItem(-1))
|
self.tbMoveU.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbMoveU.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMoveU.clicked.connect(lambda: self.projView.projTree.moveTreeItem(-1))
|
||||||
|
|
||||||
self.tbMoveD = QToolButton(self)
|
self.tbMoveD = QToolButton(self)
|
||||||
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
|
self.tbMoveD.setToolTip("%s [Ctrl+Down]" % self.tr("Move Down"))
|
||||||
self.tbMoveD.setShortcut("Ctrl+Down")
|
|
||||||
self.tbMoveD.setIcon(self.theTheme.getIcon("down"))
|
self.tbMoveD.setIcon(self.theTheme.getIcon("down"))
|
||||||
self.tbMoveD.clicked.connect(lambda: self._forwardMoveItem(1))
|
self.tbMoveD.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbMoveD.setStyleSheet(buttonStyle)
|
||||||
|
self.tbMoveD.clicked.connect(lambda: self.projView.projTree.moveTreeItem(1))
|
||||||
|
|
||||||
# Items Menu
|
# Items Menu
|
||||||
self.mItems = QMenu()
|
self.mItems = QMenu()
|
||||||
@@ -239,21 +260,29 @@ class GuiProjectToolBar(QToolBar):
|
|||||||
self.tbItems.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
|
self.tbItems.setToolTip("%s [Ctrl+N]" % self.tr("Add Item"))
|
||||||
self.tbItems.setShortcut("Ctrl+N")
|
self.tbItems.setShortcut("Ctrl+N")
|
||||||
self.tbItems.setIcon(self.theTheme.getIcon("add"))
|
self.tbItems.setIcon(self.theTheme.getIcon("add"))
|
||||||
|
self.tbItems.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbItems.setStyleSheet(buttonStyle)
|
||||||
self.tbItems.setMenu(self.mItems)
|
self.tbItems.setMenu(self.mItems)
|
||||||
self.tbItems.setPopupMode(QToolButton.InstantPopup)
|
self.tbItems.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
# Settings Menu
|
# Settings Menu
|
||||||
self.tbSettings = QToolButton(self)
|
self.tbSettings = QToolButton(self)
|
||||||
self.tbSettings.setIcon(self.theTheme.getIcon("menu"))
|
self.tbSettings.setIcon(self.theTheme.getIcon("menu"))
|
||||||
|
self.tbSettings.setIconSize(QSize(iPx, iPx))
|
||||||
|
self.tbSettings.setStyleSheet(buttonStyle)
|
||||||
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
|
self.tbSettings.setPopupMode(QToolButton.InstantPopup)
|
||||||
|
|
||||||
# Assemble
|
# Assemble
|
||||||
self.addWidget(self.projLabel)
|
self.outerBox = QHBoxLayout()
|
||||||
self.addSeparator()
|
self.outerBox.addWidget(self.projLabel)
|
||||||
self.addWidget(self.tbMoveU)
|
self.outerBox.addWidget(self.tbMoveU)
|
||||||
self.addWidget(self.tbMoveD)
|
self.outerBox.addWidget(self.tbMoveD)
|
||||||
self.addWidget(self.tbItems)
|
self.outerBox.addWidget(self.tbItems)
|
||||||
self.addWidget(self.tbSettings)
|
self.outerBox.addWidget(self.tbSettings)
|
||||||
|
self.outerBox.setContentsMargins(mPx, mPx, 0, mPx)
|
||||||
|
self.outerBox.setSpacing(mPx)
|
||||||
|
|
||||||
|
self.setLayout(self.outerBox)
|
||||||
|
|
||||||
logger.debug("GuiProjectToolBar initialisation complete")
|
logger.debug("GuiProjectToolBar initialisation complete")
|
||||||
|
|
||||||
@@ -267,7 +296,7 @@ class GuiProjectToolBar(QToolBar):
|
|||||||
def _forwardNewRootFolder(self, itemClass):
|
def _forwardNewRootFolder(self, itemClass):
|
||||||
"""Forward the request for a new root folder to the tree.
|
"""Forward the request for a new root folder to the tree.
|
||||||
"""
|
"""
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.ROOT, itemClass)
|
self.projView.projTree.newTreeItem(nwItemType.ROOT, itemClass)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
@@ -275,22 +304,15 @@ class GuiProjectToolBar(QToolBar):
|
|||||||
"""Forward the request for a new item of a given type.
|
"""Forward the request for a new item of a given type.
|
||||||
"""
|
"""
|
||||||
if type == self.ADD_PLAIN:
|
if type == self.ADD_PLAIN:
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
|
self.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=0, isNote=False)
|
||||||
elif type == self.ADD_CHAP:
|
elif type == self.ADD_CHAP:
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
|
self.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=2, isNote=False)
|
||||||
elif type == self.ADD_SCENE:
|
elif type == self.ADD_SCENE:
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
|
self.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=3, isNote=False)
|
||||||
elif type == self.ADD_NOTE:
|
elif type == self.ADD_NOTE:
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
self.projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True)
|
||||||
elif type == self.ADD_FOLDER:
|
elif type == self.ADD_FOLDER:
|
||||||
self.theWidget.projTree.newTreeItem(nwItemType.FOLDER)
|
self.projView.projTree.newTreeItem(nwItemType.FOLDER)
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot(int)
|
|
||||||
def _forwardMoveItem(self, steps):
|
|
||||||
"""Forward the request to move an item up or down.
|
|
||||||
"""
|
|
||||||
self.theWidget.projTree.moveTreeItem(steps)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -315,16 +337,16 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
C_EXPORT = 2
|
C_EXPORT = 2
|
||||||
C_STATUS = 3
|
C_STATUS = 3
|
||||||
|
|
||||||
def __init__(self, theWidget):
|
def __init__(self, projView):
|
||||||
QTreeWidget.__init__(self, theWidget)
|
QTreeWidget.__init__(self, projView)
|
||||||
|
|
||||||
logger.debug("Initialising GuiProjectTree ...")
|
logger.debug("Initialising GuiProjectTree ...")
|
||||||
|
|
||||||
self.mainConf = novelwriter.CONFIG
|
self.mainConf = novelwriter.CONFIG
|
||||||
self.theWidget = theWidget
|
self.projView = projView
|
||||||
self.theParent = theWidget.theParent
|
self.theParent = projView.theParent
|
||||||
self.theTheme = theWidget.theParent.theTheme
|
self.theTheme = projView.theParent.theTheme
|
||||||
self.theProject = theWidget.theParent.theProject
|
self.theProject = projView.theParent.theProject
|
||||||
|
|
||||||
# Internal Variables
|
# Internal Variables
|
||||||
self._treeMap = {}
|
self._treeMap = {}
|
||||||
@@ -496,7 +518,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
# If successful, update word count
|
# If successful, update word count
|
||||||
wC = self.theProject.index.getCounts(tHandle)[1]
|
wC = self.theProject.index.getCounts(tHandle)[1]
|
||||||
self.propagateCount(tHandle, wC)
|
self.propagateCount(tHandle, wC)
|
||||||
self.theWidget.wordCountsChanged.emit()
|
self.projView.wordCountsChanged.emit()
|
||||||
|
|
||||||
# Add the new item to the project tree
|
# Add the new item to the project tree
|
||||||
self.revealNewTreeItem(tHandle, nHandle)
|
self.revealNewTreeItem(tHandle, nHandle)
|
||||||
@@ -525,19 +547,12 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def moveTreeItem(self, nStep):
|
def moveTreeItem(self, nStep):
|
||||||
"""Move an item up or down in the tree, but only if the project
|
"""Move an item up or down in the tree.
|
||||||
tree has focus. This also applies when the menu is used.
|
|
||||||
"""
|
"""
|
||||||
if not self.theParent.hasProject:
|
|
||||||
logger.error("No project open")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.theWidget.anyFocus():
|
|
||||||
return False
|
|
||||||
|
|
||||||
tHandle = self.getSelectedHandle()
|
tHandle = self.getSelectedHandle()
|
||||||
tItem = self._getTreeItem(tHandle)
|
tItem = self._getTreeItem(tHandle)
|
||||||
if tItem is None:
|
if tItem is None:
|
||||||
|
logger.verbose("No item selected")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pItem = tItem.parent()
|
pItem = tItem.parent()
|
||||||
@@ -745,7 +760,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
self._deleteTreeItem(dHandle)
|
self._deleteTreeItem(dHandle)
|
||||||
|
|
||||||
self._alertTreeChange(tHandle=tHandle, flush=autoFlush)
|
self._alertTreeChange(tHandle=tHandle, flush=autoFlush)
|
||||||
self.theWidget.wordCountsChanged.emit()
|
self.projView.wordCountsChanged.emit()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# The item is not already in the trash folder, so we
|
# The item is not already in the trash folder, so we
|
||||||
@@ -871,7 +886,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.theWidget.anyFocus():
|
if not self.projView.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:
|
||||||
@@ -951,7 +966,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
tHandle = self.getSelectedHandle()
|
tHandle = self.getSelectedHandle()
|
||||||
if tHandle is not None:
|
if tHandle is not None:
|
||||||
self.theWidget.selectedItemChanged.emit(tHandle)
|
self.projView.selectedItemChanged.emit(tHandle)
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot("QTreeWidgetItem*", int)
|
@pyqtSlot("QTreeWidgetItem*", int)
|
||||||
@@ -968,7 +983,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if tItem.itemType == nwItemType.FILE:
|
if tItem.itemType == nwItemType.FILE:
|
||||||
self.theWidget.openDocumentRequest.emit(tHandle, nwDocMode.EDIT)
|
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.EDIT)
|
||||||
else:
|
else:
|
||||||
trItem = self._getTreeItem(tHandle)
|
trItem = self._getTreeItem(tHandle)
|
||||||
if trItem is not None:
|
if trItem is not None:
|
||||||
@@ -1020,7 +1035,7 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if tItem.itemType == nwItemType.FILE:
|
if tItem.itemType == nwItemType.FILE:
|
||||||
self.theWidget.openDocumentRequest.emit(tHandle, nwDocMode.VIEW)
|
self.projView.openDocumentRequest.emit(tHandle, nwDocMode.VIEW)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1227,11 +1242,11 @@ class GuiProjectTree(QTreeWidget):
|
|||||||
|
|
||||||
itemType = tItem.itemType
|
itemType = tItem.itemType
|
||||||
if itemType == nwItemType.ROOT:
|
if itemType == nwItemType.ROOT:
|
||||||
self.theWidget.rootFolderChanged.emit(tHandle)
|
self.projView.rootFolderChanged.emit(tHandle)
|
||||||
elif itemType == nwItemType.FILE and tItem.isNovelLike():
|
elif itemType == nwItemType.FILE and tItem.isNovelLike():
|
||||||
self.theWidget.novelItemChanged.emit(tHandle)
|
self.projView.novelItemChanged.emit(tHandle)
|
||||||
|
|
||||||
self.theWidget.treeItemChanged.emit(tHandle)
|
self.projView.treeItemChanged.emit(tHandle)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1283,14 +1298,6 @@ class GuiProjectTreeMenu(QMenu):
|
|||||||
self.emptyTrash.triggered.connect(self._doEmptyTrash)
|
self.emptyTrash.triggered.connect(self._doEmptyTrash)
|
||||||
self.addAction(self.emptyTrash)
|
self.addAction(self.emptyTrash)
|
||||||
|
|
||||||
self.moveUp = QAction(self.tr("Move Item Up"), self)
|
|
||||||
self.moveUp.triggered.connect(self._doMoveUp)
|
|
||||||
self.addAction(self.moveUp)
|
|
||||||
|
|
||||||
self.moveDown = QAction(self.tr("Move Item Down"), self)
|
|
||||||
self.moveDown.triggered.connect(self._doMoveDown)
|
|
||||||
self.addAction(self.moveDown)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def filterActions(self, theItem):
|
def filterActions(self, theItem):
|
||||||
@@ -1369,18 +1376,4 @@ class GuiProjectTreeMenu(QMenu):
|
|||||||
self.theTree.emptyTrash()
|
self.theTree.emptyTrash()
|
||||||
return
|
return
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _doMoveUp(self):
|
|
||||||
"""Forward the move item call to the project tree.
|
|
||||||
"""
|
|
||||||
self.theTree.moveTreeItem(-1)
|
|
||||||
return
|
|
||||||
|
|
||||||
@pyqtSlot()
|
|
||||||
def _doMoveDown(self):
|
|
||||||
"""Forward the move item call to the project tree.
|
|
||||||
"""
|
|
||||||
self.theTree.moveTreeItem(1)
|
|
||||||
return
|
|
||||||
|
|
||||||
# END Class GuiProjectTreeMenu
|
# END Class GuiProjectTreeMenu
|
||||||
|
|||||||
@@ -187,15 +187,6 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
|
|||||||
"0000000000010", "0000000000011", "0000000000012",
|
"0000000000010", "0000000000011", "0000000000012",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Move item without focus
|
|
||||||
with monkeypatch.context() as mp:
|
|
||||||
mp.setattr(GuiProjectView, "anyFocus", lambda *a: False)
|
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
|
||||||
assert nwTree.getTreeFromHandle("000000000000d") == [
|
|
||||||
"000000000000d", "000000000000e", "000000000000f",
|
|
||||||
"0000000000010", "0000000000011", "0000000000012",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Move with no selections
|
# Move with no selections
|
||||||
nwTree.projTree.clearSelection()
|
nwTree.projTree.clearSelection()
|
||||||
assert nwTree.projTree.moveTreeItem(1) is False
|
assert nwTree.projTree.moveTreeItem(1) is False
|
||||||
|
|||||||
Reference in New Issue
Block a user