Fix issue with "create note from tag" (#2215)

This commit is contained in:
Veronica Berglyd Olsen
2025-01-30 17:40:36 +01:00
committed by GitHub
7 changed files with 67 additions and 56 deletions
+4
View File
@@ -173,6 +173,10 @@ class nwKeyWords:
TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY,
]
CAN_CREATE = [
POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY,
OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY,
]
# Set of Valid Keys
VALID_KEYS = set(ALL_KEYS)
-7
View File
@@ -68,13 +68,6 @@ class nwComment(Enum):
STORY = 7
class nwTrinary(Enum):
NEGATIVE = -1
NEUTRAL = 0
POSITIVE = 1
class nwChange(Enum):
CREATE = 0
+5 -6
View File
@@ -29,7 +29,6 @@ from PyQt5.QtGui import QColor, QPainter, QPaintEvent
from PyQt5.QtWidgets import QAbstractButton, QWidget
from novelwriter import CONFIG
from novelwriter.enum import nwTrinary
from novelwriter.types import QtBlack, QtPaintAntiAlias
logger = logging.getLogger(__name__)
@@ -47,14 +46,14 @@ class StatusLED(QAbstractButton):
self._postitve = QtBlack
self._negative = QtBlack
self._color = QtBlack
self._state = nwTrinary.NEUTRAL
self._state = None
self._bPx = CONFIG.pxInt(1)
self.setFixedWidth(sW)
self.setFixedHeight(sH)
return
@property
def state(self) -> nwTrinary:
def state(self) -> bool | None:
"""The current state of the LED."""
return self._state
@@ -66,11 +65,11 @@ class StatusLED(QAbstractButton):
self.setState(self._state)
return
def setState(self, state: nwTrinary) -> None:
def setState(self, state: bool | None) -> None:
"""Set the colour state."""
if state == nwTrinary.POSITIVE:
if state is True:
self._color = self._postitve
elif state == nwTrinary.NEGATIVE:
elif state is False:
self._color = self._negative
else:
self._color = self._neutral
+30 -14
View File
@@ -34,7 +34,7 @@ from __future__ import annotations
import bisect
import logging
from enum import Enum
from enum import Enum, IntFlag
from time import time
from PyQt5.QtCore import (
@@ -57,7 +57,7 @@ from novelwriter.constants import nwConst, nwKeyWords, nwShortcode, nwUnicode
from novelwriter.core.document import NWDocument
from novelwriter.enum import (
nwChange, nwComment, nwDocAction, nwDocInsert, nwDocMode, nwItemClass,
nwItemType, nwTrinary
nwItemType
)
from novelwriter.extensions.configlayout import NColourLabel
from novelwriter.extensions.eventfilters import WheelEventFilter
@@ -84,6 +84,13 @@ class _SelectAction(Enum):
MOVE_AFTER = 3
class _TagAction(IntFlag):
NONE = 0b00
FOLLOW = 0b01
CREATE = 0b10
class GuiDocEditor(QPlainTextEdit):
"""Gui Widget: Main Document Editor"""
@@ -1158,11 +1165,11 @@ class GuiDocEditor(QPlainTextEdit):
# Follow
status = self._processTag(cursor=pCursor, follow=False)
if status == nwTrinary.POSITIVE:
if status & _TagAction.FOLLOW:
action = ctxMenu.addAction(self.tr("Follow Tag"))
action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, follow=True))
ctxMenu.addSeparator()
elif status == nwTrinary.NEGATIVE:
elif status & _TagAction.CREATE:
action = ctxMenu.addAction(self.tr("Create Note for Tag"))
action.triggered.connect(qtLambda(self._processTag, cursor=pCursor, create=True))
ctxMenu.addSeparator()
@@ -1925,8 +1932,9 @@ class GuiDocEditor(QPlainTextEdit):
self._qDocument.syntaxHighlighter.rehighlightBlock(block)
return
def _processTag(self, cursor: QTextCursor | None = None,
follow: bool = True, create: bool = False) -> nwTrinary:
def _processTag(
self, cursor: QTextCursor | None = None, follow: bool = True, create: bool = False
) -> _TagAction:
"""Activated by Ctrl+Enter. Checks that we're in a block
starting with '@'. We then find the tag under the cursor and
check that it is not the tag itself. If all this is fine, we
@@ -1936,19 +1944,22 @@ class GuiDocEditor(QPlainTextEdit):
if cursor is None:
cursor = self.textCursor()
status = _TagAction.NONE
block = cursor.block()
text = block.text()
if len(text) == 0:
return nwTrinary.NEUTRAL
return status
if text.startswith("@") and self._docHandle:
isGood, tBits, tPos = SHARED.project.index.scanThis(text)
if (
not isGood or not tBits or tBits[0] == nwKeyWords.TAG_KEY
or tBits[0] not in nwKeyWords.VALID_KEYS
not isGood
or not tBits
or (key := tBits[0]) == nwKeyWords.TAG_KEY
or key not in nwKeyWords.VALID_KEYS
):
return nwTrinary.NEUTRAL
return status
tag = ""
exist = False
@@ -1965,7 +1976,14 @@ class GuiDocEditor(QPlainTextEdit):
if not tag or tag.startswith("@"):
# The keyword cannot be looked up, so we ignore that
return nwTrinary.NEUTRAL
return status
if not exist and key in nwKeyWords.CAN_CREATE:
# Must only be set if we have a tag selected
status |= _TagAction.CREATE
if exist:
status |= _TagAction.FOLLOW
if follow and exist:
logger.debug("Attempting to follow tag '%s'", tag)
@@ -1977,9 +1995,7 @@ class GuiDocEditor(QPlainTextEdit):
itemClass = nwKeyWords.KEY_CLASS.get(tBits[0], nwItemClass.NO_CLASS)
self.requestNewNoteCreation.emit(tag, itemClass)
return nwTrinary.POSITIVE if exist else nwTrinary.NEGATIVE
return nwTrinary.NEUTRAL
return status
def _emitRenameItem(self, block: QTextBlock) -> None:
"""Emit a signal to request an item be renamed."""
+6 -7
View File
@@ -34,7 +34,6 @@ from PyQt5.QtWidgets import QApplication, QLabel, QStatusBar, QWidget
from novelwriter import CONFIG, SHARED
from novelwriter.common import formatTime
from novelwriter.constants import nwConst
from novelwriter.enum import nwTrinary
from novelwriter.extensions.modified import NClickableLabel
from novelwriter.extensions.statusled import StatusLED
@@ -121,8 +120,8 @@ class GuiMainStatus(QStatusBar):
self.setRefTime(-1.0)
self.setLanguage(*SHARED.spelling.describeDict())
self.setProjectStats(0, 0)
self.setProjectStatus(nwTrinary.NEUTRAL)
self.setDocumentStatus(nwTrinary.NEUTRAL)
self.setProjectStatus(None)
self.setDocumentStatus(None)
self.updateTime()
return
@@ -152,12 +151,12 @@ class GuiMainStatus(QStatusBar):
self._refTime = refTime
return
def setProjectStatus(self, state: nwTrinary) -> None:
def setProjectStatus(self, state: bool | None) -> None:
"""Set the project status colour icon."""
self.projIcon.setState(state)
return
def setDocumentStatus(self, state: nwTrinary) -> None:
def setDocumentStatus(self, state: bool | None) -> None:
"""Set the document status colour icon."""
self.docIcon.setState(state)
return
@@ -220,13 +219,13 @@ class GuiMainStatus(QStatusBar):
@pyqtSlot(bool)
def updateProjectStatus(self, status: bool) -> None:
"""Update the project status."""
self.setProjectStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
self.setProjectStatus(not status)
return
@pyqtSlot(bool)
def updateDocumentStatus(self, status: bool) -> None:
"""Update the document status."""
self.setDocumentStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
self.setDocumentStatus(not status)
return
##
+10 -9
View File
@@ -35,8 +35,8 @@ from novelwriter import CONFIG, SHARED
from novelwriter.common import decodeMimeHandles
from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout, nwTrinary
from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout
from novelwriter.gui.doceditor import GuiDocEditor, _TagAction
from novelwriter.text.counting import standardCounter
from novelwriter.types import (
QtAlignJustify, QtAlignLeft, QtKeepAnchor, QtModCtrl, QtModNone,
@@ -1693,21 +1693,22 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# Empty Block
docEditor.setCursorLine(2)
assert docEditor._processTag() is nwTrinary.NEUTRAL
assert docEditor._processTag() == _TagAction.NONE
# Not On Tag
docEditor.setCursorLine(1)
assert docEditor._processTag() is nwTrinary.NEUTRAL
assert docEditor._processTag() == _TagAction.NONE
# On Tag Keyword
docEditor.setCursorPosition(15)
assert docEditor._processTag() is nwTrinary.NEUTRAL
assert docEditor._processTag() == _TagAction.NONE
# On Known Tag, No Follow
docEditor.setCursorPosition(22)
assert docEditor._processTag(follow=False) is nwTrinary.POSITIVE
assert docEditor._processTag(follow=False) == _TagAction.FOLLOW
assert nwGUI.docViewer._docHandle is None
# qtbot.stop()
# On Known Tag, Follow
docEditor.setCursorPosition(22)
position = docEditor.cursorRect().center()
@@ -1723,13 +1724,13 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# On Unknown Tag, Create It
assert "0000000000011" not in SHARED.project.tree
docEditor.setCursorPosition(28)
assert docEditor._processTag(create=True) is nwTrinary.NEGATIVE
assert docEditor._processTag(create=True) == _TagAction.CREATE
assert "0000000000011" in SHARED.project.tree
# On Unknown Tag, Missing Root
assert "0000000000012" not in SHARED.project.tree
docEditor.setCursorPosition(42)
assert docEditor._processTag(create=True) is nwTrinary.NEGATIVE
assert docEditor._processTag(create=True) == _TagAction.CREATE
oHandle = SHARED.project.tree.findRoot(nwItemClass.OBJECT)
assert oHandle == "0000000000012"
@@ -1738,7 +1739,7 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
assert oItem.itemParent == "0000000000012"
docEditor.setCursorPosition(47)
assert docEditor._processTag() is nwTrinary.NEUTRAL
assert docEditor._processTag() == _TagAction.NONE
# qtbot.stop()
+12 -13
View File
@@ -25,7 +25,6 @@ import time
import pytest
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwTrinary
from tests.tools import C, buildTestProject
@@ -47,20 +46,20 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
assert status._refTime == refTime
# Project Status
status.setProjectStatus(nwTrinary.NEUTRAL)
assert status.projIcon.state == nwTrinary.NEUTRAL
status.setProjectStatus(nwTrinary.NEGATIVE)
assert status.projIcon.state == nwTrinary.NEGATIVE
status.setProjectStatus(nwTrinary.POSITIVE)
assert status.projIcon.state == nwTrinary.POSITIVE
status.setProjectStatus(None)
assert status.projIcon.state is None
status.setProjectStatus(False)
assert status.projIcon.state is False
status.setProjectStatus(True)
assert status.projIcon.state is True
# Document Status
status.setDocumentStatus(nwTrinary.NEUTRAL)
assert status.docIcon.state == nwTrinary.NEUTRAL
status.setDocumentStatus(nwTrinary.NEGATIVE)
assert status.docIcon.state == nwTrinary.NEGATIVE
status.setDocumentStatus(nwTrinary.POSITIVE)
assert status.docIcon.state == nwTrinary.POSITIVE
status.setDocumentStatus(None)
assert status.docIcon.state is None
status.setDocumentStatus(False)
assert status.docIcon.state is False
status.setDocumentStatus(True)
assert status.docIcon.state is True
# Idle Status
CONFIG.stopWhenIdle = False