diff --git a/novelwriter/config.py b/novelwriter/config.py index f1389253..b5d09e2d 100644 --- a/novelwriter/config.py +++ b/novelwriter/config.py @@ -52,6 +52,31 @@ logger = logging.getLogger(__name__) class Config: + __slots__ = ( + "_confPath", "_dataPath", "_homePath", "_backPath", "_appPath", "_appRoot", "_hasError", + "_errData", "_nwLangPath", "_qtLangPath", "_qLocale", "_dLocale", "_dShortDate", + "_dShortDateTime", "_qtTrans", "_recentProjects", "_recentPaths", "_backupPath", + + "appName", "appHandle", "pdfDocs", "guiLocale", "guiTheme", "guiSyntax", "guiFont", + "hideVScroll", "hideHScroll", "lastNotes", "nativeFont", "iconTheme", "iconColTree", + "iconColDocs", "mainWinSize", "welcomeWinSize", "prefsWinSize", "mainPanePos", + "viewPanePos", "outlinePanePos", "autoSaveProj", "autoSaveDoc", "emphLabels", + "backupOnClose", "askBeforeBackup", "textFont", "textWidth", "textMargin", "tabWidth", + "focusWidth", "hideFocusFooter", "showFullPath", "autoSelect", "doJustify", + "showTabsNSpaces", "showLineEndings", "showMultiSpaces", "doReplace", "doReplaceSQuote", + "doReplaceDQuote", "doReplaceDash", "doReplaceDots", "autoScroll", "autoScrollPos", + "scrollPastEnd", "dialogStyle", "allowOpenDial", "dialogLine", "narratorBreak", + "narratorDialog", "altDialogOpen", "altDialogClose", "highlightEmph", "stopWhenIdle", + "userIdleTime", "incNotesWCount", "fmtApostrophe", "fmtSQuoteOpen", "fmtSQuoteClose", + "fmtDQuoteOpen", "fmtDQuoteClose", "fmtPadBefore", "fmtPadAfter", "fmtPadThin", + "spellLanguage", "showViewerPanel", "showEditToolBar", "showSessionTime", "viewComments", + "viewSynopsis", "searchCase", "searchWord", "searchRegEx", "searchLoop", "searchNextFile", + "searchMatchCap", "searchProjCase", "searchProjWord", "searchProjRegEx", "verQtString", + "verQtValue", "verPyQtString", "verPyQtValue", "verPyString", "osType", "osLinux", + "osWindows", "osDarwin", "osUnknown", "hostName", "kernelVer", "isDebug", "memInfo", + "hasEnchant", + ) + LANG_NW = 1 LANG_PROJ = 2 @@ -126,12 +151,12 @@ class Config: self.iconColDocs = False # Keep theme colours on documents # Size Settings - self._mainWinSize = [1200, 650] # Last size of the main GUI window - self._welcomeSize = [800, 550] # Last size of the welcome window - self._prefsWinSize = [700, 615] # Last size of the Preferences dialog - self._mainPanePos = [300, 800] # Last position of the main window splitter - self._viewPanePos = [500, 150] # Last position of the document viewer splitter - self._outlnPanePos = [500, 150] # Last position of the outline panel splitter + self.mainWinSize = [1200, 650] # Last size of the main GUI window + self.welcomeWinSize = [800, 550] # Last size of the welcome window + self.prefsWinSize = [700, 615] # Last size of the Preferences dialog + self.mainPanePos = [300, 800] # Last position of the main window splitter + self.viewPanePos = [500, 150] # Last position of the document viewer splitter + self.outlinePanePos = [500, 150] # Last position of the outline panel splitter # Project Settings self.autoSaveProj = 60 # Interval for auto-saving project, in seconds @@ -269,30 +294,6 @@ class Config: def recentProjects(self) -> RecentProjects: return self._recentProjects - @property - def mainWinSize(self) -> list[int]: - return self._mainWinSize - - @property - def welcomeWinSize(self) -> list[int]: - return self._welcomeSize - - @property - def preferencesWinSize(self) -> list[int]: - return self._prefsWinSize - - @property - def mainPanePos(self) -> list[int]: - return self._mainPanePos - - @property - def viewPanePos(self) -> list[int]: - return self._viewPanePos - - @property - def outlinePanePos(self) -> list[int]: - return self._outlnPanePos - ## # Getters ## @@ -300,17 +301,9 @@ class Config: def getTextWidth(self, focusMode: bool = False) -> int: """Get the text with for the correct editor mode.""" if focusMode: - return self.pxInt(max(self.focusWidth, 200)) + return max(self.focusWidth, 200) else: - return self.pxInt(max(self.textWidth, 200)) - - def getTextMargin(self) -> int: - """Get the scaled text margin.""" - return self.pxInt(max(self.textMargin, 0)) - - def getTabWidth(self) -> int: - """Get the scaled tab width.""" - return self.pxInt(max(self.tabWidth, 0)) + return max(self.textWidth, 200) ## # Setters @@ -322,35 +315,20 @@ class Config: adjust it a bit, and we don't want the main window to shrink or grow each time the app is opened. """ - if abs(self._mainWinSize[0] - width) > 5: - self._mainWinSize[0] = width - if abs(self._mainWinSize[1] - height) > 5: - self._mainWinSize[1] = height + if abs(self.mainWinSize[0] - width) > 5: + self.mainWinSize[0] = width + if abs(self.mainWinSize[1] - height) > 5: + self.mainWinSize[1] = height return def setWelcomeWinSize(self, width: int, height: int) -> None: """Set the size of the Preferences dialog window.""" - self._welcomeSize = [width, height] + self.welcomeWinSize = [width, height] return def setPreferencesWinSize(self, width: int, height: int) -> None: """Set the size of the Preferences dialog window.""" - self._prefsWinSize = [width, height] - return - - def setMainPanePos(self, pos: list[int]) -> None: - """Set the position of the main GUI splitter.""" - self._mainPanePos = pos - return - - def setViewPanePos(self, pos: list[int]) -> None: - """Set the position of the viewer meta data splitter.""" - self._viewPanePos = pos - return - - def setOutlinePanePos(self, pos: list[int]) -> None: - """Set the position of the outline details splitter.""" - self._outlnPanePos = pos + self.prefsWinSize = [width, height] return def setLastPath(self, key: str, path: str | Path) -> None: @@ -614,12 +592,12 @@ class Config: # Sizes sec = "Sizes" - self._mainWinSize = conf.rdIntList(sec, "mainwindow", self._mainWinSize) - self._welcomeSize = conf.rdIntList(sec, "welcome", self._welcomeSize) - self._prefsWinSize = conf.rdIntList(sec, "preferences", self._prefsWinSize) - self._mainPanePos = conf.rdIntList(sec, "mainpane", self._mainPanePos) - self._viewPanePos = conf.rdIntList(sec, "viewpane", self._viewPanePos) - self._outlnPanePos = conf.rdIntList(sec, "outlinepane", self._outlnPanePos) + self.mainWinSize = conf.rdIntList(sec, "mainwindow", self.mainWinSize) + self.welcomeWinSize = conf.rdIntList(sec, "welcome", self.welcomeWinSize) + self.prefsWinSize = conf.rdIntList(sec, "preferences", self.prefsWinSize) + self.mainPanePos = conf.rdIntList(sec, "mainpane", self.mainPanePos) + self.viewPanePos = conf.rdIntList(sec, "viewpane", self.viewPanePos) + self.outlinePanePos = conf.rdIntList(sec, "outlinepane", self.outlinePanePos) # Project sec = "Project" @@ -728,12 +706,12 @@ class Config: } conf["Sizes"] = { - "mainwindow": self._packList(self._mainWinSize), - "welcome": self._packList(self._welcomeSize), - "preferences": self._packList(self._prefsWinSize), - "mainpane": self._packList(self._mainPanePos), - "viewpane": self._packList(self._viewPanePos), - "outlinepane": self._packList(self._outlnPanePos), + "mainwindow": self._packList(self.mainWinSize), + "welcome": self._packList(self.welcomeWinSize), + "preferences": self._packList(self.prefsWinSize), + "mainpane": self._packList(self.mainPanePos), + "viewpane": self._packList(self.viewPanePos), + "outlinepane": self._packList(self.outlinePanePos), } conf["Project"] = { diff --git a/novelwriter/dialogs/preferences.py b/novelwriter/dialogs/preferences.py index 8e3d65f1..8a433273 100644 --- a/novelwriter/dialogs/preferences.py +++ b/novelwriter/dialogs/preferences.py @@ -59,7 +59,7 @@ class GuiPreferences(NDialog): self.setObjectName("GuiPreferences") self.setWindowTitle(self.tr("Preferences")) self.setMinimumSize(CONFIG.pxInt(600), CONFIG.pxInt(500)) - self.resize(*CONFIG.preferencesWinSize) + self.resize(*CONFIG.prefsWinSize) # Title self.titleLabel = NColourLabel( diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 1a4f7c4d..d67f514c 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -331,7 +331,7 @@ class GuiDocEditor(QPlainTextEdit): # Due to cursor visibility, a part of the margin must be # allocated to the document itself. See issue #1112. self._qDocument.setDocumentMargin(4) - self._vpMargin = max(CONFIG.getTextMargin() - 4, 0) + self._vpMargin = max(CONFIG.textMargin - 4, 0) self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin) # Also set the document text options for the document text flow @@ -359,7 +359,7 @@ class GuiDocEditor(QPlainTextEdit): self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) # Refresh the tab stops - self.setTabStopDistance(CONFIG.getTabWidth()) + self.setTabStopDistance(CONFIG.tabWidth) # If we have a document open, we should refresh it in case the # font changed, otherwise we just clear the editor entirely, diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index bc545b6f..e813a5c4 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -200,7 +200,7 @@ class GuiDocViewer(QTextBrowser): self.setHorizontalScrollBarPolicy(QtScrollAsNeeded) # Refresh the tab stops - self.setTabStopDistance(CONFIG.getTabWidth()) + self.setTabStopDistance(CONFIG.tabWidth) # If we have a document open, we should reload it in case the font changed self.reloadText() @@ -250,7 +250,7 @@ class GuiDocViewer(QTextBrowser): self.setDocumentTitle(tHandle) self.setDocument(qDoc.document) - self.setTabStopDistance(CONFIG.getTabWidth()) + self.setTabStopDistance(CONFIG.tabWidth) if self._docHandle == tHandle: # This is a refresh, so we set the scrollbar back to where it was @@ -308,7 +308,7 @@ class GuiDocViewer(QTextBrowser): """Automatically adjust the margins so the text is centred.""" wW = self.width() wH = self.height() - cM = CONFIG.getTextMargin() + cM = CONFIG.textMargin vBar = self.verticalScrollBar() sW = vBar.width() if vBar.isVisible() else 0 diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 56e17517..3769090d 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -860,10 +860,10 @@ class GuiMain(QMainWindow): logger.info("Exiting novelWriter") if not SHARED.focusMode: - CONFIG.setMainPanePos(self.splitMain.sizes()) - CONFIG.setOutlinePanePos(self.outlineView.splitSizes()) + CONFIG.mainPanePos = self.splitMain.sizes() + CONFIG.outlinePanePos = self.outlineView.splitSizes() if self.docViewerPanel.isVisible(): - CONFIG.setViewPanePos(self.splitView.sizes()) + CONFIG.viewPanePos = self.splitView.sizes() CONFIG.showViewerPanel = self.docViewerPanel.isVisible() wFull = Qt.WindowState.WindowFullScreen diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index 7238ea84..448b8db2 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -750,11 +750,11 @@ class _PreviewWidget(QTextBrowser): self.setPalette(dPalette) self.setMinimumWidth(40*SHARED.theme.textNWidth) - self.setTabStopDistance(CONFIG.getTabWidth()) + self.setTabStopDistance(CONFIG.tabWidth) self.setOpenExternalLinks(False) self.setOpenLinks(False) - self.document().setDocumentMargin(CONFIG.getTextMargin()) + self.document().setDocumentMargin(CONFIG.textMargin) self.setPlaceholderText(self.tr( "Press the \"Preview\" button to generate ..." )) @@ -852,9 +852,9 @@ class _PreviewWidget(QTextBrowser): self.buildProgress.setCentreText(self.tr("Processing ...")) QApplication.processEvents() - document.setDocumentMargin(CONFIG.getTextMargin()) + document.setDocumentMargin(CONFIG.textMargin) self.setDocument(document) - self.setTabStopDistance(CONFIG.getTabWidth()) + self.setTabStopDistance(CONFIG.tabWidth) self._docTime = int(time()) self._updateBuildAge() diff --git a/tests/test_base/test_base_config.py b/tests/test_base/test_base_config.py index 34bac678..6d968292 100644 --- a/tests/test_base/test_base_config.py +++ b/tests/test_base/test_base_config.py @@ -254,52 +254,29 @@ def testBaseConfig_SettersGetters(fncPath): tstConf.setMainWinSize(70, 70) assert tstConf.mainWinSize == [70, 70] - assert tstConf._mainWinSize == [70, 70] + assert tstConf.mainWinSize == [70, 70] tstConf.setMainWinSize(1200, 650) # Welcome Window Size tstConf.setWelcomeWinSize(70, 70) assert tstConf.welcomeWinSize == [70, 70] - assert tstConf._welcomeSize == [70, 70] + assert tstConf.welcomeWinSize == [70, 70] tstConf.setWelcomeWinSize(800, 500) # Preferences Size tstConf.setPreferencesWinSize(70, 70) - assert tstConf.preferencesWinSize == [70, 70] - assert tstConf._prefsWinSize == [70, 70] + assert tstConf.prefsWinSize == [70, 70] + assert tstConf.prefsWinSize == [70, 70] tstConf.setPreferencesWinSize(700, 615) - # Main Pane Splitter - tstConf.setMainPanePos([200, 700]) - assert tstConf.mainPanePos == [200, 700] - assert tstConf._mainPanePos == [200, 700] - - tstConf.setMainPanePos([300, 800]) - - # View Pane Splitter - tstConf.setViewPanePos([400, 250]) - assert tstConf.viewPanePos == [400, 250] - assert tstConf._viewPanePos == [400, 250] - - tstConf.setViewPanePos([500, 150]) - - # Outline Pane Splitter - tstConf.setOutlinePanePos([400, 250]) - assert tstConf.outlinePanePos == [400, 250] - assert tstConf._outlnPanePos == [400, 250] - - tstConf.setOutlinePanePos([500, 150]) - # Getters Only # ============ assert tstConf.getTextWidth(False) == 700 assert tstConf.getTextWidth(True) == 800 - assert tstConf.getTextMargin() == 40 - assert tstConf.getTabWidth() == 40 @pytest.mark.base diff --git a/tests/test_dialogs/test_dlg_preferences.py b/tests/test_dialogs/test_dlg_preferences.py index bdc4db27..e5bed49a 100644 --- a/tests/test_dialogs/test_dlg_preferences.py +++ b/tests/test_dialogs/test_dlg_preferences.py @@ -72,17 +72,16 @@ def testDlgPreferences_Main(qtbot, monkeypatch, nwGUI, tstPaths): prefs.close() # Check Fallback Values - with monkeypatch.context() as mp: - mp.setattr(CONFIG, "hasEnchant", False) - prefs = GuiPreferences(nwGUI) - prefs.show() + CONFIG.hasEnchant = False + prefs = GuiPreferences(nwGUI) + prefs.show() - # Check Spell Checking - spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())] - assert len(spelling) == 1 - assert spelling == [""] + # Check Spell Checking + spelling = [prefs.spellLanguage.itemData(i) for i in range(prefs.spellLanguage.count())] + assert len(spelling) == 1 + assert spelling == [""] - prefs.close() + prefs.close() # qtbot.stop() @@ -138,13 +137,14 @@ def testDlgPreferences_Actions(qtbot, monkeypatch, nwGUI): @pytest.mark.gui -def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths): +def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, fncPath, tstPaths): """Test the preferences dialog settings.""" spelling = [("en", "English [en]"), ("de", "Deutch [de]")] - languages = [("en_GB", "British English"), ("en_US", "US English")] - monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda: spelling) - monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages) + + (fncPath / "nw_en_US.qm").touch() + (fncPath / "project_en_US.json").touch() + CONFIG._nwLangPath = fncPath prefs = GuiPreferences(nwGUI) with qtbot.waitExposed(prefs): diff --git a/tests/test_dialogs/test_dlg_projectsettings.py b/tests/test_dialogs/test_dlg_projectsettings.py index 54b5cd1d..d28322d4 100644 --- a/tests/test_dialogs/test_dlg_projectsettings.py +++ b/tests/test_dialogs/test_dlg_projectsettings.py @@ -88,7 +88,12 @@ def testDlgProjSettings_SettingsPage(qtbot, monkeypatch, nwGUI, fncPath, projPat """Test the settings page of the dialog.""" languages = [("en", "English"), ("de", "German")] monkeypatch.setattr(SHARED._spelling, "listDictionaries", lambda *a: languages) - monkeypatch.setattr(CONFIG, "listLanguages", lambda *a: languages) + + (fncPath / "nw_en.qm").touch() + (fncPath / "nw_de.qm").touch() + (fncPath / "project_en.json").touch() + (fncPath / "project_de.json").touch() + CONFIG._nwLangPath = fncPath # Create new project buildTestProject(nwGUI, projPath)