diff --git a/src/settings_store.py b/src/settings_store.py new file mode 100644 index 0000000..b696411 --- /dev/null +++ b/src/settings_store.py @@ -0,0 +1,59 @@ +from textual.app import App +from pathlib import Path +import os +import configparser +from typing import Any + + +class ConfigHandler: + def __init__(self, app: App): + self.app: App = app + self.config: configparser.ConfigParser = configparser.ConfigParser() + + self.plugin_defined_settings = {} + self.config_dir: str = self.ensure_hidden_config_dir() + self.load_settings() + + def ensure_hidden_config_dir(self): + config_dir = Path.home() / ".termdaw" + config_dir.mkdir(parents=True, exist_ok=True) + + return config_dir + + def get(self, section: str, option: str): + if not self.config.has_section(section): + self.config.add_section(section) + if not self.config.has_option(section, option): + self.set(section, option, "0") + return "0" + + return self.config.get(section, option, fallback=None) + + def set(self, section: str, option: str, new_value: str): + if not self.config.has_section(section): + self.config.add_section(section) + self.config.set(section, option, new_value) + + self.write_settings() + + def apply_settings(self): + self.app.theme = self.get("appearance", "colour_theme") + + def write_settings(self): + with open(self.config_dir / "config.ini", "w") as configfile: + self.config.write(configfile) + + def load_settings(self): + if os.path.isfile(self.config_dir / "config.ini"): + self.config.read(self.config_dir / "config.ini") + return + + self.config["config"] = { + "version": "1" + } + self.config["appearance"] = { + "colour_theme": "textual-dark" + } + + with open(self.config_dir / "config.ini", "w") as configfile: + self.config.write(configfile) \ No newline at end of file diff --git a/src/ui/app.py b/src/ui/app.py index d89e0fc..a5be61b 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -9,9 +9,12 @@ from ui.widgets.timeline import Timeline, TimelineRow from ui.widgets.project_settings import ProjectSettings from ui.widgets.channel import Channel from ui.widgets.context_menu import ContextMenu, NoSelectStatic +from ui.widgets.chunk_types.audio import AudioChunk, Chunk +from ui.screens.settings import SettingsScreen from project import ProjectChannel, Project, ChunkType -from ui.widgets.chunk_types.audio import AudioChunk, Chunk +from settings_store import ConfigHandler + class AppUI(App): @@ -23,6 +26,8 @@ class AppUI(App): self.last_zoom_level = self.zoom_level self.project = project + self.config_handler = ConfigHandler(self) + self.open_project_path = None self.first_tab_click = True # stupid events firing when the app is first composed :/ @@ -135,6 +140,9 @@ class AppUI(App): ) ), callback=callback) + case "Settings": + self.push_screen(SettingsScreen()) + case _: self.notify("Sorry, that isn't implemented yet... ;-;", severity="warning") @@ -175,4 +183,8 @@ class AppUI(App): yield Sidebar() yield Timeline() yield ProjectSettings() - yield Footer() \ No newline at end of file + yield Footer() + + def on_mount(self): + # load config into the UI + self.config_handler.apply_settings() \ No newline at end of file diff --git a/src/ui/screens/settings.py b/src/ui/screens/settings.py new file mode 100644 index 0000000..19d81bc --- /dev/null +++ b/src/ui/screens/settings.py @@ -0,0 +1,93 @@ +from textual.screen import ModalScreen +from textual.widgets import Label, Select, TabbedContent, TabPane, Switch, Input, Rule, Static +from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll +from textual.binding import Binding + + +class SettingsScreen(ModalScreen): + border_title = "Settings" + DEFAULT_CSS = """ + SettingsScreen { + align: center middle; + + #window { + border: panel $primary; + background: $background; + width: 65%; + height: 65%; + padding: 1; + } + + TabPane { + padding: 1; + } + + Rule { + color: $boost; + } + + .thingy { + margin-top: 2; + text-style: bold; + } + + .setting { + padding: 0 2; + content-align: center middle; + margin-bottom: 1; + + .setting-name { + text-style: bold; + } + + .setting-desc { + text-style: dim; + width: 100%; + min-width: 30; + } + + VerticalGroup { + width: 50%; + min-width: 20; + margin-right: 1; + } + + Select, Input { + max-width: 30; + } + } + } + """ + + BINDINGS = [ + Binding("escape", "close", "Close") + ] + + def __init__(self): + super().__init__() + + def action_close(self): + self.dismiss() + + def on_select_changed(self, event: Select.Changed): + if event.select.id == "colour-theme": + self.app.theme = event.value + self.app.config_handler.set("appearance", "colour_theme", str(event.value)) + + def compose(self): + with Vertical(id="window") as window: + window.border_title = "Settings" + with TabbedContent(): + with TabPane("Appearance"): + with VerticalScroll(): + with HorizontalGroup(classes="setting"): + with VerticalGroup(): + yield Label("Colour Theme", classes="setting-name") + yield Label("Colour theme used for the entire Berry app. You can get more themes with plugins!", classes="setting-desc") + + yield Select.from_values( + (theme_name for theme_name in self.app._registered_themes.keys() if theme_name != "textual-ansi"), + allow_blank=False, + id="colour-theme", + value=self.app.theme + ) \ No newline at end of file