From ac2849f4355574bc79a41898d183a9b8e12942a0 Mon Sep 17 00:00:00 2001 From: Gladon4 Date: Wed, 17 Sep 2025 17:44:03 +0200 Subject: [PATCH 1/5] Added Theme Support Added Theme Suppot with the qt-themes package. Has a dropdown menu and a default is set in the config. --- .gitignore | 2 ++ docs/example_config.ini | 5 +++++ pyproject.toml | 3 ++- typstwriter/actions.py | 20 ++++++++++++++++++++ typstwriter/configuration.py | 1 + typstwriter/mainwindow.py | 26 ++++++++++++++++++++++++++ typstwriter/menubar.py | 5 +++++ typstwriter/typstwriter.py | 1 - 8 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..795adf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +uv.lock +__pycache__ \ No newline at end of file diff --git a/docs/example_config.ini b/docs/example_config.ini index 246ab35..67d531d 100644 --- a/docs/example_config.ini +++ b/docs/example_config.ini @@ -41,6 +41,11 @@ show_compiler_options = True # Show compiler output on startup show_compiler_output = True +[Theme] +# The default application theme +# It can be default, one_dark_two, catppuccin_macchiato, modern_light, github_light, blender, dracula, nord, catppuccin_latte, catppuccin_mocha, catppuccin_frappe, modern_dark, github_dark, monokai, atom_one +default_theme = default + [Internals] # The path where the list of recent files will be saved recent_files_path = ~/.local/share/typstwriter/recentFiles.txt diff --git a/pyproject.toml b/pyproject.toml index d4947e6..5941b74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,8 @@ dependencies = [ "qtpy >= 2.3", "pyside6 >= 6.4.0", "pygments >= 2.18", -"platformdirs >= 4.0" +"platformdirs >= 4.0", +"qt-themes >= 0.3.0" ] dynamic = ["version"] diff --git a/typstwriter/actions.py b/typstwriter/actions.py index ade13bf..bd91934 100644 --- a/typstwriter/actions.py +++ b/typstwriter/actions.py @@ -2,6 +2,8 @@ from qtpy import QtCore from qtpy import QtWidgets +import qt_themes + from typstwriter import util from typstwriter import logging @@ -117,6 +119,24 @@ def __init__(self, parent): self.layout.addAction(self.layout_editorL) self.layout.addAction(self.layout_editorR) + self.themes_list = [] + self.default = None + self.themes = QtWidgets.QActionGroup(self) + + self.theme = QtWidgets.QAction(self) + self.theme.setText("System Default") + self.theme.setCheckable(True) + self.themes_list.append((self.theme, None)) + self.themes.addAction(self.theme) + + for theme in sorted(list(qt_themes.get_themes().keys())): + self.theme = QtWidgets.QAction(self) + name = theme.replace("_", " ").title() + self.theme.setText(name) + self.theme.setCheckable(True) + self.themes_list.append((self.theme, theme)) + self.themes.addAction(self.theme) + self.show_fs_explorer = QtWidgets.QAction(self) self.show_fs_explorer.setText("Show FS Explorer") self.show_fs_explorer.setCheckable(True) diff --git a/typstwriter/configuration.py b/typstwriter/configuration.py index da84404..c6bb1e9 100644 --- a/typstwriter/configuration.py +++ b/typstwriter/configuration.py @@ -33,6 +33,7 @@ "show_fs_explorer": True, "show_compiler_options": True, "show_compiler_output": True}, + "Theme": {"default_theme": "default"}, "Internals": {"recent_files_path": default_recent_files_path, "recent_files_length": 16, "session_path": default_session_path}} # fmt: skip diff --git a/typstwriter/mainwindow.py b/typstwriter/mainwindow.py index e062443..7c79857 100644 --- a/typstwriter/mainwindow.py +++ b/typstwriter/mainwindow.py @@ -2,6 +2,8 @@ from qtpy import QtCore from qtpy import QtWidgets +import qt_themes + from typstwriter import menubar from typstwriter import toolbar from typstwriter import actions @@ -151,6 +153,9 @@ def __init__(self): state.main_file.Signal.connect(lambda s: self.CompilerOptions.main_changed(s)) state.main_file.Signal.connect(lambda s: self.PDFWidget.open(util.pdf_path(s))) + for theme in self.actions.themes_list: + theme[0].triggered.connect(lambda s, t=theme: self.set_theme(t[1])) + # For now only display errors self.CompilerConnector.compilation_started.connect(self.CompilerOutput.insert_block) self.CompilerConnector.new_stderr.connect(self.CompilerOutput.append_to_block) @@ -162,6 +167,9 @@ def __init__(self): # Use default layout self.use_default_layout() + # Use default theme + self.use_default_theme() + # Load last session if config.get("General", "resume_last_session", "bool"): self.load_session() @@ -179,6 +187,13 @@ def __init__(self): logger.info("Gui ready") + def set_theme(self, theme): + """Set Theme to selected.""" + if theme not in list(qt_themes.get_themes().keys()): + theme = None + + qt_themes.set_theme(theme) + def set_layout_typewriter(self): """Set Typstwriter Layout: PDF on top, Editor on bottom.""" # Vertical orientation @@ -224,6 +239,17 @@ def use_default_layout(self): self.splitter.setSizes([1e6, 1e6]) + def use_default_theme(self): + """Apply theme set in the config.""" + default_theme = config.get("Theme", "default_theme") + if default_theme not in list(qt_themes.get_themes().keys()): + default_theme = None + + # Set this way to make sure it is also selcted in the menu bar + for theme in self.actions.themes_list: + if theme[1] == default_theme: + theme[0].trigger() + def set_fs_explorer_visibility(self, visibility): """Set the visibility of the fs explplorer.""" self.FSdock.setVisible(visibility) diff --git a/typstwriter/menubar.py b/typstwriter/menubar.py index 0145562..1e57a02 100644 --- a/typstwriter/menubar.py +++ b/typstwriter/menubar.py @@ -60,6 +60,10 @@ def __init__(self, actions): self.layout_menu.setTitle("Layout") self.layout_menu.addActions(actions.layout.actions()) + self.theme_menu = QtWidgets.QMenu(self) + self.theme_menu.setTitle("Theme") + self.theme_menu.addActions(actions.themes.actions()) + self.editor_zoom_menu = QtWidgets.QMenu(self) self.editor_zoom_menu.setTitle("Editor Zoom") self.editor_zoom_menu.addAction(actions.font_size_up) @@ -67,6 +71,7 @@ def __init__(self, actions): self.editor_zoom_menu.addAction(actions.font_size_reset) self.menuView.addMenu(self.layout_menu) + self.menuView.addMenu(self.theme_menu) self.menuView.addSeparator() self.menuView.addAction(actions.show_fs_explorer) self.menuView.addAction(actions.show_compiler_options) diff --git a/typstwriter/typstwriter.py b/typstwriter/typstwriter.py index f0ef5ea..4b51f4e 100644 --- a/typstwriter/typstwriter.py +++ b/typstwriter/typstwriter.py @@ -6,7 +6,6 @@ os.environ["QT_API"] = "pyside6" import qtpy # noqa: E402 RUF100 - def main(): """Run Typstwriter.""" # Initialise logging From 70b081395729bbf85767c1a93b47688998e91ae8 Mon Sep 17 00:00:00 2001 From: Gladon4 Date: Mon, 20 Oct 2025 13:38:03 +0200 Subject: [PATCH 2/5] ruff check fix --- typstwriter/actions.py | 2 +- typstwriter/mainwindow.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/typstwriter/actions.py b/typstwriter/actions.py index bd91934..9f65aac 100644 --- a/typstwriter/actions.py +++ b/typstwriter/actions.py @@ -129,7 +129,7 @@ def __init__(self, parent): self.themes_list.append((self.theme, None)) self.themes.addAction(self.theme) - for theme in sorted(list(qt_themes.get_themes().keys())): + for theme in sorted(qt_themes.get_themes().keys()): self.theme = QtWidgets.QAction(self) name = theme.replace("_", " ").title() self.theme.setText(name) diff --git a/typstwriter/mainwindow.py b/typstwriter/mainwindow.py index 7c79857..a355850 100644 --- a/typstwriter/mainwindow.py +++ b/typstwriter/mainwindow.py @@ -244,7 +244,7 @@ def use_default_theme(self): default_theme = config.get("Theme", "default_theme") if default_theme not in list(qt_themes.get_themes().keys()): default_theme = None - + # Set this way to make sure it is also selcted in the menu bar for theme in self.actions.themes_list: if theme[1] == default_theme: From 8cf7d25588eac4b73a3836c717f549ff2a7bddbc Mon Sep 17 00:00:00 2001 From: Gladon4 Date: Wed, 22 Oct 2025 18:53:44 +0200 Subject: [PATCH 3/5] Restructuring changes --- .gitignore | 2 -- docs/example_config.ini | 8 +++----- typstwriter/actions.py | 29 ++++++++++++++--------------- typstwriter/configuration.py | 4 ++-- typstwriter/mainwindow.py | 13 +++++++------ typstwriter/typstwriter.py | 1 + 6 files changed, 27 insertions(+), 30 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 795adf4..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -uv.lock -__pycache__ \ No newline at end of file diff --git a/docs/example_config.ini b/docs/example_config.ini index 67d531d..f360736 100644 --- a/docs/example_config.ini +++ b/docs/example_config.ini @@ -5,6 +5,9 @@ working_directory = ~/Documents/typst_projects/ # working_directory = . # Resume last session on startup resume_last_session = True +# The default application theme +# It can be default, one_dark_two, catppuccin_macchiato, modern_light, github_light, blender, dracula, nord, catppuccin_latte, catppuccin_mocha, catppuccin_frappe, modern_dark, github_dark, monokai, atom_one +default_theme = default [Compiler] # The name of the Typst compiler on your system (in almost all cases simply typst) @@ -41,11 +44,6 @@ show_compiler_options = True # Show compiler output on startup show_compiler_output = True -[Theme] -# The default application theme -# It can be default, one_dark_two, catppuccin_macchiato, modern_light, github_light, blender, dracula, nord, catppuccin_latte, catppuccin_mocha, catppuccin_frappe, modern_dark, github_dark, monokai, atom_one -default_theme = default - [Internals] # The path where the list of recent files will be saved recent_files_path = ~/.local/share/typstwriter/recentFiles.txt diff --git a/typstwriter/actions.py b/typstwriter/actions.py index 9f65aac..c6c4c28 100644 --- a/typstwriter/actions.py +++ b/typstwriter/actions.py @@ -119,23 +119,22 @@ def __init__(self, parent): self.layout.addAction(self.layout_editorL) self.layout.addAction(self.layout_editorR) - self.themes_list = [] - self.default = None self.themes = QtWidgets.QActionGroup(self) - self.theme = QtWidgets.QAction(self) - self.theme.setText("System Default") - self.theme.setCheckable(True) - self.themes_list.append((self.theme, None)) - self.themes.addAction(self.theme) - - for theme in sorted(qt_themes.get_themes().keys()): - self.theme = QtWidgets.QAction(self) - name = theme.replace("_", " ").title() - self.theme.setText(name) - self.theme.setCheckable(True) - self.themes_list.append((self.theme, theme)) - self.themes.addAction(self.theme) + theme = QtWidgets.QAction(self) + theme.setData(None) + theme.setText("System Default") + theme.setCheckable(True) + theme.triggered.connect(lambda: qt_themes.set_theme(None)) + self.themes.addAction(theme) + + for t in sorted(qt_themes.get_themes().keys()): + theme = QtWidgets.QAction(self) + theme.setData(t) + theme.setText(t.replace("_", " ").title()) + theme.setCheckable(True) + theme.triggered.connect(lambda s, t=t: qt_themes.set_theme(t)) + self.themes.addAction(theme) self.show_fs_explorer = QtWidgets.QAction(self) self.show_fs_explorer.setText("Show FS Explorer") diff --git a/typstwriter/configuration.py b/typstwriter/configuration.py index c6bb1e9..bcad974 100644 --- a/typstwriter/configuration.py +++ b/typstwriter/configuration.py @@ -19,7 +19,8 @@ "./typstwriter.ini"] # fmt: skip default_config = {"General": {"working_directory": "~/", - "resume_last_session": False}, + "resume_last_session": False, + "default_theme": "default"}, "Compiler": {"name": "typst", "mode": "on_demand"}, "Editor": {"font_size": 10, @@ -33,7 +34,6 @@ "show_fs_explorer": True, "show_compiler_options": True, "show_compiler_output": True}, - "Theme": {"default_theme": "default"}, "Internals": {"recent_files_path": default_recent_files_path, "recent_files_length": 16, "session_path": default_session_path}} # fmt: skip diff --git a/typstwriter/mainwindow.py b/typstwriter/mainwindow.py index a355850..d764ad0 100644 --- a/typstwriter/mainwindow.py +++ b/typstwriter/mainwindow.py @@ -153,8 +153,8 @@ def __init__(self): state.main_file.Signal.connect(lambda s: self.CompilerOptions.main_changed(s)) state.main_file.Signal.connect(lambda s: self.PDFWidget.open(util.pdf_path(s))) - for theme in self.actions.themes_list: - theme[0].triggered.connect(lambda s, t=theme: self.set_theme(t[1])) + # for theme in self.actions.themes_list: + # theme[0].triggered.connect(lambda s, t=theme: self.set_theme(t[1])) # For now only display errors self.CompilerConnector.compilation_started.connect(self.CompilerOutput.insert_block) @@ -241,14 +241,15 @@ def use_default_layout(self): def use_default_theme(self): """Apply theme set in the config.""" - default_theme = config.get("Theme", "default_theme") + default_theme = config.get("General", "default_theme") if default_theme not in list(qt_themes.get_themes().keys()): default_theme = None # Set this way to make sure it is also selcted in the menu bar - for theme in self.actions.themes_list: - if theme[1] == default_theme: - theme[0].trigger() + for theme in list(self.actions.themes.actions()): + print(theme.data(), default_theme) + if theme.data() == default_theme: + theme.trigger() def set_fs_explorer_visibility(self, visibility): """Set the visibility of the fs explplorer.""" diff --git a/typstwriter/typstwriter.py b/typstwriter/typstwriter.py index 4b51f4e..f0ef5ea 100644 --- a/typstwriter/typstwriter.py +++ b/typstwriter/typstwriter.py @@ -6,6 +6,7 @@ os.environ["QT_API"] = "pyside6" import qtpy # noqa: E402 RUF100 + def main(): """Run Typstwriter.""" # Initialise logging From efeb17de28ca0f05744e0391612f598b368eadb8 Mon Sep 17 00:00:00 2001 From: Gladon4 Date: Wed, 22 Oct 2025 18:57:31 +0200 Subject: [PATCH 4/5] removed commented lines left in by accident --- typstwriter/mainwindow.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/typstwriter/mainwindow.py b/typstwriter/mainwindow.py index d764ad0..3171fa8 100644 --- a/typstwriter/mainwindow.py +++ b/typstwriter/mainwindow.py @@ -153,9 +153,6 @@ def __init__(self): state.main_file.Signal.connect(lambda s: self.CompilerOptions.main_changed(s)) state.main_file.Signal.connect(lambda s: self.PDFWidget.open(util.pdf_path(s))) - # for theme in self.actions.themes_list: - # theme[0].triggered.connect(lambda s, t=theme: self.set_theme(t[1])) - # For now only display errors self.CompilerConnector.compilation_started.connect(self.CompilerOutput.insert_block) self.CompilerConnector.new_stderr.connect(self.CompilerOutput.append_to_block) From 365bb319fea68078d1099813fa9b1ee396ab8cdd Mon Sep 17 00:00:00 2001 From: Bzero Date: Sun, 2 Nov 2025 21:33:18 +0100 Subject: [PATCH 5/5] Simplify theme setting. --- docs/example_config.ini | 2 +- typstwriter/actions.py | 2 +- typstwriter/configuration.py | 2 +- typstwriter/mainwindow.py | 18 ++---------------- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/docs/example_config.ini b/docs/example_config.ini index f360736..e19bbfa 100644 --- a/docs/example_config.ini +++ b/docs/example_config.ini @@ -7,7 +7,7 @@ working_directory = ~/Documents/typst_projects/ resume_last_session = True # The default application theme # It can be default, one_dark_two, catppuccin_macchiato, modern_light, github_light, blender, dracula, nord, catppuccin_latte, catppuccin_mocha, catppuccin_frappe, modern_dark, github_dark, monokai, atom_one -default_theme = default +theme = default [Compiler] # The name of the Typst compiler on your system (in almost all cases simply typst) diff --git a/typstwriter/actions.py b/typstwriter/actions.py index c6c4c28..2be6cb6 100644 --- a/typstwriter/actions.py +++ b/typstwriter/actions.py @@ -120,11 +120,11 @@ def __init__(self, parent): self.layout.addAction(self.layout_editorR) self.themes = QtWidgets.QActionGroup(self) - theme = QtWidgets.QAction(self) theme.setData(None) theme.setText("System Default") theme.setCheckable(True) + theme.setChecked(True) theme.triggered.connect(lambda: qt_themes.set_theme(None)) self.themes.addAction(theme) diff --git a/typstwriter/configuration.py b/typstwriter/configuration.py index bcad974..0c199a8 100644 --- a/typstwriter/configuration.py +++ b/typstwriter/configuration.py @@ -20,7 +20,7 @@ default_config = {"General": {"working_directory": "~/", "resume_last_session": False, - "default_theme": "default"}, + "theme": "default"}, "Compiler": {"name": "typst", "mode": "on_demand"}, "Editor": {"font_size": 10, diff --git a/typstwriter/mainwindow.py b/typstwriter/mainwindow.py index 3171fa8..59efe03 100644 --- a/typstwriter/mainwindow.py +++ b/typstwriter/mainwindow.py @@ -2,8 +2,6 @@ from qtpy import QtCore from qtpy import QtWidgets -import qt_themes - from typstwriter import menubar from typstwriter import toolbar from typstwriter import actions @@ -184,13 +182,6 @@ def __init__(self): logger.info("Gui ready") - def set_theme(self, theme): - """Set Theme to selected.""" - if theme not in list(qt_themes.get_themes().keys()): - theme = None - - qt_themes.set_theme(theme) - def set_layout_typewriter(self): """Set Typstwriter Layout: PDF on top, Editor on bottom.""" # Vertical orientation @@ -238,13 +229,8 @@ def use_default_layout(self): def use_default_theme(self): """Apply theme set in the config.""" - default_theme = config.get("General", "default_theme") - if default_theme not in list(qt_themes.get_themes().keys()): - default_theme = None - - # Set this way to make sure it is also selcted in the menu bar - for theme in list(self.actions.themes.actions()): - print(theme.data(), default_theme) + default_theme = config.get("General", "theme") + for theme in self.actions.themes.actions(): if theme.data() == default_theme: theme.trigger()