Skip to content

Commit d455cb7

Browse files
authored
Handle non-modal dialogs better (#1671)
2 parents 2772c9b + 9311117 commit d455cb7

File tree

3 files changed

+28
-17
lines changed

3 files changed

+28
-17
lines changed

novelwriter/common.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
logger = logging.getLogger(__name__)
4747

4848

49-
# =============================================================================================== #
49+
##
5050
# Checker Functions
51-
# =============================================================================================== #
51+
##
5252

5353
def checkStringNone(value: Any, default: str | None) -> str | None:
5454
"""Check if a variable is a string or a None."""
@@ -131,9 +131,9 @@ def checkPath(value: Any, default: Path) -> Path:
131131
return default
132132

133133

134-
# =============================================================================================== #
134+
##
135135
# Validator Functions
136-
# =============================================================================================== #
136+
##
137137

138138
def isHandle(value: Any) -> bool:
139139
"""Check if a string is a valid novelWriter handle.
@@ -204,9 +204,9 @@ def checkIntTuple(value: int, valid: tuple | list | set, default: int) -> int:
204204
return default
205205

206206

207-
# =============================================================================================== #
207+
##
208208
# Formatting Functions
209-
# =============================================================================================== #
209+
##
210210

211211
def formatInt(value: int) -> str:
212212
"""Formats an integer with k, M, G etc."""
@@ -250,9 +250,9 @@ def formatTime(t: int) -> str:
250250
return "ERROR"
251251

252252

253-
# =============================================================================================== #
253+
##
254254
# String Functions
255-
# =============================================================================================== #
255+
##
256256

257257
def simplified(text: str) -> str:
258258
"""Take a string and strip leading and trailing whitespaces, and
@@ -371,9 +371,9 @@ def numberToRoman(value: int, toLower: bool = False) -> str:
371371
return roman.lower() if toLower else roman
372372

373373

374-
# =============================================================================================== #
374+
##
375375
# Encoder Functions
376-
# =============================================================================================== #
376+
##
377377

378378
def jsonEncode(data: dict | list | tuple, n: int = 0, nmax: int = 0) -> str:
379379
"""Encode a dictionary, list or tuple as a json object or array, and
@@ -463,9 +463,9 @@ def indentChildren(elem, level):
463463
return
464464

465465

466-
# =============================================================================================== #
466+
##
467467
# File and File System Functions
468-
# =============================================================================================== #
468+
##
469469

470470
def readTextFile(path: str | Path) -> str:
471471
"""Read the content of a text file in a robust manner."""
@@ -507,9 +507,9 @@ def openExternalPath(path: Path) -> bool:
507507
return False
508508

509509

510-
# =============================================================================================== #
510+
##
511511
# Classes
512-
# =============================================================================================== #
512+
##
513513

514514
class NWConfigParser(ConfigParser):
515515
"""Common: Adapted Config Parser

novelwriter/guimain.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,8 @@ def showProjectDetailsDialog(self) -> None:
899899
def showBuildManuscriptDialog(self) -> None:
900900
"""Open the build manuscript dialog."""
901901
if SHARED.hasProject:
902-
dialog = GuiManuscript(self)
902+
if (dialog := SHARED.findTopLevelWidget(GuiManuscript)) is None:
903+
dialog = GuiManuscript(self)
903904
dialog.setModal(False)
904905
dialog.show()
905906
dialog.raise_()
@@ -920,7 +921,8 @@ def showProjectWordListDialog(self) -> None:
920921
def showWritingStatsDialog(self) -> None:
921922
"""Open the session stats dialog."""
922923
if SHARED.hasProject:
923-
dialog = GuiWritingStats(self)
924+
if (dialog := SHARED.findTopLevelWidget(GuiWritingStats)) is None:
925+
dialog = GuiWritingStats(self)
924926
dialog.setModal(False)
925927
dialog.show()
926928
dialog.raise_()

novelwriter/shared.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import logging
2828

2929
from time import time
30-
from typing import TYPE_CHECKING
30+
from typing import TYPE_CHECKING, TypeVar
3131
from pathlib import Path
3232

3333
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
@@ -42,6 +42,8 @@
4242

4343
logger = logging.getLogger(__name__)
4444

45+
NWWidget = TypeVar("NWWidget", bound=QWidget)
46+
4547

4648
class SharedData(QObject):
4749

@@ -215,6 +217,13 @@ def runInThreadPool(self, runnable: QRunnable, priority: int = 0) -> None:
215217
QThreadPool.globalInstance().start(runnable, priority=priority)
216218
return
217219

220+
def findTopLevelWidget(self, kind: type[NWWidget]) -> NWWidget | None:
221+
"""Find a top level widget."""
222+
for widget in self.mainGui.children():
223+
if isinstance(widget, kind):
224+
return widget
225+
return None
226+
218227
##
219228
# Signal Proxy
220229
##

0 commit comments

Comments
 (0)