Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some minor issues #1632

Merged
merged 4 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions novelwriter/gui/doceditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2012,20 +2012,25 @@ def _autoSelect(self) -> QTextCursor:
cPos = cursor.position()
bPos = cursor.block().position()
bLen = cursor.block().length()
apos = nwUnicode.U_APOS + nwUnicode.U_RSQUO

# Scan backwards
# Scan backward
sPos = cPos
for i in range(cPos - bPos):
sPos = cPos - i - 1
if not self._qDocument.characterAt(sPos).isalnum():
cOne = self._qDocument.characterAt(sPos)
cTwo = self._qDocument.characterAt(sPos - 1)
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
sPos += 1
break

# Scan forwards
# Scan forward
ePos = cPos
for i in range(bPos + bLen - cPos):
ePos = cPos + i
if not self._qDocument.characterAt(ePos).isalnum():
cOne = self._qDocument.characterAt(ePos)
cTwo = self._qDocument.characterAt(ePos + 1)
if not (cOne.isalnum() or cOne in apos and cTwo.isalnum()):
break

if ePos - sPos <= 0:
Expand Down
94 changes: 42 additions & 52 deletions novelwriter/gui/noveltree.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,18 @@ def __init__(self, novelView: GuiNovelView) -> None:

# Novel Selector
selFont = self.font()
selFont.setWeight(QFont.Bold)
selFont.setWeight(QFont.Weight.Bold)
self.novelPrefix = self.tr("Outline of {0}")
self.novelValue = NovelSelector(self)
self.novelValue.setFont(selFont)
self.novelValue.setMinimumWidth(CONFIG.pxInt(150))
self.novelValue.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.novelValue.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self.novelValue.novelSelectionChanged.connect(self.setCurrentRoot)

self.tbNovel = QToolButton(self)
self.tbNovel.setToolTip(self.tr("Novel Root"))
self.tbNovel.setIconSize(QSize(iPx, iPx))
self.tbNovel.clicked.connect(self._openNovelSelector)
self.tbNovel.clicked.connect(self.novelValue.showPopup)

# Refresh Button
self.tbRefresh = QToolButton(self)
Expand All @@ -244,7 +244,7 @@ def __init__(self, novelView: GuiNovelView) -> None:
self.tbMore.setToolTip(self.tr("More Options"))
self.tbMore.setIconSize(QSize(iPx, iPx))
self.tbMore.setMenu(self.mMore)
self.tbMore.setPopupMode(QToolButton.InstantPopup)
self.tbMore.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)

# Assemble
self.outerBox = QHBoxLayout()
Expand Down Expand Up @@ -275,7 +275,7 @@ def updateTheme(self) -> None:
self.tbMore.setIcon(SHARED.theme.getIcon("menu"))

qPalette = self.palette()
qPalette.setBrush(QPalette.Window, qPalette.base())
qPalette.setBrush(QPalette.ColorRole.Window, qPalette.base())
self.setPalette(qPalette)

# StyleSheets
Expand Down Expand Up @@ -327,12 +327,6 @@ def setLastColType(self, colType: NovelTreeColumn, doRefresh: bool = True) -> No
# Private Slots
##

@pyqtSlot()
def _openNovelSelector(self) -> None:
"""Trigger the dropdown list of the novel selector."""
self.novelValue.showPopup()
return

@pyqtSlot()
def _refreshNovelTree(self) -> None:
"""Rebuild the current tree."""
Expand Down Expand Up @@ -408,25 +402,25 @@ def __init__(self, novelView: GuiNovelView) -> None:
cMg = CONFIG.pxInt(6)

self.setIconSize(QSize(iPx, iPx))
self.setFrameStyle(QFrame.NoFrame)
self.setFrameStyle(QFrame.Shape.NoFrame)
self.setUniformRowHeights(True)
self.setAllColumnsShowFocus(True)
self.setHeaderHidden(True)
self.setIndentation(0)
self.setColumnCount(4)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.setExpandsOnDoubleClick(False)
self.setDragEnabled(False)

# Lock the column sizes
treeHeader = self.header()
treeHeader.setStretchLastSection(False)
treeHeader.setMinimumSectionSize(iPx + cMg)
treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.Stretch)
treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_TITLE, QHeaderView.ResizeMode.Stretch)
treeHeader.setSectionResizeMode(self.C_WORDS, QHeaderView.ResizeMode.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_EXTRA, QHeaderView.ResizeMode.ResizeToContents)
treeHeader.setSectionResizeMode(self.C_MORE, QHeaderView.ResizeMode.ResizeToContents)

