Merge branch 'main' into release_2.5rc1

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