From 693f3fb6564965231e5d52b1f552b8a3c8c8337c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:53:36 +0100 Subject: [PATCH 1/5] Add a keyboard shortcut to toggle tree view --- novelwriter/guimain.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 519a40cf..2e8682aa 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -31,7 +31,7 @@ from pathlib import Path from datetime import datetime from PyQt5.QtCore import Qt, QTimer, pyqtSlot -from PyQt5.QtGui import QCloseEvent, QCursor, QIcon, QKeySequence +from PyQt5.QtGui import QCloseEvent, QCursor, QIcon from PyQt5.QtWidgets import ( QDialog, QFileDialog, QHBoxLayout, QMainWindow, QMessageBox, QShortcut, QSplitter, QStackedWidget, QVBoxLayout, QWidget, qApp @@ -300,17 +300,21 @@ class GuiMain(QMainWindow): # Shortcuts and Actions self._connectMenuActions() - keyReturn = QShortcut(self) - keyReturn.setKey(QKeySequence(Qt.Key_Return)) - keyReturn.activated.connect(self._keyPressReturn) + self.keyReturn = QShortcut(self) + self.keyReturn.setKey(Qt.Key.Key_Return) + self.keyReturn.activated.connect(self._keyPressReturn) - keyEnter = QShortcut(self) - keyEnter.setKey(QKeySequence(Qt.Key_Enter)) - keyEnter.activated.connect(self._keyPressReturn) + self.keyEnter = QShortcut(self) + self.keyEnter.setKey(Qt.Key.Key_Enter) + self.keyEnter.activated.connect(self._keyPressReturn) - keyEscape = QShortcut(self) - keyEscape.setKey(QKeySequence(Qt.Key_Escape)) - keyEscape.activated.connect(self._keyPressEscape) + self.keyEscape = QShortcut(self) + self.keyEscape.setKey(Qt.Key.Key_Escape) + self.keyEscape.activated.connect(self._keyPressEscape) + + self.keyTreeView = QShortcut(self) + self.keyTreeView.setKey("Ctrl+T") + self.keyTreeView.activated.connect(self._rotateTreeView) # Check that config loaded fine self.reportConfErr() @@ -1224,18 +1228,23 @@ class GuiMain(QMainWindow): if view == nwView.EDITOR: # Only change the main stack, but not the project stack self.mainStack.setCurrentWidget(self.splitMain) - elif view == nwView.PROJECT: self.mainStack.setCurrentWidget(self.splitMain) self.projStack.setCurrentWidget(self.projView) - elif view == nwView.NOVEL: self.mainStack.setCurrentWidget(self.splitMain) self.projStack.setCurrentWidget(self.novelView) - elif view == nwView.OUTLINE: self.mainStack.setCurrentWidget(self.outlineView) + return + @pyqtSlot() + def _rotateTreeView(self) -> None: + """Change view to the next tree view.""" + if self.projStack.currentWidget() is self.projView: + self._changeView(nwView.NOVEL) + else: + self._changeView(nwView.PROJECT) return @pyqtSlot(nwDocAction) From 2097eaeeaa390cf4c7992d6d4a923423dd70d0da Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 10 Nov 2023 17:53:54 +0100 Subject: [PATCH 2/5] Add keyboard shortcuts to sidebar tooltips --- novelwriter/gui/sidebar.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index be719a42..f980aa7f 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -57,12 +57,12 @@ class GuiSideBar(QWidget): # Buttons self.tbProject = QToolButton(self) - self.tbProject.setToolTip(self.tr("Project Tree View")) + self.tbProject.setToolTip("{0} [Ctrl+T]".format(self.tr("Project Tree View"))) self.tbProject.setIconSize(iconSize) self.tbProject.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.PROJECT)) self.tbNovel = QToolButton(self) - self.tbNovel.setToolTip(self.tr("Novel Tree View")) + self.tbNovel.setToolTip("{0} [Ctrl+T]".format(self.tr("Novel Tree View"))) self.tbNovel.setIconSize(iconSize) self.tbNovel.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) @@ -72,17 +72,17 @@ class GuiSideBar(QWidget): self.tbOutline.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) self.tbBuild = QToolButton(self) - self.tbBuild.setToolTip(self.tr("Build Manuscript")) + self.tbBuild.setToolTip("{0} [F5]".format(self.tr("Build Manuscript"))) self.tbBuild.setIconSize(iconSize) self.tbBuild.clicked.connect(self.mainGui.showBuildManuscriptDialog) self.tbDetails = QToolButton(self) - self.tbDetails.setToolTip(self.tr("Project Details")) + self.tbDetails.setToolTip("{0} [Shift+F6]".format(self.tr("Project Details"))) self.tbDetails.setIconSize(iconSize) self.tbDetails.clicked.connect(self.mainGui.showProjectDetailsDialog) self.tbStats = QToolButton(self) - self.tbStats.setToolTip(self.tr("Writing Statistics")) + self.tbStats.setToolTip("{0} [F6]".format(self.tr("Writing Statistics"))) self.tbStats.setIconSize(iconSize) self.tbStats.clicked.connect(self.mainGui.showWritingStatsDialog) From 788f46bd7f8273fc6967992b5d096897494b2be9 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 10 Nov 2023 18:25:32 +0100 Subject: [PATCH 3/5] Improve focus switching and combine with main menu entries --- novelwriter/gui/mainmenu.py | 54 +++++++++++++++++++++------------ novelwriter/gui/projtree.py | 2 +- novelwriter/gui/sidebar.py | 2 +- novelwriter/guimain.py | 60 +++++++++++++++++-------------------- 4 files changed, 65 insertions(+), 53 deletions(-) diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index e6453a82..06a137cd 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -54,6 +54,7 @@ class GuiMainMenu(QMenuBar): requestDocInsert = pyqtSignal(nwDocInsert) requestDocInsertText = pyqtSignal(str) requestDocKeyWordInsert = pyqtSignal(str) + requestFocusChange = pyqtSignal(nwWidget) def __init__(self, mainGui: GuiMain) -> None: super().__init__(parent=mainGui) @@ -246,12 +247,16 @@ class GuiMainMenu(QMenuBar): # Edit > Undo self.aEditUndo = self.editMenu.addAction(self.tr("Undo")) self.aEditUndo.setShortcut("Ctrl+Z") - self.aEditUndo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.UNDO)) + self.aEditUndo.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.UNDO) + ) # Edit > Redo self.aEditRedo = self.editMenu.addAction(self.tr("Redo")) self.aEditRedo.setShortcut("Ctrl+Y") - self.aEditRedo.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.REDO)) + self.aEditRedo.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.REDO) + ) # Edit > Separator self.editMenu.addSeparator() @@ -259,17 +264,23 @@ class GuiMainMenu(QMenuBar): # Edit > Cut self.aEditCut = self.editMenu.addAction(self.tr("Cut")) self.aEditCut.setShortcut("Ctrl+X") - self.aEditCut.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.CUT)) + self.aEditCut.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.CUT) + ) # Edit > Copy self.aEditCopy = self.editMenu.addAction(self.tr("Copy")) self.aEditCopy.setShortcut("Ctrl+C") - self.aEditCopy.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.COPY)) + self.aEditCopy.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.COPY) + ) # Edit > Paste self.aEditPaste = self.editMenu.addAction(self.tr("Paste")) self.aEditPaste.setShortcut("Ctrl+V") - self.aEditPaste.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.PASTE)) + self.aEditPaste.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.PASTE) + ) # Edit > Separator self.editMenu.addSeparator() @@ -277,12 +288,16 @@ class GuiMainMenu(QMenuBar): # Edit > Select All self.aSelectAll = self.editMenu.addAction(self.tr("Select All")) self.aSelectAll.setShortcut("Ctrl+A") - self.aSelectAll.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL)) + self.aSelectAll.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.SEL_ALL) + ) # Edit > Select Paragraph self.aSelectPar = self.editMenu.addAction(self.tr("Select Paragraph")) self.aSelectPar.setShortcut("Ctrl+Shift+A") - self.aSelectPar.triggered.connect(lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA)) + self.aSelectPar.triggered.connect( + lambda: self.requestDocAction.emit(nwDocAction.SEL_PARA) + ) return @@ -293,23 +308,24 @@ class GuiMainMenu(QMenuBar): # View > TreeView self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Project Tree")) - self.aFocusTree.setShortcut("Ctrl+Alt+1" if CONFIG.osWindows else "Alt+1") - self.aFocusTree.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.TREE)) + self.aFocusTree.setShortcut("Ctrl+T") + self.aFocusTree.triggered.connect( + lambda: self.requestFocusChange.emit(nwWidget.TREE) + ) - # View > Document Pane 1 + # View > Document Editor self.aFocusEditor = self.viewMenu.addAction(self.tr("Go to Document Editor")) - self.aFocusEditor.setShortcut("Ctrl+Alt+2" if CONFIG.osWindows else "Alt+2") - self.aFocusEditor.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.EDITOR)) - - # View > Document Pane 2 - self.aFocusView = self.viewMenu.addAction(self.tr("Go to Document Viewer")) - self.aFocusView.setShortcut("Ctrl+Alt+3" if CONFIG.osWindows else "Alt+3") - self.aFocusView.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.VIEWER)) + self.aFocusEditor.setShortcut("Ctrl+E") + self.aFocusEditor.triggered.connect( + lambda: self.requestFocusChange.emit(nwWidget.EDITOR) + ) # View > Outline self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline")) - self.aFocusOutline.setShortcut("Ctrl+Alt+4" if CONFIG.osWindows else "Alt+4") - self.aFocusOutline.triggered.connect(lambda: self.mainGui.switchFocus(nwWidget.OUTLINE)) + self.aFocusOutline.setShortcut("Ctrl+Shift+T") + self.aFocusOutline.triggered.connect( + lambda: self.requestFocusChange.emit(nwWidget.OUTLINE) + ) # View > Separator self.viewMenu.addSeparator() diff --git a/novelwriter/gui/projtree.py b/novelwriter/gui/projtree.py index d1c2f903..b52680d7 100644 --- a/novelwriter/gui/projtree.py +++ b/novelwriter/gui/projtree.py @@ -189,7 +189,7 @@ class GuiProjectView(QWidget): self.projTree.buildTree() return - def setFocus(self) -> None: + def setTreeFocus(self) -> None: """Forward the set focus call to the tree widget.""" self.projTree.setFocus() return diff --git a/novelwriter/gui/sidebar.py b/novelwriter/gui/sidebar.py index f980aa7f..6bee63a6 100644 --- a/novelwriter/gui/sidebar.py +++ b/novelwriter/gui/sidebar.py @@ -67,7 +67,7 @@ class GuiSideBar(QWidget): self.tbNovel.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.NOVEL)) self.tbOutline = QToolButton(self) - self.tbOutline.setToolTip(self.tr("Novel Outline View")) + self.tbOutline.setToolTip(f"{0} [Ctrl+Shift+T]".format(self.tr("Novel Outline View"))) self.tbOutline.setIconSize(iconSize) self.tbOutline.clicked.connect(lambda: self.viewChangeRequested.emit(nwView.OUTLINE)) diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 2e8682aa..c672a1f2 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -247,6 +247,7 @@ class GuiMain(QMainWindow): self.mainMenu.requestDocInsert.connect(self._passDocumentInsert) self.mainMenu.requestDocInsertText.connect(self._passDocumentInsert) self.mainMenu.requestDocKeyWordInsert.connect(self.docEditor.insertKeyWord) + self.mainMenu.requestFocusChange.connect(self.switchFocus) self.sideBar.viewChangeRequested.connect(self._changeView) @@ -312,10 +313,6 @@ class GuiMain(QMainWindow): self.keyEscape.setKey(Qt.Key.Key_Escape) self.keyEscape.activated.connect(self._keyPressEscape) - self.keyTreeView = QShortcut(self) - self.keyTreeView.setKey("Ctrl+T") - self.keyTreeView.activated.connect(self._rotateTreeView) - # Check that config loaded fine self.reportConfErr() @@ -1097,25 +1094,6 @@ class GuiMain(QMainWindow): return True - def switchFocus(self, paneNo: nwWidget) -> None: - """Switch focus between main GUI views.""" - if paneNo == nwWidget.TREE: - tabIdx = self.projStack.currentIndex() - if tabIdx == self.idxProjView: - self.projView.setFocus() - elif tabIdx == self.idxNovelView: - self.novelView.setTreeFocus() - elif paneNo == nwWidget.EDITOR: - self._changeView(nwView.EDITOR) - self.docEditor.setFocus() - elif paneNo == nwWidget.VIEWER: - self._changeView(nwView.EDITOR) - self.docViewer.setFocus() - elif paneNo == nwWidget.OUTLINE: - self._changeView(nwView.OUTLINE) - self.outlineView.setTreeFocus() - return - def closeDocViewer(self, byUser: bool = True) -> bool: """Close the document view panel.""" self.docViewer.clearViewer() @@ -1193,6 +1171,33 @@ class GuiMain(QMainWindow): return + @pyqtSlot(nwWidget) + def switchFocus(self, paneNo: nwWidget) -> None: + """Switch focus between main GUI views.""" + if paneNo == nwWidget.TREE: + if self.projStack.currentWidget() is self.projView: + if self.projView.treeHasFocus(): + self._changeView(nwView.NOVEL) + self.novelView.setTreeFocus() + else: + self.projView.setTreeFocus() + else: + if self.novelView.treeHasFocus(): + self._changeView(nwView.PROJECT) + self.projView.setTreeFocus() + else: + self.novelView.setTreeFocus() + elif paneNo == nwWidget.EDITOR: + self._changeView(nwView.EDITOR) + self.docEditor.setFocus() + elif paneNo == nwWidget.VIEWER: + self._changeView(nwView.EDITOR) + self.docViewer.setFocus() + elif paneNo == nwWidget.OUTLINE: + self._changeView(nwView.OUTLINE) + self.outlineView.setTreeFocus() + return + ## # Private Slots ## @@ -1238,15 +1243,6 @@ class GuiMain(QMainWindow): self.mainStack.setCurrentWidget(self.outlineView) return - @pyqtSlot() - def _rotateTreeView(self) -> None: - """Change view to the next tree view.""" - if self.projStack.currentWidget() is self.projView: - self._changeView(nwView.NOVEL) - else: - self._changeView(nwView.PROJECT) - return - @pyqtSlot(nwDocAction) def _passDocumentAction(self, action: nwDocAction) -> None: """Pass on a document action to the document viewer if it has From a3f63530d743f5499f2ddfd64b1461934c6c589c Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 10 Nov 2023 18:57:48 +0100 Subject: [PATCH 4/5] Update documentation --- docs/source/usage_shortcuts.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/source/usage_shortcuts.rst b/docs/source/usage_shortcuts.rst index 4e931351..61dfa230 100644 --- a/docs/source/usage_shortcuts.rst +++ b/docs/source/usage_shortcuts.rst @@ -25,19 +25,19 @@ Main Window Shortcuts ":kbd:`F8`", "Toggle :guilabel:`Focus Mode`" ":kbd:`F9`", "Re-build the project index" ":kbd:`F11`", "Toggle full screen mode" - ":kbd:`Alt+1`", "Switch focus to the project tree (Windows :kbd:`Ctrl+Alt+1`)" - ":kbd:`Alt+2`", "Switch focus to document editor (Windows :kbd:`Ctrl+Alt+2`)" - ":kbd:`Alt+3`", "Switch focus to document viewer (Windows :kbd:`Ctrl+Alt+3`)" - ":kbd:`Alt+4`", "Switch focus to outline view (Windows :kbd:`Ctrl+Alt+4`)" ":kbd:`Ctrl+,`", "Open the :guilabel:`Preferences` dialog" + ":kbd:`Ctrl+E`", "Switch focus to the document editor" + ":kbd:`Ctrl+T`", "Switch focus to the project/novel tree" ":kbd:`Ctrl+Q`", "Exit novelWriter" ":kbd:`Ctrl+Shift+,`", "Open the :guilabel:`Project Settings` dialog" ":kbd:`Ctrl+Shift+O`", "Open a project" ":kbd:`Ctrl+Shift+S`", "Save the current project" + ":kbd:`Ctrl+Shift+T`", "Switch focus to the outline view" ":kbd:`Ctrl+Shift+W`", "Close the current project" ":kbd:`Shift+F1`", "Open the local user manual (PDF) if it is available" ":kbd:`Shift+F6`", "Open the :guilabel:`Project Details` dialog" + .. _a_kb_tree: Project Tree Shortcuts @@ -134,6 +134,7 @@ Other Editor Shortcuts ":kbd:`Ctrl+Shift+A`", "Select all text in the current paragraph" ":kbd:`Ctrl+Shift+I`", "Import text to the current document from a text file" + .. _a_kb_ins: Insert Shortcuts From 98c62f0912c626f46db7b8592ec3a7cad9ba5071 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 10 Nov 2023 19:03:30 +0100 Subject: [PATCH 5/5] Make Ctrl+Del the default for deleting item in project tree --- docs/source/usage_shortcuts.rst | 30 +++++++++++++++--------------- novelwriter/gui/mainmenu.py | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/source/usage_shortcuts.rst b/docs/source/usage_shortcuts.rst index 61dfa230..db9382cb 100644 --- a/docs/source/usage_shortcuts.rst +++ b/docs/source/usage_shortcuts.rst @@ -46,21 +46,21 @@ Project Tree Shortcuts .. csv-table:: :header: "Shortcut", "Description" - ":kbd:`F2`", "Edit the label of the selected item" - ":kbd:`Return`", "Open the selected document in the editor" - ":kbd:`Alt+Up`", "Jump or go to the previous item at same level in the tree" - ":kbd:`Alt+Down`", "Jump or go to the next item at same level in the tree" - ":kbd:`Alt+Left`", "Jump to the parent item in the tree" - ":kbd:`Alt+Right`", "Jump to the first child item in the project tree" - ":kbd:`Ctrl+.`", "Open the context menu on the selected item" - ":kbd:`Ctrl+L`", "Open the :guilabel:`Quick Links` menu" - ":kbd:`Ctrl+N`", "Open the :guilabel:`Create New Item` menu" - ":kbd:`Ctrl+O`", "Open selected document" - ":kbd:`Ctrl+R`", "Open the selected document in the viewer" - ":kbd:`Ctrl+Up`", "Move selected item one step up in the tree" - ":kbd:`Ctrl+Down`", "Move selected item one step down in the tree" - ":kbd:`Ctrl+Shift+Z`", "Undo the last move of a project item, if possible" - ":kbd:`Ctrl+Shift+Del`", "Move the selected item to Trash" + ":kbd:`F2`", "Edit the label of the selected item" + ":kbd:`Return`", "Open the selected document in the editor" + ":kbd:`Alt+Up`", "Jump or go to the previous item at same level in the tree" + ":kbd:`Alt+Down`", "Jump or go to the next item at same level in the tree" + ":kbd:`Alt+Left`", "Jump to the parent item in the tree" + ":kbd:`Alt+Right`", "Jump to the first child item in the project tree" + ":kbd:`Ctrl+.`", "Open the context menu on the selected item" + ":kbd:`Ctrl+L`", "Open the :guilabel:`Quick Links` menu" + ":kbd:`Ctrl+N`", "Open the :guilabel:`Create New Item` menu" + ":kbd:`Ctrl+O`", "Open selected document" + ":kbd:`Ctrl+R`", "Open the selected document in the viewer" + ":kbd:`Ctrl+Up`", "Move selected item one step up in the tree" + ":kbd:`Ctrl+Down`", "Move selected item one step down in the tree" + ":kbd:`Ctrl+Del`", "Move the selected item to Trash" + ":kbd:`Ctrl+Shift+Z`", "Undo the last move of a project item, if possible" .. _a_kb_editor: diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 06a137cd..a0420144 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -174,7 +174,7 @@ class GuiMainMenu(QMenuBar): # Project > Delete self.aDeleteItem = self.projMenu.addAction(self.tr("Delete Item")) - self.aDeleteItem.setShortcut("Ctrl+Shift+Del") + self.aDeleteItem.setShortcuts(["Ctrl+Del", "Ctrl+Shift+Del"]) # Latter is deprecated self.aDeleteItem.triggered.connect(lambda: self.mainGui.projView.requestDeleteItem(None)) # Project > Empty Trash