# Pre-Generate Tree Formatting
fH1 = self.font()
Expand Down Expand Up @@ -455,14 +449,14 @@ def initSettings(self) -> None:
"""Set or update tree widget settings."""
# Scroll bars
if CONFIG.hideVScroll:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else:
self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

if CONFIG.hideHScroll:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
else:
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

return

Expand Down Expand Up @@ -522,21 +516,16 @@ def refreshTree(self, rootHandle: str | None = None, overRide: bool = False) ->

def refreshHandle(self, tHandle: str) -> None:
"""Refresh the data for a given handle."""
idxData = SHARED.project.index.getItemData(tHandle)
if idxData is None:
return

logger.debug("Refreshing meta data for item '%s'", tHandle)
for sTitle, tHeading in idxData.items():
sKey = f"{tHandle}:{sTitle}"
trItem = self._treeMap.get(sKey, None)
if trItem is None:
logger.debug("Heading '%s' not in novel tree", sKey)
self.refreshTree()
return

self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)

if idxData := SHARED.project.index.getItemData(tHandle):
logger.debug("Refreshing meta data for item '%s'", tHandle)
for sTitle, tHeading in idxData.items():
sKey = f"{tHandle}:{sTitle}"
if trItem := self._treeMap.get(sKey, None):
self._updateTreeItemValues(trItem, tHeading, tHandle, sTitle)
else:
logger.debug("Heading '%s' not in novel tree", sKey)
self.refreshTree()
return
return

def getSelectedHandle(self) -> tuple[str | None, str | None]:
Expand Down Expand Up @@ -567,27 +556,25 @@ def setLastColSize(self, colSize: int) -> None:
self._lastColSize = minmax(colSize, 15, 75)/100.0
return

def setActiveHandle(self, tHandle: str | None) -> None:
def setActiveHandle(self, tHandle: str | None, doScroll: bool = False) -> None:
"""Highlight the rows associated with a given handle."""
tStart = time()

didScroll = False
self._actHandle = tHandle
for i in range(self.topLevelItemCount()):
tItem = self.topLevelItem(i)
if tItem is not None:
if tItem := self.topLevelItem(i):
if tItem.data(self.C_DATA, self.D_HANDLE) == tHandle:
tItem.setBackground(self.C_TITLE, self.palette().alternateBase())
tItem.setBackground(self.C_WORDS, self.palette().alternateBase())
tItem.setBackground(self.C_EXTRA, self.palette().alternateBase())
tItem.setBackground(self.C_MORE, self.palette().alternateBase())
if doScroll and not didScroll:
self.scrollToItem(tItem, QAbstractItemView.ScrollHint.PositionAtCenter)
didScroll = True
else:
tItem.setBackground(self.C_TITLE, self.palette().base())
tItem.setBackground(self.C_WORDS, self.palette().base())
tItem.setBackground(self.C_EXTRA, self.palette().base())
tItem.setBackground(self.C_MORE, self.palette().base())

logger.debug("Highlighted Novel Tree in %.3f ms", (time() - tStart)*1000)

return

