Hide session timer (#2149)

This commit is contained in:
Veronica Berglyd Olsen
2024-12-29 18:02:17 +01:00
committed by GitHub
7 changed files with 118 additions and 42 deletions
+13
View File
@@ -388,3 +388,16 @@ application like for instance Libre Office Calc or Excel.
definition of idle here is that the novelWriter main window loses focus, or the user hasn't made
any changes to the currently open document in five minutes. The number of minutes can be altered
in **Preferences**.
Session Timer
-------------
A session timer is by default visible in the status bar. The icon will show you a clock icon when
you are active, and a pause icon when you are considered "idle" per the criteria mentioned above.
If you do not wish to see the timer, you can click on it once to hide it. The icon will still be
visible. Click the icon once more to display the timer again.
.. versionadded:: 2.6
As of version 2.6, clicking the timer text or icon in the status bar will toggle its visibility.
+3
View File
@@ -194,6 +194,7 @@ class Config:
# State
self.showViewerPanel = True # The panel for the viewer is visible
self.showEditToolBar = False # The document editor toolbar visibility
self.showSessionTime = True # Show the session time in the status bar
self.viewComments = True # Comments are shown in the viewer
self.viewSynopsis = True # Synopsis is shown in the viewer
@@ -674,6 +675,7 @@ class Config:
sec = "State"
self.showViewerPanel = conf.rdBool(sec, "showviewerpanel", self.showViewerPanel)
self.showEditToolBar = conf.rdBool(sec, "showedittoolbar", self.showEditToolBar)
self.showSessionTime = conf.rdBool(sec, "showsessiontime", self.showSessionTime)
self.viewComments = conf.rdBool(sec, "viewcomments", self.viewComments)
self.viewSynopsis = conf.rdBool(sec, "viewsynopsis", self.viewSynopsis)
self.searchCase = conf.rdBool(sec, "searchcase", self.searchCase)
@@ -784,6 +786,7 @@ class Config:
conf["State"] = {
"showviewerpanel": str(self.showViewerPanel),
"showedittoolbar": str(self.showEditToolBar),
"showsessiontime": str(self.showSessionTime),
"viewcomments": str(self.viewComments),
"viewsynopsis": str(self.viewSynopsis),
"searchcase": str(self.searchCase),
+16 -4
View File
@@ -30,14 +30,15 @@ from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from PyQt5.QtCore import QSize, Qt, pyqtSlot
from PyQt5.QtGui import QWheelEvent
from PyQt5.QtCore import QSize, Qt, pyqtSignal, pyqtSlot
from PyQt5.QtGui import QMouseEvent, QWheelEvent
from PyQt5.QtWidgets import (
QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
QWidget
QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
QToolButton, QWidget
)
from novelwriter import CONFIG, SHARED
from novelwriter.types import QtMouseLeft
if TYPE_CHECKING: # pragma: no cover
from novelwriter.guimain import GuiMain
@@ -196,3 +197,14 @@ class NIconToggleButton(QToolButton):
iconSize = self.iconSize()
self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height())))
return
class NClickableLabel(QLabel):
mouseClicked = pyqtSignal()
def mousePressEvent(self, event: QMouseEvent) -> None:
"""Capture a left mouse click and emit its signal."""
if event.button() == QtMouseLeft:
self.mouseClicked.emit()
return super().mousePressEvent(event)
+19 -2
View File
@@ -35,6 +35,7 @@ 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
logger = logging.getLogger(__name__)
@@ -92,12 +93,16 @@ class GuiMainStatus(QStatusBar):
# The Session Clock
# Set the minimum width so the label doesn't rescale every second
self.timeIcon = QLabel(self)
self.timeText = QLabel("", self)
self.timeIcon = NClickableLabel(self)
self.timeIcon.mouseClicked.connect(self._onClickTimerLabel)
self.timeText = NClickableLabel("", self)
self.timeText.setToolTip(self.tr("Session Time"))
self.timeText.setMinimumWidth(SHARED.theme.getTextWidth("00:00:00:"))
self.timeIcon.setContentsMargins(0, 0, 0, 0)
self.timeText.setContentsMargins(0, 0, 0, 0)
self.timeText.setVisible(CONFIG.showSessionTime)
self.timeText.mouseClicked.connect(self._onClickTimerLabel)
self.addPermanentWidget(self.timeIcon)
self.addPermanentWidget(self.timeText)
@@ -224,6 +229,18 @@ class GuiMainStatus(QStatusBar):
self.setDocumentStatus(nwTrinary.NEGATIVE if status else nwTrinary.POSITIVE)
return
##
# Private Slots
##
@pyqtSlot()
def _onClickTimerLabel(self) -> None:
"""Process mouse click on timer label."""
state = not CONFIG.showSessionTime
self.timeText.setVisible(state)
CONFIG.showSessionTime = state
return
##
# Debug
##
+2 -1
View File
@@ -1,5 +1,5 @@
[Meta]
timestamp = 2024-11-03 21:45:08
timestamp = 2024-12-29 17:30:10
[Main]
font =
@@ -71,6 +71,7 @@ useridletime = 300
[State]
showviewerpanel = True
showedittoolbar = False
showsessiontime = True
viewcomments = True
viewsynopsis = True
searchcase = False
+21 -3
View File
@@ -23,11 +23,13 @@ from __future__ import annotations
import pytest
from PyQt5.QtCore import QEvent, QPoint, QPointF, Qt
from PyQt5.QtGui import QKeyEvent, QWheelEvent
from PyQt5.QtGui import QKeyEvent, QMouseEvent, QWheelEvent
from PyQt5.QtWidgets import QWidget
from novelwriter.extensions.modified import NComboBox, NDialog, NDoubleSpinBox, NSpinBox
from novelwriter.types import QtModNone, QtRejected
from novelwriter.extensions.modified import (
NClickableLabel, NComboBox, NDialog, NDoubleSpinBox, NSpinBox
)
from novelwriter.types import QtModNone, QtMouseLeft, QtRejected
from tests.tools import SimpleDialog
@@ -139,3 +141,19 @@ def testExtModified_NDoubleSpinBox(qtbot, monkeypatch):
assert event.ignored is True
# qtbot.stop()
@pytest.mark.gui
def testExtModified_NClickableLabel(qtbot, monkeypatch):
"""Test the NClickableLabel class."""
widget = NClickableLabel()
dialog = SimpleDialog(widget)
dialog.show()
position = widget.rect().center()
event = QMouseEvent(
QEvent.Type.MouseButtonPress, position, QtMouseLeft, QtMouseLeft, QtModNone
)
with qtbot.waitSignal(widget.mouseClicked):
widget.mousePressEvent(event)
+44 -32
View File
@@ -39,56 +39,68 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
newDoc.writeDocument("# A Note\n\n")
nwGUI.rebuildIndex(beQuiet=True)
status = nwGUI.mainStatus
# Reference Time
refTime = time.time()
nwGUI.mainStatus.setRefTime(refTime)
assert nwGUI.mainStatus._refTime == refTime
status.setRefTime(refTime)
assert status._refTime == refTime
# Project Status
nwGUI.mainStatus.setProjectStatus(nwTrinary.NEUTRAL)
assert nwGUI.mainStatus.projIcon.state == nwTrinary.NEUTRAL
nwGUI.mainStatus.setProjectStatus(nwTrinary.NEGATIVE)
assert nwGUI.mainStatus.projIcon.state == nwTrinary.NEGATIVE
nwGUI.mainStatus.setProjectStatus(nwTrinary.POSITIVE)
assert nwGUI.mainStatus.projIcon.state == nwTrinary.POSITIVE
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
# Document Status
nwGUI.mainStatus.setDocumentStatus(nwTrinary.NEUTRAL)
assert nwGUI.mainStatus.docIcon.state == nwTrinary.NEUTRAL
nwGUI.mainStatus.setDocumentStatus(nwTrinary.NEGATIVE)
assert nwGUI.mainStatus.docIcon.state == nwTrinary.NEGATIVE
nwGUI.mainStatus.setDocumentStatus(nwTrinary.POSITIVE)
assert nwGUI.mainStatus.docIcon.state == nwTrinary.POSITIVE
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
# Idle Status
CONFIG.stopWhenIdle = False
nwGUI.mainStatus.setUserIdle(True)
nwGUI.mainStatus.updateTime()
assert nwGUI.mainStatus._userIdle is False
assert nwGUI.mainStatus.timeText.text() == "00:00:00"
status.setUserIdle(True)
status.updateTime()
assert status._userIdle is False
assert status.timeText.text() == "00:00:00"
CONFIG.stopWhenIdle = True
nwGUI.mainStatus.setUserIdle(True)
nwGUI.mainStatus.updateTime(5)
assert nwGUI.mainStatus._userIdle is True
assert nwGUI.mainStatus.timeText.text() != "00:00:00"
status.setUserIdle(True)
status.updateTime(5)
assert status._userIdle is True
assert status.timeText.text() != "00:00:00"
nwGUI.mainStatus.setUserIdle(False)
nwGUI.mainStatus.updateTime(5)
assert nwGUI.mainStatus._userIdle is False
assert nwGUI.mainStatus.timeText.text() != "00:00:00"
status.setUserIdle(False)
status.updateTime(5)
assert status._userIdle is False
assert status.timeText.text() != "00:00:00"
# Show/Hide Timer
assert status.timeText.isVisible() is True
assert CONFIG.showSessionTime is True
status._onClickTimerLabel()
assert status.timeText.isVisible() is False
assert CONFIG.showSessionTime is False
status._onClickTimerLabel()
assert status.timeText.isVisible() is True
assert CONFIG.showSessionTime is True
# Language
nwGUI.mainStatus.setLanguage("None", "None")
assert nwGUI.mainStatus.langText.text() == "None"
nwGUI.mainStatus.setLanguage("en", "None")
assert nwGUI.mainStatus.langText.text() == "American English"
status.setLanguage("None", "None")
assert status.langText.text() == "None"
status.setLanguage("en", "None")
assert status.langText.text() == "American English"
# Project Stats
CONFIG.incNotesWCount = False
nwGUI._lastTotalCount = 0
nwGUI._updateStatusWordCount()
assert nwGUI.mainStatus.statsText.text() == "Words: 9 (+9)"
assert status.statsText.text() == "Words: 9 (+9)"
# Update again, but through time tick
with monkeypatch.context() as mp:
@@ -96,6 +108,6 @@ def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
CONFIG.incNotesWCount = True
nwGUI._lastTotalCount = 0
nwGUI._timeTick()
assert nwGUI.mainStatus.statsText.text() == "Words: 11 (+11)"
assert status.statsText.text() == "Words: 11 (+11)"
# qtbot.stop()