Replace the edit item dialog (#1082)

This commit is contained in:
Veronica Berglyd Olsen
2022-06-11 18:54:08 +02:00
committed by GitHub
14 changed files with 171 additions and 508 deletions
+1 -2
View File
@@ -25,7 +25,7 @@ The main shorcuts are as follows:
":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`." ":kbd:`Alt`:kbd:`4`", "Switch focus to outline view. On Windows, use :kbd:`Ctrl`:kbd:`Alt`:kbd:`4`."
":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer." ":kbd:`Alt`:kbd:`Left`", "Move backward in the view history of the document viewer."
":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer." ":kbd:`Alt`:kbd:`Right`", "Move forward in the view history of the document viewer."
":kbd:`Ctrl`:kbd:`.`", "Open menu to correct word under cursor." ":kbd:`Ctrl`:kbd:`.`", "Open the context menu in the project tree or the document editor."
":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog." ":kbd:`Ctrl`:kbd:`,`", "Open the :guilabel:`Preferences` dialog."
":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment." ":kbd:`Ctrl`:kbd:`/`", "Toggle block format as comment."
":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor." ":kbd:`Ctrl`:kbd:`0`", "Remove block formatting for block under cursor."
@@ -42,7 +42,6 @@ The main shorcuts are as follows:
":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)." ":kbd:`Ctrl`:kbd:`B`", "Format selected text, or word under cursor, with strong emphasis (bold)."
":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard." ":kbd:`Ctrl`:kbd:`C`", "Copy selected text to clipboard."
":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor." ":kbd:`Ctrl`:kbd:`D`", "Strikethrough selected text, or word under cursor."
":kbd:`Ctrl`:kbd:`E`", "If in the project tree, edit a document or folder settings."
":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected." ":kbd:`Ctrl`:kbd:`F`", "Open the search bar and search for the selected word, if any is selected."
":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document." ":kbd:`Ctrl`:kbd:`G`", "Find next occurrence of search word in current document."
":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:`=`.)"
+2 -2
View File
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
from novelwriter.dialogs.about import GuiAbout from novelwriter.dialogs.about import GuiAbout
from novelwriter.dialogs.docmerge import GuiDocMerge from novelwriter.dialogs.docmerge import GuiDocMerge
from novelwriter.dialogs.docsplit import GuiDocSplit from novelwriter.dialogs.docsplit import GuiDocSplit
from novelwriter.dialogs.itemeditor import GuiItemEditor from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.dialogs.preferences import GuiPreferences from novelwriter.dialogs.preferences import GuiPreferences
from novelwriter.dialogs.projdetails import GuiProjectDetails from novelwriter.dialogs.projdetails import GuiProjectDetails
from novelwriter.dialogs.projload import GuiProjectLoad from novelwriter.dialogs.projload import GuiProjectLoad
@@ -35,7 +35,7 @@ __all__ = [
"GuiAbout", "GuiAbout",
"GuiDocMerge", "GuiDocMerge",
"GuiDocSplit", "GuiDocSplit",
"GuiItemEditor", "GuiEditLabel",
"GuiPreferences", "GuiPreferences",
"GuiProjectDetails", "GuiProjectDetails",
"GuiProjectLoad", "GuiProjectLoad",
+84
View File
@@ -0,0 +1,84 @@
"""
novelWriter Edit Label Dialog
===============================
A simple dialog for editing a label
File History:
Created: 2022-06-11 [1.7b1]
This file is a part of novelWriter
Copyright 20182022, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import novelwriter
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QLineEdit, QLabel, QDialogButtonBox, QHBoxLayout
)
logger = logging.getLogger(__name__)
class GuiEditLabel(QDialog):
def __init__(self, parent, text=""):
QDialog.__init__(self, parent=parent)
self.setObjectName("GuiEditLabel")
self.setWindowTitle(self.tr("Item Label"))
mVd = novelwriter.CONFIG.pxInt(220)
mSp = novelwriter.CONFIG.pxInt(12)
# Item Label
self.labelValue = QLineEdit()
self.labelValue.setMinimumWidth(mVd)
self.labelValue.setMaxLength(200)
self.labelValue.setText(text)
self.labelValue.selectAll()
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
# Assemble
self.innerBox = QHBoxLayout()
self.innerBox.addWidget(QLabel(self.tr("Label")), 0)
self.innerBox.addWidget(self.labelValue, 1)
self.innerBox.setSpacing(mSp)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(mSp)
self.outerBox.addLayout(self.innerBox, 1)
self.outerBox.addWidget(self.buttonBox, 0)
self.setLayout(self.outerBox)
return
@property
def itemLabel(self):
return self.labelValue.text()
@classmethod
def getLabel(cls, parent, text):
cls = GuiEditLabel(parent, text=text)
cls.exec_()
return cls.itemLabel, cls.result() == QDialog.Accepted
# END Class GuiEditLabel
-203
View File
@@ -1,203 +0,0 @@
"""
novelWriter GUI Item Editor
=============================
GUI class for the item editor dialog
File History:
Created: 2019-04-27 [0.0.1]
This file is a part of novelWriter
Copyright 20182022, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import novelwriter
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QGridLayout, QLineEdit, QComboBox, QLabel,
QDialogButtonBox
)
from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.constants import trConst, nwLabels
from novelwriter.gui.custom import QSwitch
logger = logging.getLogger(__name__)
class GuiItemEditor(QDialog):
def __init__(self, mainGui, tHandle):
QDialog.__init__(self, mainGui)
logger.debug("Initialising GuiItemEditor ...")
self.setObjectName("GuiItemEditor")
self.mainConf = novelwriter.CONFIG
self.mainGui = mainGui
self.theProject = mainGui.theProject
##
# Build GUI
##
self.theItem = self.theProject.tree[tHandle]
if self.theItem is None:
self.close()
return
self.setWindowTitle(self.tr("Item Settings"))
mVd = self.mainConf.pxInt(220)
mSp = self.mainConf.pxInt(16)
vSp = self.mainConf.pxInt(4)
# Item Label
self.editName = QLineEdit()
self.editName.setMinimumWidth(mVd)
self.editName.setMaxLength(200)
# Item Status
self.editStatus = QComboBox()
self.editStatus.setMinimumWidth(mVd)
if self.theItem.isNovelLike():
for key, entry in self.theProject.statusItems.items():
self.editStatus.addItem(entry["icon"], entry["name"], key)
index = self.editStatus.findData(self.theItem.itemStatus)
if index != -1:
self.editStatus.setCurrentIndex(index)
else:
for key, entry in self.theProject.importItems.items():
self.editStatus.addItem(entry["icon"], entry["name"], key)
index = self.editStatus.findData(self.theItem.itemImport)
if index != -1:
self.editStatus.setCurrentIndex(index)
# Item Layout
self.editLayout = QComboBox()
self.editLayout.setMinimumWidth(mVd)
validLayouts = []
if self.theItem.itemType == nwItemType.FILE:
if self.theItem.documentAllowed():
validLayouts.append(nwItemLayout.DOCUMENT)
validLayouts.append(nwItemLayout.NOTE)
else:
validLayouts.append(nwItemLayout.NO_LAYOUT)
self.editLayout.setEnabled(False)
for itemLayout in nwItemLayout:
if itemLayout in validLayouts:
self.editLayout.addItem(trConst(nwLabels.LAYOUT_NAME[itemLayout]), itemLayout)
index = self.editLayout.findData(self.theItem.itemLayout)
if index != -1:
self.editLayout.setCurrentIndex(index)
# Export Switch
self.textExport = QLabel(self.tr("Include when building project"))
self.editExport = QSwitch()
if self.theItem.itemType == nwItemType.FILE:
self.editExport.setEnabled(True)
self.editExport.setChecked(self.theItem.isExported)
else:
self.editExport.setEnabled(False)
self.editExport.setChecked(False)
# Buttons
self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.buttonBox.accepted.connect(self._doSave)
self.buttonBox.rejected.connect(self._doClose)
# Set Current Values
self.editName.setText(self.theItem.itemName)
self.editName.selectAll()
##
# Assemble
##
nameLabel = QLabel(self.tr("Label"))
statusLabel = QLabel(self.tr("Status"))
layoutLabel = QLabel(self.tr("Layout"))
self.mainForm = QGridLayout()
self.mainForm.setVerticalSpacing(vSp)
self.mainForm.setHorizontalSpacing(mSp)
self.mainForm.addWidget(nameLabel, 0, 0, 1, 1)
self.mainForm.addWidget(self.editName, 0, 1, 1, 2)
self.mainForm.addWidget(statusLabel, 1, 0, 1, 1)
self.mainForm.addWidget(self.editStatus, 1, 1, 1, 2)
self.mainForm.addWidget(layoutLabel, 2, 0, 1, 1)
self.mainForm.addWidget(self.editLayout, 2, 1, 1, 2)
self.mainForm.addWidget(self.textExport, 3, 0, 1, 2)
self.mainForm.addWidget(self.editExport, 3, 2, 1, 1)
self.mainForm.setColumnStretch(0, 0)
self.mainForm.setColumnStretch(1, 1)
self.mainForm.setColumnStretch(2, 0)
self.outerBox = QVBoxLayout()
self.outerBox.setSpacing(mSp)
self.outerBox.addLayout(self.mainForm)
self.outerBox.addStretch(1)
self.outerBox.addWidget(self.buttonBox)
self.setLayout(self.outerBox)
self.rejected.connect(self._doClose)
logger.debug("GuiItemEditor initialisation complete")
return
##
# Slots
##
@pyqtSlot()
def _doSave(self):
"""Save the setting to the item.
"""
logger.verbose("ItemEditor save button clicked")
itemName = self.editName.text()
itemStatus = self.editStatus.currentData()
itemLayout = self.editLayout.currentData()
isExported = self.editExport.isChecked()
self.theItem.setName(itemName)
self.theItem.setImportStatus(itemStatus)
self.theItem.setLayout(itemLayout)
self.theItem.setExported(isExported)
self.theProject.setProjectChanged(True)
self.accept()
self.close()
return
@pyqtSlot()
def _doClose(self):
"""Close the dialog without saving the settings.
"""
logger.verbose("ItemEditor cancel button clicked")
self.close()
return
# END Class GuiItemEditor
+1 -1
View File
@@ -2746,7 +2746,7 @@ class GuiDocEditHeader(QWidget):
def _editDocument(self): def _editDocument(self):
"""Open the edit item dialog from the main GUI. """Open the edit item dialog from the main GUI.
""" """
self.mainGui.editItem(self._docHandle) self.mainGui.editItemLabel(self._docHandle)
return return
def _searchDocument(self): def _searchDocument(self):
+3 -3
View File
@@ -163,9 +163,9 @@ class GuiMainMenu(QMenuBar):
self.projMenu.addSeparator() self.projMenu.addSeparator()
# Project > Edit # Project > Edit
self.aEditItem = QAction(self.tr("Edit Item"), self) self.aEditItem = QAction(self.tr("Rename Item"), self)
self.aEditItem.setShortcuts(["Ctrl+E", "F2"]) self.aEditItem.setShortcuts(["F2"])
self.aEditItem.triggered.connect(lambda: self.mainGui.editItem(None)) self.aEditItem.triggered.connect(lambda: self.mainGui.editItemLabel(None))
self.projMenu.addAction(self.aEditItem) self.projMenu.addAction(self.aEditItem)
# Project > Delete # Project > Delete
+32 -24
View File
@@ -34,15 +34,15 @@ 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, QPalette from PyQt5.QtGui import QIcon, QPalette
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QAbstractItemView, QDialog, QFrame, QHBoxLayout, QHeaderView, QInputDialog, QAbstractItemView, QFrame, QHBoxLayout, QHeaderView, QLabel,
QLabel, QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QMenu, QShortcut, QSizePolicy, QToolButton, QTreeWidget, QTreeWidgetItem,
QTreeWidgetItem, QVBoxLayout, QWidget QVBoxLayout, QWidget
) )
from novelwriter.core import NWDoc from novelwriter.core import NWDoc
from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert from novelwriter.enum import nwDocMode, nwItemType, nwItemClass, nwItemLayout, nwAlert
from novelwriter.dialogs.itemeditor import GuiItemEditor
from novelwriter.constants import trConst, nwLabels from novelwriter.constants import trConst, nwLabels
from novelwriter.dialogs.editlabel import GuiEditLabel
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -97,9 +97,14 @@ class GuiProjectView(QWidget):
self.keyUndoMv.setContext(Qt.WidgetShortcut) self.keyUndoMv.setContext(Qt.WidgetShortcut)
self.keyUndoMv.activated.connect(lambda: self.projTree.undoLastMove()) self.keyUndoMv.activated.connect(lambda: self.projTree.undoLastMove())
self.keyContext = QShortcut(self.projTree)
self.keyContext.setKey("Ctrl+.")
self.keyContext.setContext(Qt.WidgetShortcut)
self.keyContext.activated.connect(lambda: self.projTree.openContextOnSelected())
# Function Mappings # Function Mappings
self.revealNewTreeItem = self.projTree.revealNewTreeItem self.revealNewTreeItem = self.projTree.revealNewTreeItem
self.editTreeItem = self.projTree.editTreeItem self.renameTreeItem = self.projTree.renameTreeItem
self.getTreeFromHandle = self.projTree.getTreeFromHandle self.getTreeFromHandle = self.projTree.getTreeFromHandle
self.emptyTrash = self.projTree.emptyTrash self.emptyTrash = self.projTree.emptyTrash
self.deleteItem = self.projTree.deleteItem self.deleteItem = self.projTree.deleteItem
@@ -470,7 +475,7 @@ class GuiProjectTree(QTreeWidget):
else: else:
newLabel = self.tr("New Folder") newLabel = self.tr("New Folder")
newLabel, dlgOk = QInputDialog.getText(self, "", self.tr("Label:"), text=newLabel) newLabel, dlgOk = GuiEditLabel.getLabel(self, text=newLabel)
if not dlgOk: if not dlgOk:
logger.info("New item creation cancelled by user") logger.info("New item creation cancelled by user")
return False return False
@@ -565,23 +570,20 @@ class GuiProjectTree(QTreeWidget):
return True return True
def editTreeItem(self, tHandle): def renameTreeItem(self, tHandle):
"""Open the edit item dialog. """Open a dialog to edit the label of an item.
""" """
tItem = self.theProject.tree[tHandle] tItem = self.theProject.tree[tHandle]
if tItem is None: if tItem is None:
return False return False
if tItem.itemType == nwItemType.NO_TYPE:
return False
logger.verbose("Requesting change to item '%s'", tHandle) newLabel, dlgOk = GuiEditLabel.getLabel(self, text=tItem.itemName)
dlgProj = GuiItemEditor(self, tHandle) if dlgOk:
dlgProj.exec_() tItem.setName(newLabel)
if dlgProj.result() == QDialog.Accepted:
self.setTreeItemValues(tHandle) self.setTreeItemValues(tHandle)
self._alertTreeChange(tHandle=tHandle, flush=False) self._alertTreeChange(tHandle=tHandle, flush=False)
return True return
def saveTreeOrder(self): def saveTreeOrder(self):
"""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
@@ -916,9 +918,6 @@ class GuiProjectTree(QTreeWidget):
def setSelectedHandle(self, tHandle, doScroll=False): def setSelectedHandle(self, tHandle, doScroll=False):
"""Set a specific handle as the selected item. """Set a specific handle as the selected item.
""" """
if tHandle not in self._treeMap:
return False
tItem = self._getTreeItem(tHandle) tItem = self._getTreeItem(tHandle)
if tItem is None: if tItem is None:
return False return False
@@ -932,6 +931,15 @@ class GuiProjectTree(QTreeWidget):
return True return True
def openContextOnSelected(self):
"""Open the context menu on the current selected item.
"""
selItem = self.selectedItems()
if selItem:
pos = self.visualItemRect(selItem[0]).center()
return self._openContextMenu(pos)
return False
def changedSince(self, checkTime): def changedSince(self, checkTime):
"""Check if the tree has changed since a given time. """Check if the tree has changed since a given time.
""" """
@@ -1019,6 +1027,10 @@ class GuiProjectTree(QTreeWidget):
# Edit Item Settings # Edit Item Settings
# ================== # ==================
ctxMenu.addAction(
self.tr("Change Label"), lambda: self.renameTreeItem(tHandle)
)
if isFile: if isFile:
ctxMenu.addAction( ctxMenu.addAction(
self.tr("Toggle Exported"), lambda: self._toggleItemExported(tHandle) self.tr("Toggle Exported"), lambda: self._toggleItemExported(tHandle)
@@ -1057,12 +1069,8 @@ class GuiProjectTree(QTreeWidget):
ctxMenu.addSeparator() ctxMenu.addSeparator()
# Major Item Actions # Delete Item
# ================== # ===========
ctxMenu.addAction(
self.tr("Edit Item Settings"), lambda: self.editTreeItem(tHandle)
)
if tItem.itemClass == nwItemClass.TRASH or tItem.itemType == nwItemType.ROOT: if tItem.itemClass == nwItemClass.TRASH or tItem.itemType == nwItemType.ROOT:
ctxMenu.addAction( ctxMenu.addAction(
+2 -2
View File
@@ -804,7 +804,7 @@ class GuiMain(QMainWindow):
return True return True
def editItem(self, tHandle=None): def editItemLabel(self, tHandle=None):
"""Open the edit item dialog. """Open the edit item dialog.
""" """
if not self.hasProject: if not self.hasProject:
@@ -817,7 +817,7 @@ class GuiMain(QMainWindow):
else: else:
tHandle = self.projView.getSelectedHandle() tHandle = self.projView.getSelectedHandle()
if tHandle: if tHandle:
return self.projView.editTreeItem(tHandle) return self.projView.renameTreeItem(tHandle)
return False return False
+22 -5
View File
@@ -24,11 +24,7 @@ import pytest
from PyQt5.QtCore import QItemSelectionModel from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox from PyQt5.QtWidgets import QAction, QListWidgetItem, QDialog, QMessageBox
from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates from novelwriter.dialogs import GuiQuoteSelect, GuiUpdates, GuiEditLabel
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui @pytest.mark.gui
@@ -101,3 +97,24 @@ def testDlgOther_Updates(qtbot, monkeypatch, nwGUI):
nwUpdate._doClose() nwUpdate._doClose()
# END Test testDlgOther_Updates # END Test testDlgOther_Updates
@pytest.mark.gui
def testDlgOther_EditLabel(qtbot, monkeypatch):
"""Test the label editor dialog.
"""
monkeypatch.setattr(GuiEditLabel, "exec_", lambda *a: None)
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Accepted)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
assert dlgOk is True
assert newLabel == "Hello World"
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "result", lambda *a: QDialog.Rejected)
newLabel, dlgOk = GuiEditLabel.getLabel(None, text="Hello World")
assert dlgOk is False
assert newLabel == "Hello World"
# END Test testDlgOther_EditLabel
+3 -4
View File
@@ -25,10 +25,10 @@ import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile, buildTestProject from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog, QInputDialog from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.dialogs import GuiDocMerge, GuiItemEditor from novelwriter.dialogs import GuiDocMerge, GuiEditLabel
from novelwriter.core.tree import NWTree from novelwriter.core.tree import NWTree
@@ -39,7 +39,7 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project # Create a new project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
@@ -55,7 +55,6 @@ def testDlgMerge_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
hMergedDoc = "0000000000023" hMergedDoc = "0000000000023"
# Add Project Content # Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True) nwGUI.projView.projTree._getTreeItem(hChapterDir).setSelected(True)
+3 -4
View File
@@ -25,10 +25,10 @@ import pytest
from mock import causeOSError from mock import causeOSError
from tools import getGuiItem, readFile, writeFile, buildTestProject from tools import getGuiItem, readFile, writeFile, buildTestProject
from PyQt5.QtWidgets import QAction, QMessageBox, QDialog, QInputDialog from PyQt5.QtWidgets import QAction, QMessageBox
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.dialogs import GuiDocSplit, GuiItemEditor from novelwriter.dialogs import GuiDocSplit, GuiEditLabel
from novelwriter.core.tree import NWTree from novelwriter.core.tree import NWTree
from novelwriter.core.document import NWDoc from novelwriter.core.document import NWDoc
@@ -40,7 +40,7 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
# Block message box # Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Ok)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create a new project # Create a new project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
@@ -59,7 +59,6 @@ def testDlgSplit_Main(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
hSceneFive = "0000000000028" hSceneFive = "0000000000028"
# Add Project Content # Add Project Content
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: QDialog.Accepted)
nwGUI.switchFocus(nwWidget.TREE) nwGUI.switchFocus(nwWidget.TREE)
nwGUI.projView.projTree.clearSelection() nwGUI.projView.projTree.clearSelection()
nwGUI.projView.projTree._getTreeItem(hNovelRoot).setSelected(True) nwGUI.projView.projTree._getTreeItem(hNovelRoot).setSelected(True)
-246
View File
@@ -1,246 +0,0 @@
"""
novelWriter Item Editor Dialog Class Tester
=============================================
This file is a part of novelWriter
Copyright 20182022, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
from tools import getGuiItem, buildTestProject
from PyQt5.QtWidgets import QAction, QDialog, QMessageBox, QInputDialog
from novelwriter.enum import nwItemLayout, nwItemType
from novelwriter.dialogs import GuiItemEditor
from novelwriter.core.tree import NWTree
from novelwriter.gui.projtree import GuiProjectTree
statusKeys = ["s000000", "s000001", "s000002", "s000003"]
importKeys = ["i000004", "i000005", "i000006", "i000007"]
@pytest.mark.gui
def testDlgItemEditor_Dialog(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test launching the item editor dialog from GuiMain.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
# Block Dialog exec_
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *a: None)
# Open Editor wo/Project
assert nwGUI.editItem() is False
# Create and Open Project
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
# No Selection
nwGUI.projView.projTree.clearSelection()
assert nwGUI.editItem() is False
# Force opening from editor
assert nwGUI.openDocument(tHandle)
nwGUI.isFocusMode = True
# Block Tree Lookup
with monkeypatch.context() as mp:
mp.setattr(NWTree, "__getitem__", lambda *a: None)
assert nwGUI.editItem() is False
# Invalid Type
nwGUI.theProject.tree[tHandle]._type = nwItemType.NO_TYPE
assert nwGUI.editItem() is False
nwGUI.theProject.tree[tHandle]._type = nwItemType.FILE
# Open Properly
assert nwGUI.editItem() is True
qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000)
itemEdit = getGuiItem("GuiItemEditor")
assert itemEdit is not None
itemEdit.close()
# Open Via Menu
with monkeypatch.context() as mp:
mp.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
nwGUI.mainMenu.aEditItem.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiItemEditor") is not None, timeout=1000)
itemEdit = getGuiItem("GuiItemEditor")
assert itemEdit is not None
itemEdit.close()
nwGUI.isFocusMode = False
# END Test testDlgItemEditor_Dialog
@pytest.mark.gui
def testDlgItemEditor_Novel(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a novel document.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
tHandle = "000000000000f"
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.openDocument(tHandle) is True
# Check that an invalid handle is managed
itemEdit = GuiItemEditor(nwGUI, "whatever")
itemEdit.show()
itemEdit._doClose()
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, tHandle)
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Scene"
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.DOCUMENT
assert itemEdit.editExport.isChecked() is True
# Change Settings
layoutIdx = itemEdit.editLayout.findData(nwItemLayout.NOTE)
itemEdit.editName.setText("Great Scene")
itemEdit.editStatus.setCurrentIndex(1)
itemEdit.editLayout.setCurrentIndex(layoutIdx)
itemEdit.editExport.setChecked(False)
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Great Scene"
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
# Check that the editor header is updated
nwGUI.docEditor.updateDocInfo(tHandle)
assert nwGUI.docEditor.docHeader.theTitle.text() == "Novel New Chapter Great Scene"
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Dialog
@pytest.mark.gui
def testDlgItemEditor_Note(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a project note.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
assert nwGUI.theProject.importItems.name(importKeys[0]) == "New"
assert nwGUI.theProject.importItems.name(importKeys[1]) == "Minor"
# Create Note
nwGUI.projView.projTree.clearSelection()
nwGUI.projView.projTree._getTreeItem("000000000000a").setSelected(True)
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
# Open Note
assert nwGUI.openDocument("0000000000010")
# Edit a Document
itemEdit = GuiItemEditor(nwGUI, "0000000000010")
itemEdit.show()
# Check Existing Settings
assert itemEdit.editName.text() == "New Note"
assert itemEdit.editStatus.currentData() == importKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NOTE
assert itemEdit.editExport.isChecked() is True
# Change Settings
itemEdit.editName.setText("New Character")
itemEdit.editStatus.setCurrentIndex(1)
itemEdit.editExport.setChecked(False)
itemEdit._doSave()
# Check New Settings
assert itemEdit.theItem.itemName == "New Character"
assert itemEdit.theItem.itemStatus == statusKeys[0]
assert itemEdit.theItem.itemImport == importKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NOTE
assert itemEdit.theItem.isExported is False
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Note
@pytest.mark.gui
def testDlgItemEditor_Folder(qtbot, monkeypatch, nwGUI, fncProj, mockRnd):
"""Test the item editor dialog for a folder.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
# Create Project and Open Document
buildTestProject(nwGUI, fncProj)
# Edit a Folder
itemEdit = GuiItemEditor(nwGUI, "000000000000d")
itemEdit.show()
assert nwGUI.theProject.statusItems.name(statusKeys[0]) == "New"
assert nwGUI.theProject.statusItems.name(statusKeys[1]) == "Note"
# Check Existing Settings
assert itemEdit.editName.text() == "New Chapter"
assert itemEdit.editStatus.currentData() == statusKeys[0]
assert itemEdit.editLayout.currentData() == nwItemLayout.NO_LAYOUT
assert itemEdit.editExport.isChecked() is False
assert itemEdit.editLayout.isEnabled() is False
assert itemEdit.editExport.isEnabled() is False
# Change Settings
itemEdit.editName.setText("Chapter One")
itemEdit.editStatus.setCurrentIndex(1)
# Check New Settings
itemEdit._doSave()
assert itemEdit.theItem.itemName == "Chapter One"
assert itemEdit.theItem.itemStatus == statusKeys[1]
assert itemEdit.theItem.itemLayout == nwItemLayout.NO_LAYOUT
assert itemEdit.theItem.isExported is False
itemEdit.close()
del itemEdit
# qtbot.stopForInteraction()
# END Test testDlgItemEditor_Folder
+4 -5
View File
@@ -26,13 +26,13 @@ from shutil import copyfile
from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile from tools import cmpFiles, buildTestProject, XML_IGNORE, writeFile
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox, QDialog, QInputDialog from PyQt5.QtWidgets import QMessageBox, QInputDialog
from novelwriter.gui import GuiDocEditor, GuiNovelTree, GuiOutlineView from novelwriter.gui import GuiDocEditor, GuiNovelTree, GuiOutlineView
from novelwriter.enum import nwItemType, nwWidget from novelwriter.enum import nwItemType, nwWidget
from novelwriter.tools import GuiProjectWizard from novelwriter.tools import GuiProjectWizard
from novelwriter.gui.projtree import GuiProjectTree from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.dialogs.itemeditor import GuiItemEditor from novelwriter.dialogs import GuiEditLabel
keyDelay = 2 keyDelay = 2
typeDelay = 1 typeDelay = 1
@@ -57,7 +57,7 @@ def testGuiMain_ProjectBlocker(monkeypatch, nwGUI):
assert nwGUI.mergeDocuments() is False assert nwGUI.mergeDocuments() is False
assert nwGUI.splitDocument() is False assert nwGUI.splitDocument() is False
assert nwGUI.openSelectedItem() is False assert nwGUI.openSelectedItem() is False
assert nwGUI.editItem() is False assert nwGUI.editItemLabel() is False
assert nwGUI.requestNovelTreeRefresh() is False assert nwGUI.requestNovelTreeRefresh() is False
assert nwGUI.rebuildIndex() is False assert nwGUI.rebuildIndex() is False
assert nwGUI.showProjectSettingsDialog() is False assert nwGUI.showProjectSettingsDialog() is False
@@ -169,11 +169,10 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir, mock
# Block message box # Block message box
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(GuiItemEditor, "exec_", lambda *a: None)
monkeypatch.setattr(GuiItemEditor, "result", lambda *a: QDialog.Accepted)
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True) monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *a: True)
monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True)) monkeypatch.setattr(QInputDialog, "getText", lambda *a, text: (text, True))
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
# Create new, save, close project # Create new, save, close project
buildTestProject(nwGUI, fncProj) buildTestProject(nwGUI, fncProj)
+14 -7
View File
@@ -24,10 +24,11 @@ import os
from tools import buildTestProject from tools import buildTestProject
from PyQt5.QtWidgets import QMessageBox, QInputDialog, QMenu from PyQt5.QtWidgets import QMessageBox, QMenu
from novelwriter.gui.projtree import GuiProjectTree
from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass
from novelwriter.dialogs import GuiEditLabel
from novelwriter.gui.projtree import GuiProjectTree
@pytest.mark.gui @pytest.mark.gui
@@ -39,7 +40,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
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(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.projView nwTree = nwGUI.projView
@@ -125,7 +126,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockRnd)
# Cancel during creation # Cancel during creation
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(QInputDialog, "getText", lambda *a, **k: ("", False)) mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("", False))
nwTree.setSelectedHandle("0000000000013") nwTree.setSelectedHandle("0000000000013")
assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False assert nwTree.projTree.newTreeItem(nwItemType.FILE) is False
@@ -162,7 +163,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, fncDir, mockRnd):
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
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(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.projView nwTree = nwGUI.projView
@@ -277,7 +278,7 @@ def testGuiProjTree_DeleteItems(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
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(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
nwTree = nwGUI.projView nwTree = nwGUI.projView
@@ -466,7 +467,7 @@ def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes)
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(GuiEditLabel, "getLabel", lambda *a, text: (text, True))
monkeypatch.setattr(QMenu, "exec_", lambda *a: None) monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
# Create a project # Create a project
@@ -510,6 +511,12 @@ def testGuiProjTree_ContextMenu(qtbot, caplog, monkeypatch, nwGUI, fncDir, mockR
assert projTree._openContextMenu(itemPos(hCharRoot)) is True assert projTree._openContextMenu(itemPos(hCharRoot)) is True
assert projTree._openContextMenu(itemPos(hCharNote)) is True assert projTree._openContextMenu(itemPos(hCharNote)) is True
# Check the keyboard shortcut handler as well
projTree.setSelectedHandle(hNovelRoot)
assert projTree.openContextOnSelected() is True
projTree.clearSelection()
assert projTree.openContextOnSelected() is False
# Direct Edit Functions # Direct Edit Functions
# ===================== # =====================
# Trigger the dedicated functions the menu entries connect to # Trigger the dedicated functions the menu entries connect to