##
Expand All @@ -601,12 +588,12 @@ def mousePressEvent(self, event: QMouseEvent) -> None:
"""
super().mousePressEvent(event)

if event.button() == Qt.LeftButton:
if event.button() == Qt.MouseButton.LeftButton:
selItem = self.indexAt(event.pos())
if not selItem.isValid():
self.clearSelection()

elif event.button() == Qt.MiddleButton:
elif event.button() == Qt.MouseButton.MiddleButton:
selItem = self.itemAt(event.pos())
if not isinstance(selItem, QTreeWidgetItem):
return
Expand Down Expand Up @@ -637,7 +624,10 @@ def resizeEvent(self, event: QResizeEvent) -> None:
trItem = self.topLevelItem(i)
if isinstance(trItem, QTreeWidgetItem):
lastText = trItem.data(self.C_DATA, self.D_EXTRA)
trItem.setText(self.C_EXTRA, fMetric.elidedText(lastText, Qt.ElideRight, eliW))
trItem.setText(
self.C_EXTRA,
fMetric.elidedText(lastText, Qt.TextElideMode.ElideRight, eliW)
)
return

##
Expand Down Expand Up @@ -693,7 +683,7 @@ def _populateTree(self, rootHandle: str | None) -> None:
newItem.setData(self.C_DATA, self.D_HANDLE, tHandle)
newItem.setData(self.C_DATA, self.D_TITLE, sTitle)
newItem.setData(self.C_DATA, self.D_KEY, tKey)
newItem.setTextAlignment(self.C_WORDS, Qt.AlignRight)
newItem.setTextAlignment(self.C_WORDS, Qt.AlignmentFlag.AlignRight)

self._updateTreeItemValues(newItem, novIdx, tHandle, sTitle)
self._treeMap[tKey] = newItem
Expand All @@ -712,16 +702,16 @@ def _updateTreeItemValues(self, trItem: QTreeWidgetItem, idxItem: IndexHeading,
iLevel = nwHeaders.H_LEVEL.get(idxItem.level, 0)
hDec = SHARED.theme.getHeaderDecoration(iLevel)

trItem.setData(self.C_TITLE, Qt.DecorationRole, hDec)
trItem.setData(self.C_TITLE, Qt.ItemDataRole.DecorationRole, hDec)
trItem.setText(self.C_TITLE, idxItem.title)
trItem.setFont(self.C_TITLE, self._hFonts[iLevel])
trItem.setText(self.C_WORDS, f"{idxItem.wordCount:n}")
trItem.setData(self.C_MORE, Qt.DecorationRole, self._pMore)
trItem.setData(self.C_MORE, Qt.ItemDataRole.DecorationRole, self._pMore)

# Custom column
mW = int(self._lastColSize * self.viewport().width())
lastText, toolTip = self._getLastColumnText(tHandle, sTitle)
elideText = self.fontMetrics().elidedText(lastText, Qt.ElideRight, mW)
elideText = self.fontMetrics().elidedText(lastText, Qt.TextElideMode.ElideRight, mW)
trItem.setText(self.C_EXTRA, elideText)
trItem.setData(self.C_DATA, self.D_EXTRA, lastText)
trItem.setToolTip(self.C_EXTRA, toolTip)
Expand Down
2 changes: 1 addition & 1 deletion novelwriter/guimain.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ def openDocument(self, tHandle: str | None, tLine: int | None = None,
if self.docEditor.loadText(tHandle, tLine):
SHARED.project.data.setLastHandle(tHandle, "editor")
self.projView.setSelectedHandle(tHandle, doScroll=doScroll)
self.novelView.setActiveHandle(tHandle)
self.novelView.setActiveHandle(tHandle, doScroll=doScroll)
if changeFocus:
self.docEditor.setFocus()
else:
Expand Down
24 changes: 22 additions & 2 deletions tests/test_gui/test_gui_noveltree.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
from tools import C, buildTestProject

from PyQt5.QtGui import QFocusEvent
from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtCore import QPoint, Qt, QEvent
from PyQt5.QtWidgets import QInputDialog, QToolTip

from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwWidget, nwItemType
from novelwriter.gui.noveltree import NovelTreeColumn
from novelwriter.gui.noveltree import GuiNovelTree, NovelTreeColumn
from novelwriter.dialogs.editlabel import GuiEditLabel


Expand Down Expand Up @@ -192,12 +192,32 @@ def showText(pos, text):
mIndex = novelTree.model().index(2, novelTree.C_MORE)
with monkeypatch.context() as mp:
mp.setattr(QToolTip, "showText", showText)

ttText = ""
novelTree._treeItemClicked(mIndex)
assert ttText == (
"<p><b>Point of View</b>: Jane<br><b>Focus</b>: Jane</p>"
"<p><b>Synopsis</b>: This is a scene.</p>"
)

ttText = ""
novelTree._popMetaBox(QPoint(1, 1), C.hInvalid, "T0001")
assert ttText == ""

# Set Default Root
# ================
SHARED.project.data.setLastHandle(C.hInvalid, "novelTree")
novelView.openProjectTasks()
assert novelBar.novelValue.handle == C.hNovelRoot

# Tree Focus
# ==========
with monkeypatch.context() as mp:
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: False)
assert novelView.treeHasFocus() is False
mp.setattr(GuiNovelTree, "hasFocus", lambda *a: True)
assert novelView.treeHasFocus() is True

# Other Checks
# ============

Expand Down