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_switch_changed(self, event: Switch.Changed): if event.switch.id == "word-wrap": self.app.query_one("#code-editor").soft_wrap = event.value self.app.config_handler.set("editor", "word_wrap", str(int(event.value))) elif event.switch.id == "line-numbers": self.app.query_one("#code-editor").show_line_numbers = event.value self.app.config_handler.set("editor", "line_numbers", str(int(event.value))) elif event.switch.id == "plugins-enabled": self.app.config_handler.set("plugins", "enabled", str(int(event.value))) self.notify("Restart for changes to apply.", title="Restart Required", severity="warning") elif event.switch.id == "plugins-log": self.app.config_handler.set("plugins", "log", str(int(event.value))) elif event.switch.has_class("plugin-option"): if event.switch.berry_changed_func != None: event.switch.berry_changed_func(event.switch.value) 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 on_input_changed(self, event: Input.Changed): if not event.input.is_valid: return if event.input.id == "log-timeout": self.app.config_handler.set("plugins", "log_timeout", str(event.input.value)) elif event.input.has_class("plugin-option"): if event.input.berry_changed_func != None: event.input.berry_changed_func(event.input.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 ) with TabPane("Editor"): with VerticalScroll(): with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label("Word Wrap", classes="setting-name") yield Label("Enable word wrap in the code editor.", classes="setting-desc") yield Switch(value=bool(int(self.app.config_handler.get("editor", "word_wrap"))), id="word-wrap") with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label("Line Numbers", classes="setting-name") yield Label("Show line numbers in the editor.", classes="setting-desc") yield Switch(value=bool(int(self.app.config_handler.get("editor", "line_numbers"))), id="line-numbers") with TabPane("Plugins"): with VerticalScroll(): with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label("Plugins Enabled", classes="setting-name") yield Label("Enable or disable Lua plugins. This requires a restart.", classes="setting-desc") yield Switch(value=bool(int(self.app.config_handler.get("plugins", "enabled"))), id="plugins-enabled") with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label("Plugins Log", classes="setting-name") yield Label("Show the plugin loader log on startup.", classes="setting-desc") yield Switch(value=bool(int(self.app.config_handler.get("plugins", "log"))), id="plugins-log") with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label("Log Timeout", classes="setting-name") yield Label("How many seconds before the log automatically closes. This gets overriden by the [b]Plugins Log[/] option. The window doesn't automatically close if there was an error. If this is set to -1, the log will not close automatically.", classes="setting-desc") yield Input(value=self.app.config_handler.get("plugins", "log_timeout"), id="log-timeout", type="integer") yield Static("Plugin-defined Settings", classes="thingy") yield Rule(line_style="double") for plugin_section, plugin_settings in self.app.config_handler.plugin_defined_settings.items(): yield Static(f"{plugin_section}", classes="thingy") yield Rule() for setting_name, setting in plugin_settings.items(): with HorizontalGroup(classes="setting"): with VerticalGroup(): yield Label(setting_name, classes="setting-name") yield Label(setting["description"], classes="setting-desc") value = self.app.config_handler.get(f"plugin_{plugin_section}", setting["option_name"]) new_widget = None if setting["type"] == bool: value = bool(int(value)) new_widget = Switch(value=value) elif setting["type"] == int: new_widget = Input(value=value, type="integer", placeholder=setting["default_value"]) elif setting["type"] == float: new_widget = Input(value=value, type="number", placeholder=setting["default_value"]) elif setting["type"] == str: new_widget = Input(value=value, placeholder=setting["default_value"]) new_widget.add_class("plugin-option") setattr(new_widget, "berry_changed_func", setting['on_changed_func']) yield new_widget