Add focus widget hinting (#1940)
This commit is contained in:
@@ -15,6 +15,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Python Setup
|
||||
|
||||
@@ -132,6 +132,7 @@ A GUI theme ``.conf`` file consists of the following settings:
|
||||
|
||||
[GUI]
|
||||
helptext = 0, 0, 0
|
||||
fadedtext = 128, 128, 128
|
||||
errortext = 255, 0, 0
|
||||
statusnone = 120, 120, 120
|
||||
statussaved = 2, 133, 37
|
||||
@@ -149,7 +150,7 @@ colour values are RGB numbers on the format ``r, g, b`` where each is an integer
|
||||
not defined, it is computed as a colour between the ``window`` and ``windowtext`` colour.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
The ``errortext`` theme colour entry was added.
|
||||
The ``fadedtext`` and ``errortext`` theme colour entries were added.
|
||||
|
||||
|
||||
Custom Syntax Theme
|
||||
|
||||
@@ -25,6 +25,7 @@ linkvisited = 50, 0, 80
|
||||
|
||||
[GUI]
|
||||
helptext = 97, 97, 97
|
||||
fadedtext = 97, 97, 97
|
||||
errortext = 255, 77, 77
|
||||
statusnone = 50, 50, 50
|
||||
statussaved = 77, 255, 77
|
||||
|
||||
@@ -26,6 +26,7 @@ linkvisited = 102, 153, 204
|
||||
|
||||
[GUI]
|
||||
helptext = 164, 164, 164
|
||||
fadedtext = 148, 148, 148
|
||||
errortext = 255, 164, 164
|
||||
statusnone = 150, 152, 150
|
||||
statussaved = 39, 135, 78
|
||||
|
||||
@@ -26,6 +26,7 @@ linkvisited = 66, 113, 174
|
||||
|
||||
[GUI]
|
||||
helptext = 92, 92, 92
|
||||
fadedtext = 108, 108, 108
|
||||
errortext = 255, 92, 92
|
||||
statusnone = 120, 120, 120
|
||||
statussaved = 200, 15, 39
|
||||
|
||||
@@ -41,6 +41,7 @@ linkvisited = 139, 233, 253
|
||||
|
||||
[GUI]
|
||||
helptext = 204, 172, 249
|
||||
fadedtext = 98, 114, 164
|
||||
errortext = 255, 85, 85
|
||||
statusnone = 98, 114, 164
|
||||
statussaved = 80, 250, 123
|
||||
|
||||
@@ -25,6 +25,7 @@ linkvisited = 38, 139, 210
|
||||
|
||||
[GUI]
|
||||
helptext = 166, 161, 149
|
||||
fadedtext = 166, 161, 149
|
||||
errortext = 255, 161, 149
|
||||
statusnone = 88, 110, 117
|
||||
statussaved = 42, 161, 152
|
||||
|
||||
@@ -25,6 +25,7 @@ linkvisited = 38, 139, 210
|
||||
|
||||
[GUI]
|
||||
helptext = 78, 91, 95
|
||||
fadedtext = 78, 91, 95
|
||||
errortext = 255, 91, 95
|
||||
statusnone = 88, 110, 117
|
||||
statussaved = 42, 161, 152
|
||||
|
||||
@@ -75,7 +75,7 @@ class GuiAbout(NDialog):
|
||||
|
||||
# Credits
|
||||
self.lblCredits = NColourLabel(
|
||||
self.tr("Credits"), scale=1.6, parent=self, bold=True
|
||||
self.tr("Credits"), self, scale=1.6, bold=True
|
||||
)
|
||||
|
||||
self.txtCredits = QTextBrowser(self)
|
||||
|
||||
@@ -58,7 +58,7 @@ class GuiDocMerge(NDialog):
|
||||
self.headLabel.setFont(SHARED.theme.guiFontB)
|
||||
self.helpLabel = NColourLabel(
|
||||
self.tr("Drag and drop items to change the order, or uncheck to exclude."),
|
||||
SHARED.theme.helpText, parent=self, wrap=True
|
||||
self, color=SHARED.theme.helpText, wrap=True
|
||||
)
|
||||
|
||||
iPx = SHARED.theme.baseIconHeight
|
||||
|
||||
@@ -62,7 +62,7 @@ class GuiDocSplit(NDialog):
|
||||
self.headLabel.setFont(SHARED.theme.guiFontB)
|
||||
self.helpLabel = NColourLabel(
|
||||
self.tr("Select the maximum level to split into files."),
|
||||
SHARED.theme.helpText, parent=self, wrap=True
|
||||
self, color=SHARED.theme.helpText, wrap=True
|
||||
)
|
||||
|
||||
# Values
|
||||
|
||||
@@ -66,8 +66,8 @@ class GuiPreferences(NDialog):
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Preferences"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
self.tr("Preferences"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
|
||||
# Search Box
|
||||
|
||||
@@ -76,8 +76,8 @@ class GuiProjectSettings(NDialog):
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Project Settings"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
self.tr("Project Settings"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
|
||||
# SideBar
|
||||
@@ -345,7 +345,7 @@ class _StatusPage(NFixedPage):
|
||||
|
||||
# Title
|
||||
self.pageTitle = NColourLabel(
|
||||
pageLabel, SHARED.theme.helpText, parent=self,
|
||||
pageLabel, self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
@@ -637,8 +637,8 @@ class _ReplacePage(NFixedPage):
|
||||
|
||||
# Title
|
||||
self.pageTitle = NColourLabel(
|
||||
self.tr("Text Auto-Replace for Preview and Build"),
|
||||
SHARED.theme.helpText, parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
self.tr("Text Auto-Replace for Preview and Build"), self,
|
||||
color=SHARED.theme.helpText, scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
# List Box
|
||||
|
||||
@@ -69,7 +69,7 @@ class GuiWordList(NDialog):
|
||||
|
||||
# Header
|
||||
self.headLabel = NColourLabel(
|
||||
self.tr("Project Word List"), SHARED.theme.helpText, parent=self,
|
||||
self.tr("Project Word List"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
|
||||
+4
-5
@@ -148,12 +148,11 @@ class nwView(Enum):
|
||||
SEARCH = 4
|
||||
|
||||
|
||||
class nwWidget(Enum):
|
||||
class nwFocus(Enum):
|
||||
|
||||
TREE = 1
|
||||
EDITOR = 2
|
||||
VIEWER = 3
|
||||
OUTLINE = 4
|
||||
TREE = 1
|
||||
DOCUMENT = 2
|
||||
OUTLINE = 3
|
||||
|
||||
|
||||
class nwOutline(Enum):
|
||||
|
||||
@@ -34,7 +34,7 @@ from PyQt5.QtWidgets import (
|
||||
QVBoxLayout, QWidget
|
||||
)
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter import CONFIG, SHARED
|
||||
|
||||
DEFAULT_SCALE = 0.9
|
||||
|
||||
@@ -203,7 +203,7 @@ class NScrollableForm(QScrollArea):
|
||||
|
||||
if helpText:
|
||||
qHelp = NColourLabel(
|
||||
str(helpText), color=self._helpCol, parent=self,
|
||||
str(helpText), self, color=self._helpCol,
|
||||
scale=self._fontScale, wrap=True, indent=self._indent
|
||||
)
|
||||
labelBox = QVBoxLayout()
|
||||
@@ -252,12 +252,21 @@ class NColourLabel(QLabel):
|
||||
HELP_SCALE = DEFAULT_SCALE
|
||||
HEADER_SCALE = 1.25
|
||||
|
||||
def __init__(self, text: str, color: QColor | None = None, parent: QWidget | None = None,
|
||||
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
|
||||
bold: bool = False) -> None:
|
||||
_state = None
|
||||
|
||||
def __init__(
|
||||
self, text: str, parent: QWidget, *,
|
||||
color: QColor | None = None, faded: QColor | None = None,
|
||||
scale: float = HELP_SCALE, wrap: bool = False, indent: int = 0,
|
||||
bold: bool = False
|
||||
) -> None:
|
||||
super().__init__(text, parent=parent)
|
||||
|
||||
font = self.font()
|
||||
default = self.palette().windowText().color()
|
||||
self._color = color or default
|
||||
self._faded = faded or default
|
||||
|
||||
font = SHARED.theme.guiFont
|
||||
font.setPointSizeF(scale*font.pointSizeF())
|
||||
font.setWeight(QFont.Weight.Bold if bold else QFont.Weight.Normal)
|
||||
if color:
|
||||
@@ -268,9 +277,19 @@ class NColourLabel(QLabel):
|
||||
self.setFont(font)
|
||||
self.setIndent(indent)
|
||||
self.setWordWrap(wrap)
|
||||
self.setColorState(True)
|
||||
|
||||
return
|
||||
|
||||
def setColorState(self, state: bool) -> None:
|
||||
"""Change the colour state."""
|
||||
if self._state is not state:
|
||||
self._state = state
|
||||
colour = self.palette()
|
||||
colour.setColor(QPalette.ColorRole.WindowText, self._color if state else self._faded)
|
||||
self.setPalette(colour)
|
||||
return
|
||||
|
||||
|
||||
class NWrappedWidgetBox(QHBoxLayout):
|
||||
"""Extension: A Text-Wrapped Widget Box
|
||||
|
||||
@@ -55,6 +55,7 @@ from novelwriter.common import minmax, transferCase
|
||||
from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
|
||||
from novelwriter.core.document import NWDocument
|
||||
from novelwriter.enum import nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
|
||||
from novelwriter.extensions.configlayout import NColourLabel
|
||||
from novelwriter.extensions.eventfilters import WheelEventFilter
|
||||
from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton
|
||||
from novelwriter.gui.dochighlight import BLOCK_META, BLOCK_TITLE
|
||||
@@ -210,6 +211,7 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
# Function Mapping
|
||||
self.closeSearch = self.docSearch.closeSearch
|
||||
self.searchVisible = self.docSearch.isVisible
|
||||
self.changeFocusState = self.docHeader.changeFocusState
|
||||
|
||||
# Finalise
|
||||
self.updateSyntaxColours()
|
||||
@@ -2785,8 +2787,7 @@ class GuiDocEditHeader(QWidget):
|
||||
self.setAutoFillBackground(True)
|
||||
|
||||
# Title Label
|
||||
self.itemTitle = QLabel("", self)
|
||||
self.itemTitle.setIndent(0)
|
||||
self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
|
||||
self.itemTitle.setMargin(0)
|
||||
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
||||
self.itemTitle.setAutoFillBackground(True)
|
||||
@@ -2924,6 +2925,11 @@ class GuiDocEditHeader(QWidget):
|
||||
|
||||
return
|
||||
|
||||
def changeFocusState(self, state: bool) -> None:
|
||||
"""Toggle focus state."""
|
||||
self.itemTitle.setColorState(state)
|
||||
return
|
||||
|
||||
def setHandle(self, tHandle: str) -> None:
|
||||
"""Set the document title from the handle, or alternatively, set
|
||||
the whole document path within the project.
|
||||
|
||||
@@ -33,7 +33,7 @@ from enum import Enum
|
||||
from PyQt5.QtCore import QPoint, Qt, QUrl, pyqtSignal, pyqtSlot
|
||||
from PyQt5.QtGui import QCursor, QMouseEvent, QPalette, QResizeEvent, QTextCursor
|
||||
from PyQt5.QtWidgets import (
|
||||
QAction, QApplication, QFrame, QHBoxLayout, QLabel, QMenu, QTextBrowser,
|
||||
QAction, QApplication, QFrame, QHBoxLayout, QMenu, QTextBrowser,
|
||||
QToolButton, QWidget
|
||||
)
|
||||
|
||||
@@ -42,6 +42,7 @@ from novelwriter.constants import nwHeaders, nwUnicode
|
||||
from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument
|
||||
from novelwriter.enum import nwDocAction, nwDocMode, nwItemType
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.extensions.configlayout import NColourLabel
|
||||
from novelwriter.extensions.eventfilters import WheelEventFilter
|
||||
from novelwriter.extensions.modified import NIconToolButton
|
||||
from novelwriter.gui.theme import STYLES_MIN_TOOLBUTTON
|
||||
@@ -92,6 +93,9 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
self.customContextMenuRequested.connect(self._openContextMenu)
|
||||
|
||||
# Function Mapping
|
||||
self.changeFocusState = self.docHeader.changeFocusState
|
||||
|
||||
self.initViewer()
|
||||
|
||||
logger.debug("Ready: GuiDocViewer")
|
||||
@@ -278,6 +282,10 @@ class GuiDocViewer(QTextBrowser):
|
||||
return False
|
||||
return True
|
||||
|
||||
def anyFocus(self) -> bool:
|
||||
"""Check if any widget or child widget has focus."""
|
||||
return self.hasFocus() or self.isAncestorOf(QApplication.focusWidget())
|
||||
|
||||
def clearNavHistory(self) -> None:
|
||||
"""Clear the navigation history."""
|
||||
self.docHistory.clear()
|
||||
@@ -597,9 +605,7 @@ class GuiDocViewHeader(QWidget):
|
||||
self.setAutoFillBackground(True)
|
||||
|
||||
# Title Label
|
||||
self.itemTitle = QLabel(self)
|
||||
self.itemTitle.setText("")
|
||||
self.itemTitle.setIndent(0)
|
||||
self.itemTitle = NColourLabel("", self, faded=SHARED.theme.fadedText)
|
||||
self.itemTitle.setMargin(0)
|
||||
self.itemTitle.setContentsMargins(0, 0, 0, 0)
|
||||
self.itemTitle.setAutoFillBackground(True)
|
||||
@@ -738,6 +744,11 @@ class GuiDocViewHeader(QWidget):
|
||||
self.itemTitle.setPalette(palette)
|
||||
return
|
||||
|
||||
def changeFocusState(self, state: bool) -> None:
|
||||
"""Toggle focus state."""
|
||||
self.itemTitle.setColorState(state)
|
||||
return
|
||||
|
||||
def setHandle(self, tHandle: str) -> None:
|
||||
"""Sets the document title from the handle, or alternatively,
|
||||
set the whole document path.
|
||||
|
||||
@@ -35,7 +35,7 @@ from PyQt5.QtWidgets import QAction, QMenuBar
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import openExternalPath
|
||||
from novelwriter.constants import nwConst, nwKeyWords, nwLabels, nwUnicode, trConst
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwView, nwWidget
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwFocus, nwView
|
||||
from novelwriter.extensions.eventfilters import StatusTipFilter
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -54,7 +54,7 @@ class GuiMainMenu(QMenuBar):
|
||||
requestDocInsert = pyqtSignal(nwDocInsert)
|
||||
requestDocInsertText = pyqtSignal(str)
|
||||
requestDocKeyWordInsert = pyqtSignal(str)
|
||||
requestFocusChange = pyqtSignal(nwWidget)
|
||||
requestFocusChange = pyqtSignal(nwFocus)
|
||||
requestViewChange = pyqtSignal(nwView)
|
||||
|
||||
def __init__(self, mainGui: GuiMain) -> None:
|
||||
@@ -303,24 +303,24 @@ class GuiMainMenu(QMenuBar):
|
||||
self.viewMenu = self.addMenu(self.tr("&View"))
|
||||
|
||||
# View > TreeView
|
||||
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Project Tree"))
|
||||
self.aFocusTree = self.viewMenu.addAction(self.tr("Go to Tree View"))
|
||||
self.aFocusTree.setShortcut("Ctrl+T")
|
||||
self.aFocusTree.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.TREE)
|
||||
lambda: self.requestFocusChange.emit(nwFocus.TREE)
|
||||
)
|
||||
|
||||
# View > Document Editor
|
||||
self.aFocusEditor = self.viewMenu.addAction(self.tr("Go to Document Editor"))
|
||||
self.aFocusEditor.setShortcut("Ctrl+E")
|
||||
self.aFocusEditor.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.EDITOR)
|
||||
self.aFocusDocument = self.viewMenu.addAction(self.tr("Go to Document"))
|
||||
self.aFocusDocument.setShortcut("Ctrl+E")
|
||||
self.aFocusDocument.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwFocus.DOCUMENT)
|
||||
)
|
||||
|
||||
# View > Outline
|
||||
self.aFocusOutline = self.viewMenu.addAction(self.tr("Go to Outline"))
|
||||
self.aFocusOutline.setShortcut("Ctrl+Shift+T")
|
||||
self.aFocusOutline.triggered.connect(
|
||||
lambda: self.requestFocusChange.emit(nwWidget.OUTLINE)
|
||||
lambda: self.requestFocusChange.emit(nwFocus.OUTLINE)
|
||||
)
|
||||
|
||||
# View > Separator
|
||||
|
||||
@@ -215,7 +215,7 @@ class GuiOutlineToolBar(QToolBar):
|
||||
|
||||
# Novel Selector
|
||||
self.novelLabel = NColourLabel(
|
||||
self.tr("Outline of"), parent=self, scale=NColourLabel.HEADER_SCALE, bold=True
|
||||
self.tr("Outline of"), self, scale=NColourLabel.HEADER_SCALE, bold=True
|
||||
)
|
||||
self.novelLabel.setContentsMargins(0, 0, CONFIG.pxInt(12), 0)
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class GuiTheme:
|
||||
self.statUnsaved = QColor(0, 0, 0)
|
||||
self.statSaved = QColor(0, 0, 0)
|
||||
self.helpText = QColor(0, 0, 0)
|
||||
self.fadedText = QColor(0, 0, 0)
|
||||
self.errorText = QColor(255, 0, 0)
|
||||
|
||||
# Loaded Syntax Settings
|
||||
@@ -263,6 +264,7 @@ class GuiTheme:
|
||||
sec = "GUI"
|
||||
if parser.has_section(sec):
|
||||
self.helpText = self._parseColour(parser, sec, "helptext")
|
||||
self.fadedText = self._parseColour(parser, sec, "fadedtext")
|
||||
self.errorText = self._parseColour(parser, sec, "errortext")
|
||||
self.statNone = self._parseColour(parser, sec, "statusnone")
|
||||
self.statUnsaved = self._parseColour(parser, sec, "statusunsaved")
|
||||
@@ -405,6 +407,7 @@ class GuiTheme:
|
||||
self.statUnsaved = QColor(200, 15, 39)
|
||||
self.statSaved = QColor(2, 133, 37)
|
||||
self.helpText = QColor(0, 0, 0)
|
||||
self.fadedText = QColor(128, 128, 128)
|
||||
self.errorText = QColor(255, 0, 0)
|
||||
return
|
||||
|
||||
|
||||
+49
-26
@@ -44,7 +44,7 @@ from novelwriter.dialogs.about import GuiAbout
|
||||
from novelwriter.dialogs.preferences import GuiPreferences
|
||||
from novelwriter.dialogs.projectsettings import GuiProjectSettings
|
||||
from novelwriter.dialogs.wordlist import GuiWordList
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwView, nwWidget
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwFocus, nwItemType, nwView
|
||||
from novelwriter.gui.doceditor import GuiDocEditor
|
||||
from novelwriter.gui.docviewer import GuiDocViewer
|
||||
from novelwriter.gui.docviewerpanel import GuiDocViewerPanel
|
||||
@@ -227,7 +227,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.mainMenu.requestFocusChange.connect(self._switchFocus)
|
||||
self.mainMenu.requestViewChange.connect(self._changeView)
|
||||
|
||||
self.sideBar.requestViewChange.connect(self._changeView)
|
||||
@@ -326,6 +326,11 @@ class GuiMain(QMainWindow):
|
||||
|
||||
def postLaunchTasks(self, cmdOpen: str | None) -> None:
|
||||
"""Process tasks after the main window has been created."""
|
||||
QApplication.processEvents()
|
||||
app = QApplication.instance()
|
||||
if isinstance(app, QApplication):
|
||||
app.focusChanged.connect(self._appFocusChanged)
|
||||
|
||||
# Check that config loaded fine
|
||||
if CONFIG.hasError:
|
||||
SHARED.error(CONFIG.errorText())
|
||||
@@ -944,14 +949,37 @@ class GuiMain(QMainWindow):
|
||||
SHARED.setFocusMode(not SHARED.focusMode)
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot("QWidget*", "QWidget*")
|
||||
def _appFocusChanged(self, old: QWidget, new: QWidget) -> None:
|
||||
"""Alert main widgets that they have received or lost focus."""
|
||||
if isinstance(new, QWidget):
|
||||
docEditor = False
|
||||
docViewer = False
|
||||
if self.docEditor.isAncestorOf(new):
|
||||
docEditor = True
|
||||
elif self.docViewer.isAncestorOf(new):
|
||||
docViewer = True
|
||||
|
||||
self.docEditor.changeFocusState(docEditor)
|
||||
self.docViewer.changeFocusState(docViewer)
|
||||
|
||||
logger.debug("Main focus switched to: %s", type(new).__name__)
|
||||
|
||||
return
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _focusModeChanged(self, focusMode: bool) -> None:
|
||||
"""Handle change of focus mode. The Main GUI Focus Mode hides tree,
|
||||
view, statusbar and menu.
|
||||
"""Handle change of focus mode. The Main GUI Focus Mode hides
|
||||
tree, view, statusbar and menu.
|
||||
"""
|
||||
if focusMode:
|
||||
logger.debug("Activating Focus Mode")
|
||||
self.switchFocus(nwWidget.EDITOR)
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docEditor.setFocus()
|
||||
else:
|
||||
logger.debug("Deactivating Focus Mode")
|
||||
|
||||
@@ -974,10 +1002,10 @@ class GuiMain(QMainWindow):
|
||||
self.docEditor.ensureCursorVisibleNoCentre()
|
||||
return
|
||||
|
||||
@pyqtSlot(nwWidget)
|
||||
def switchFocus(self, paneNo: nwWidget) -> None:
|
||||
@pyqtSlot(nwFocus)
|
||||
def _switchFocus(self, paneNo: nwFocus) -> None:
|
||||
"""Switch focus between main GUI views."""
|
||||
if paneNo == nwWidget.TREE:
|
||||
if paneNo == nwFocus.TREE:
|
||||
if self.projStack.currentWidget() is self.projView:
|
||||
if self.projView.treeHasFocus():
|
||||
self._changeView(nwView.NOVEL)
|
||||
@@ -993,21 +1021,19 @@ class GuiMain(QMainWindow):
|
||||
else:
|
||||
self._changeView(nwView.PROJECT)
|
||||
self.projView.setTreeFocus()
|
||||
elif paneNo == nwWidget.EDITOR:
|
||||
elif paneNo == nwFocus.DOCUMENT:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docEditor.setFocus()
|
||||
elif paneNo == nwWidget.VIEWER:
|
||||
self._changeView(nwView.EDITOR)
|
||||
self.docViewer.setFocus()
|
||||
elif paneNo == nwWidget.OUTLINE:
|
||||
if self.docEditor.anyFocus():
|
||||
self.docViewer.setFocus()
|
||||
elif self.docViewer.anyFocus():
|
||||
self.docEditor.setFocus()
|
||||
else:
|
||||
self.docEditor.setFocus()
|
||||
elif paneNo == nwFocus.OUTLINE:
|
||||
self._changeView(nwView.OUTLINE)
|
||||
self.outlineView.setTreeFocus()
|
||||
return
|
||||
|
||||
##
|
||||
# Private Slots
|
||||
##
|
||||
|
||||
@pyqtSlot(bool, bool, bool, bool)
|
||||
def _processConfigChanges(self, restart: bool, tree: bool, theme: bool, syntax: bool) -> None:
|
||||
"""Refresh GUI based on flags from the Preferences dialog."""
|
||||
@@ -1146,16 +1172,13 @@ class GuiMain(QMainWindow):
|
||||
|
||||
@pyqtSlot(nwDocAction)
|
||||
def _passDocumentAction(self, action: nwDocAction) -> None:
|
||||
"""Pass on a document action to the document viewer if it has
|
||||
focus, or pass it to the document editor if it or any of its
|
||||
child widgets have focus. If neither has focus, ignore it.
|
||||
"""Pass on a document action to the editor or viewer based on
|
||||
which one has focus, or if neither has focus, ignore it.
|
||||
"""
|
||||
if self.docViewer.hasFocus():
|
||||
self.docViewer.docAction(action)
|
||||
elif self.docEditor.hasFocus():
|
||||
if self.docEditor.hasFocus():
|
||||
self.docEditor.docAction(action)
|
||||
else:
|
||||
logger.debug("Action cancelled as neither editor nor viewer has focus")
|
||||
elif self.docViewer.hasFocus():
|
||||
self.docViewer.docAction(action)
|
||||
return
|
||||
|
||||
@pyqtSlot(str)
|
||||
|
||||
@@ -98,8 +98,8 @@ class GuiBuildSettings(NToolDialog):
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Manuscript Build Settings"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
self.tr("Manuscript Build Settings"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
|
||||
# Settings Name
|
||||
|
||||
@@ -68,8 +68,8 @@ class GuiNovelDetails(NNonBlockingDialog):
|
||||
|
||||
# Title
|
||||
self.titleLabel = NColourLabel(
|
||||
self.tr("Novel Details"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
self.tr("Novel Details"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE, indent=CONFIG.pxInt(4)
|
||||
)
|
||||
|
||||
# Novel Selector
|
||||
@@ -199,8 +199,8 @@ class _OverviewPage(NScrollablePage):
|
||||
|
||||
# Project Info
|
||||
self.projLabel = NColourLabel(
|
||||
self.tr("Project"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
self.tr("Project"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
self.projName = QLabel("", self)
|
||||
@@ -223,8 +223,8 @@ class _OverviewPage(NScrollablePage):
|
||||
|
||||
# Novel Info
|
||||
self.novelLabel = NColourLabel(
|
||||
self.tr("Selected Novel"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
self.tr("Selected Novel"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
self.novelName = QLabel("", self)
|
||||
@@ -315,8 +315,8 @@ class _ContentsPage(NFixedPage):
|
||||
|
||||
# Title
|
||||
self.contentLabel = NColourLabel(
|
||||
self.tr("Table of Contents"), SHARED.theme.helpText,
|
||||
parent=self, scale=NColourLabel.HEADER_SCALE
|
||||
self.tr("Table of Contents"), self, color=SHARED.theme.helpText,
|
||||
scale=NColourLabel.HEADER_SCALE
|
||||
)
|
||||
|
||||
# Contents Tree
|
||||
|
||||
@@ -62,15 +62,17 @@ def testBaseInit_Launch(caplog, monkeypatch, fncPath):
|
||||
CONFIG.osWindows = osWindows
|
||||
|
||||
# Normal Launch
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *a: None)
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setApplicationName", lambda *a: None)
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setApplicationVersion", lambda *a: None)
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setWindowIcon", lambda *a: None)
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.setOrganizationDomain", lambda *a: None)
|
||||
monkeypatch.setattr("PyQt5.QtWidgets.QApplication.exec", lambda *a: 0)
|
||||
with pytest.raises(SystemExit) as ex:
|
||||
main([f"--config={fncPath}", f"--data={fncPath}"])
|
||||
assert ex.value.code == 0
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.__init__", lambda *a: None)
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.setApplicationName", lambda *a: None)
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.setApplicationVersion", lambda *a: None)
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.setWindowIcon", lambda *a: None)
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.setOrganizationDomain", lambda *a: None)
|
||||
mp.setattr("PyQt5.QtWidgets.QApplication.exec", lambda *a: 0)
|
||||
# mp.setattr("PyQt5.QtWidgets.QApplication.focusChange.connect", lambda *a: None)
|
||||
with pytest.raises(SystemExit) as ex:
|
||||
main([f"--config={fncPath}", f"--data={fncPath}"])
|
||||
assert ex.value.code == 0
|
||||
|
||||
|
||||
@pytest.mark.base
|
||||
|
||||
@@ -29,9 +29,7 @@ from PyQt5.QtWidgets import QAction, QApplication, QMenu
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.constants import nwKeyWords, nwUnicode
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.enum import (
|
||||
nwDocAction, nwDocInsert, nwItemClass, nwItemLayout, nwTrinary, nwWidget
|
||||
)
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout, nwTrinary
|
||||
from novelwriter.gui.doceditor import GuiDocEditor
|
||||
from novelwriter.text.counting import standardCounter
|
||||
from novelwriter.types import (
|
||||
@@ -1678,7 +1676,7 @@ def testGuiEditor_Completer(qtbot, nwGUI, projPath, mockRnd):
|
||||
completer = docEditor._completer
|
||||
|
||||
# Create Scene
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
nwGUI.docEditor.setFocus()
|
||||
for c in "### Scene One":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
@@ -32,7 +32,7 @@ from PyQt5.QtWidgets import QInputDialog, QMenu
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.enum import nwItemType, nwView, nwWidget
|
||||
from novelwriter.enum import nwDocAction, nwFocus, nwItemType, nwView
|
||||
from novelwriter.gui.doceditor import GuiDocEditor
|
||||
from novelwriter.gui.noveltree import GuiNovelView
|
||||
from novelwriter.gui.outline import GuiOutlineView
|
||||
@@ -64,7 +64,21 @@ def testGuiMain_Launch(qtbot, monkeypatch, nwGUI, projPath):
|
||||
|
||||
# Open Lipsum project
|
||||
nwGUI.postLaunchTasks(projPath)
|
||||
assert SHARED.hasProject is True
|
||||
nwGUI.closeProject()
|
||||
assert SHARED.hasProject is False
|
||||
|
||||
# Open as if called from Welcome
|
||||
nwGUI._openProjectFromWelcome(projPath)
|
||||
assert SHARED.hasProject is True
|
||||
nwGUI.closeProject()
|
||||
assert SHARED.hasProject is False
|
||||
|
||||
# Open as if called from Welcome, invalid path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI, "showWelcomeDialog", lambda *a: None)
|
||||
nwGUI._openProjectFromWelcome(None)
|
||||
assert SHARED.hasProject is False
|
||||
|
||||
# Project open fails
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -113,7 +127,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
|
||||
# Project Tree has focus
|
||||
nwGUI._changeView(nwView.PROJECT)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projStack.setCurrentIndex(0)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiProjectTree, "hasFocus", lambda *a: True)
|
||||
@@ -137,7 +151,7 @@ def testGuiMain_ProjectTreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
|
||||
# Project Outline has focus
|
||||
nwGUI._changeView(nwView.OUTLINE)
|
||||
nwGUI.switchFocus(nwWidget.OUTLINE)
|
||||
nwGUI._switchFocus(nwFocus.OUTLINE)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiOutlineView, "treeHasFocus", lambda *a: True)
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
@@ -230,7 +244,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
CONFIG.autoScroll = True
|
||||
|
||||
# Add a Character File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
@@ -250,7 +264,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
docEditor._qDocument.syntaxHighlighter.initHighlighter()
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
nwGUI.docEditor.setFocus()
|
||||
qtbot.keyClick(docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||
for c in "# Jane Doe":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
@@ -265,14 +279,14 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
qtbot.keyClick(docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
# Add a Plot File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(C.hPlotRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
nwGUI.docEditor.setFocus()
|
||||
qtbot.keyClick(docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||
for c in "# Main Plot":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
@@ -287,7 +301,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
qtbot.keyClick(docEditor, Qt.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
# Add a World File
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(C.hWorldRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE, None, isNote=True)
|
||||
@@ -299,7 +313,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
docEditor.replaceText("")
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
nwGUI.docEditor.setFocus()
|
||||
qtbot.keyClick(docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||
for c in "# Main Location":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
@@ -318,7 +332,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
nwGUI._autoSaveProject()
|
||||
|
||||
# Select the 'New Scene' file
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(C.hNovelRoot).setExpanded(True)
|
||||
nwGUI.projView.projTree._getTreeItem(C.hChapterDir).setExpanded(True)
|
||||
@@ -326,7 +340,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
nwGUI.docEditor.setFocus()
|
||||
qtbot.keyClick(docEditor, "a", modifier=Qt.ControlModifier, delay=KEY_DELAY)
|
||||
for c in "# Novel":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
@@ -535,7 +549,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
nwGUI.rebuildIndex()
|
||||
|
||||
# Open and view the edited document
|
||||
nwGUI.switchFocus(nwWidget.VIEWER)
|
||||
nwGUI.docViewer.setFocus()
|
||||
assert nwGUI.openDocument(C.hSceneDoc)
|
||||
assert nwGUI.viewDocument(C.hSceneDoc)
|
||||
assert nwGUI.saveProject()
|
||||
@@ -689,3 +703,118 @@ def testGuiMain_Features(qtbot, nwGUI, projPath, mockRnd):
|
||||
nwGUI.sideBar.mSettings.hide()
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiMain_FocusView(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test switching focus and view of the main window."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
|
||||
nwGUI.openDocument(C.hSceneDoc)
|
||||
nwGUI.viewDocument(C.hSceneDoc)
|
||||
|
||||
# Toggle Focus
|
||||
# ============
|
||||
nwGUI.docEditor.setFocus()
|
||||
assert nwGUI.docEditor.anyFocus()
|
||||
|
||||
# Simulate focus change to viewer
|
||||
nwGUI._appFocusChanged(None, nwGUI.docViewer)
|
||||
assert nwGUI.docEditor.docHeader.itemTitle._state is False
|
||||
assert nwGUI.docViewer.docHeader.itemTitle._state is True
|
||||
|
||||
# Simulate focus change to editor
|
||||
nwGUI._appFocusChanged(None, nwGUI.docEditor)
|
||||
assert nwGUI.docEditor.docHeader.itemTitle._state is True
|
||||
assert nwGUI.docViewer.docHeader.itemTitle._state is False
|
||||
|
||||
# Focus Tree
|
||||
# ==========
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.projView
|
||||
|
||||
# Switch from editor to project tree
|
||||
nwGUI.docEditor.setFocus()
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.projView
|
||||
|
||||
# Triggering again should switch to novel view
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.novelView
|
||||
|
||||
# Switch from editor to novel view
|
||||
nwGUI.docEditor.setFocus()
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.novelView
|
||||
|
||||
# Triggering again should switch back to project tree
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.projView
|
||||
|
||||
# If in search mode, should default to project tree
|
||||
nwGUI._changeView(nwView.SEARCH)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
assert nwGUI.projStack.currentWidget() == nwGUI.projView
|
||||
|
||||
# Focus Document
|
||||
# ==============
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
|
||||
def mockEmitEditorFocus(*a):
|
||||
nwGUI._appFocusChanged(None, nwGUI.docEditor)
|
||||
|
||||
def mockEmitViewerFocus(*a):
|
||||
nwGUI._appFocusChanged(None, nwGUI.docViewer)
|
||||
|
||||
# Switch to viewer
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: True)
|
||||
mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False)
|
||||
mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus)
|
||||
mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus)
|
||||
nwGUI._switchFocus(nwFocus.DOCUMENT)
|
||||
assert nwGUI.docEditor.docHeader.itemTitle._state is False
|
||||
assert nwGUI.docViewer.docHeader.itemTitle._state is True
|
||||
|
||||
# Call again to switch to editor
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False)
|
||||
mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: True)
|
||||
mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus)
|
||||
mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus)
|
||||
nwGUI._switchFocus(nwFocus.DOCUMENT)
|
||||
assert nwGUI.docEditor.docHeader.itemTitle._state is True
|
||||
assert nwGUI.docViewer.docHeader.itemTitle._state is False
|
||||
|
||||
# Default to editor
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False)
|
||||
mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False)
|
||||
mp.setattr(nwGUI.docEditor, "setFocus", mockEmitEditorFocus)
|
||||
mp.setattr(nwGUI.docViewer, "setFocus", mockEmitViewerFocus)
|
||||
nwGUI._switchFocus(nwFocus.DOCUMENT)
|
||||
assert nwGUI.docEditor.docHeader.itemTitle._state is True
|
||||
assert nwGUI.docViewer.docHeader.itemTitle._state is False
|
||||
|
||||
# Focus Outline
|
||||
# =============
|
||||
nwGUI._switchFocus(nwFocus.OUTLINE)
|
||||
assert nwGUI.mainStack.currentWidget() == nwGUI.outlineView
|
||||
|
||||
# Pass Actions
|
||||
# ============
|
||||
|
||||
# Pass to editor
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: True)
|
||||
mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: False)
|
||||
nwGUI._passDocumentAction(nwDocAction.SEL_ALL)
|
||||
assert nwGUI.docEditor.textCursor().hasSelection() is True
|
||||
|
||||
# Pass to viewer
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.docEditor, "hasFocus", lambda *a: False)
|
||||
mp.setattr(nwGUI.docViewer, "hasFocus", lambda *a: True)
|
||||
nwGUI._passDocumentAction(nwDocAction.SEL_ALL)
|
||||
assert nwGUI.docViewer.textCursor().hasSelection() is True
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
@@ -64,6 +64,8 @@ def testGuiI18n_Localisation(qtbot, monkeypatch, language, nwGUI, projPath):
|
||||
qtbot.waitUntil(lambda: SHARED.findTopLevelWidget(dType) is not None, timeout=1000)
|
||||
dialog = SHARED.findTopLevelWidget(dType)
|
||||
assert isinstance(dialog, dType)
|
||||
assert dialog is not None
|
||||
dialog.deleteLater()
|
||||
|
||||
showDialog(nwGUI.showWelcomeDialog, GuiWelcome)
|
||||
showDialog(nwGUI.showPreferencesDialog, GuiPreferences)
|
||||
|
||||
@@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QInputDialog, QToolTip
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.enum import nwItemType, nwWidget
|
||||
from novelwriter.enum import nwFocus, nwItemType
|
||||
from novelwriter.gui.noveltree import GuiNovelTree, NovelTreeColumn
|
||||
from novelwriter.types import QtMouseLeft
|
||||
|
||||
@@ -44,7 +44,7 @@ def testGuiNovelTree_TreeItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
|
||||
buildTestProject(nwGUI, projPath)
|
||||
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.projView.projTree.clearSelection()
|
||||
nwGUI.projView.projTree._getTreeItem(C.hCharRoot).setSelected(True)
|
||||
nwGUI.projView.projTree.newTreeItem(nwItemType.FILE)
|
||||
|
||||
@@ -34,7 +34,7 @@ from novelwriter.core.project import NWProject
|
||||
from novelwriter.dialogs.docmerge import GuiDocMerge
|
||||
from novelwriter.dialogs.docsplit import GuiDocSplit
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.enum import nwItemClass, nwItemLayout, nwItemType, nwWidget
|
||||
from novelwriter.enum import nwFocus, nwItemClass, nwItemLayout, nwItemType
|
||||
from novelwriter.gui.projtree import GuiProjectTree, GuiProjectView, _TreeContextMenu
|
||||
from novelwriter.guimain import GuiMain
|
||||
from novelwriter.types import QtAccepted, QtModNone, QtMouseLeft, QtMouseMiddle, QtRejected
|
||||
@@ -1111,7 +1111,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# Create a project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
nwGUI.openProject(projPath)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
|
||||
# Handles for new objects
|
||||
hCharNote = "0000000000011"
|
||||
@@ -1408,7 +1408,7 @@ def testGuiProjTree_Templates(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# Create a project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
nwGUI.openProject(projPath)
|
||||
nwGUI.switchFocus(nwWidget.TREE)
|
||||
nwGUI._switchFocus(nwFocus.TREE)
|
||||
nwGUI.show()
|
||||
|
||||
project = SHARED.project
|
||||
|
||||
Reference in New Issue
Block a user