plugin defined settings!!!!

This commit is contained in:
SpookyDervish
2025-10-30 17:45:30 +11:00
parent 13862bdad5
commit 94c2a72c41
5 changed files with 156 additions and 39 deletions

View File

@@ -1,7 +1,8 @@
from textual_window import Window from textual_window import Window
from textual.widgets import RichLog, Button from textual.widgets import RichLog, Button
from textual import work, on from textual import work
import textual.widgets import textual.widgets
from typing import Any
from traceback import format_exception from traceback import format_exception
from lupa import lua51 from lupa import lua51
@@ -68,6 +69,9 @@ class PluginLoader(Window):
) )
#self.app.mount(new_window) #self.app.mount(new_window)
return new_window return new_window
def create_setting(self, section_name: str, option_name: str, description: str, option_type: str, default_value: Any, on_changed_func = None):
self.app.config_handler.define_plugin_setting(section_name, option_name, description, option_type, default_value, on_changed_func)
# endregion # endregion
@@ -83,8 +87,6 @@ class PluginLoader(Window):
lua_runtime_stuff = { lua_runtime_stuff = {
"ui": { "ui": {
"notify": self.fake_notify, "notify": self.fake_notify,
#"createSidebarButton": self.create_sidebar_button,
#"setTheme": self.set_theme,
"runAction": self.fake_run_action, "runAction": self.fake_run_action,
"addBind": self.add_bind, "addBind": self.add_bind,
"createAction": self.create_action, "createAction": self.create_action,
@@ -93,7 +95,10 @@ class PluginLoader(Window):
"app": self.app, "app": self.app,
"onMessage": self.run_on_message "onMessage": self.run_on_message
}, },
"config": self.app.config_handler "config": {
"get": self.app.config_handler.get,
"defineSetting": self.create_setting
}
} }
log.write("[b]Finding plugins...[/]") log.write("[b]Finding plugins...[/]")
@@ -190,7 +195,7 @@ class PluginLoader(Window):
log.write("\n[b]Done loading plugins![/]") log.write("\n[b]Done loading plugins![/]")
if no_errors and int(self.app.config_handler.get("plugins", "log_timeout")) != -1: if no_errors and int(self.app.config_handler.get("plugins", "log_timeout")) != -1:
log.write("[d]Window will automatically close in 10 seconds.[/]") log.write(f'[d]Window will automatically close in {self.app.config_handler.get("plugins", "log_timeout")} seconds.[/]')
await asyncio.sleep(int(self.app.config_handler.get("plugins", "log_timeout"))) await asyncio.sleep(int(self.app.config_handler.get("plugins", "log_timeout")))
self.close_window() self.close_window()

14
plugins/test/lua/main.lua Normal file
View File

@@ -0,0 +1,14 @@
local plugin = {}
function test(newValue)
berry.ui.notify(tostring(newValue))
end
function plugin.run()
berry.config.defineSetting("Test Plugin", "test setting", "this is a description", "string", "hi, this is the default value", test)
berry.config.defineSetting("Test Plugin", "another setting!", "woah!", "boolean", "1", test)
berry.config.defineSetting("Test Plugin", "integer setting", "i love integers :O", "integer", "123", test)
berry.config.defineSetting("Test Plugin", "float setting", "i love floating point numbers :O", "float", "123.456", test)
end
return plugin

6
plugins/test/plugin.json Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "Test Plugin",
"version": "1.0.0",
"author": "SpookyDervish",
"dependencies": []
}

View File

@@ -1,6 +1,6 @@
from textual.screen import ModalScreen from textual.screen import ModalScreen
from textual.widgets import Label, Select, TabbedContent, TabPane, Switch, Input from textual.widgets import Label, Select, TabbedContent, TabPane, Switch, Input, Rule, Static
from textual.containers import Vertical, HorizontalGroup, VerticalGroup from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll
from textual.binding import Binding from textual.binding import Binding
@@ -22,6 +22,15 @@ class SettingsScreen(ModalScreen):
padding: 1; padding: 1;
} }
Rule {
color: $boost;
}
.thingy {
margin-top: 2;
text-style: bold;
}
.setting { .setting {
padding: 0 2; padding: 0 2;
content-align: center middle; content-align: center middle;
@@ -69,6 +78,9 @@ class SettingsScreen(ModalScreen):
self.notify("Restart for changes to apply.", title="Restart Required", severity="warning") self.notify("Restart for changes to apply.", title="Restart Required", severity="warning")
elif event.switch.id == "plugins-log": elif event.switch.id == "plugins-log":
self.app.config_handler.set("plugins", "log", str(int(event.value))) 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): def on_select_changed(self, event: Select.Changed):
if event.select.id == "colour-theme": if event.select.id == "colour-theme":
@@ -80,47 +92,83 @@ class SettingsScreen(ModalScreen):
if event.input.id == "log-timeout": if event.input.id == "log-timeout":
self.app.config_handler.set("plugins", "log_timeout", str(event.input.value)) 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): def compose(self):
with Vertical(id="window") as window: with Vertical(id="window") as window:
window.border_title = "Settings" window.border_title = "Settings"
with TabbedContent(): with TabbedContent():
with TabPane("Appearance"): with TabPane("Appearance"):
with HorizontalGroup(classes="setting"): with VerticalScroll():
with VerticalGroup(): with HorizontalGroup(classes="setting"):
yield Label("Colour Theme", classes="setting-name") with VerticalGroup():
yield Label("Colour theme used for the entire Berry app. You can get more themes with plugins!", classes="setting-desc") 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( yield Select.from_values(
(theme_name for theme_name in self.app._registered_themes.keys() if theme_name != "textual-ansi"), (theme_name for theme_name in self.app._registered_themes.keys() if theme_name != "textual-ansi"),
allow_blank=False, allow_blank=False,
id="colour-theme", id="colour-theme",
value=self.app.theme value=self.app.theme
) )
with TabPane("Editor"): with TabPane("Editor"):
with HorizontalGroup(classes="setting"): with VerticalScroll():
with VerticalGroup(): with HorizontalGroup(classes="setting"):
yield Label("Word Wrap", classes="setting-name") with VerticalGroup():
yield Label("Enable word wrap in the code editor.", classes="setting-desc") yield Label("Word Wrap", classes="setting-name")
yield Switch(value=bool(int(self.app.config_handler.get("editor", "word_wrap"))), id="word-wrap") 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 TabPane("Plugins"): 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 HorizontalGroup(classes="setting"):
with VerticalGroup(): with VerticalGroup():
yield Label("Plugins Enabled", classes="setting-name") yield Label("Plugins Log", classes="setting-name")
yield Label("Enable or disable Lua plugins. This requires a restart.", classes="setting-desc") yield Label("Show the plugin loader log on startup.", classes="setting-desc")
yield Switch(value=bool(int(self.app.config_handler.get("plugins", "enabled"))), id="plugins-enabled") 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="thick")
with HorizontalGroup(classes="setting"): for plugin_section, plugin_settings in self.app.config_handler.plugin_defined_settings.items():
with VerticalGroup(): yield Static(f"{plugin_section}", classes="thingy")
yield Label("Plugins Log", classes="setting-name") yield Rule()
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") for setting_name, setting in plugin_settings.items():
with HorizontalGroup(classes="setting"):
with HorizontalGroup(classes="setting"): with VerticalGroup():
with VerticalGroup(): yield Label(setting_name, classes="setting-name")
yield Label("Log Timeout", classes="setting-name") yield Label(setting["description"], classes="setting-desc")
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") 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

View File

@@ -2,6 +2,7 @@ from textual.app import App
from pathlib import Path from pathlib import Path
import os import os
import configparser import configparser
from typing import Any
class ConfigHandler: class ConfigHandler:
@@ -9,8 +10,43 @@ class ConfigHandler:
self.app: App = app self.app: App = app
self.config: configparser.ConfigParser = configparser.ConfigParser() self.config: configparser.ConfigParser = configparser.ConfigParser()
self.plugin_defined_settings = {}
self.config_dir: str = self.ensure_hidden_config_dir() self.config_dir: str = self.ensure_hidden_config_dir()
self.load_settings() self.load_settings()
def define_plugin_setting(self, plugin_name: str, option_name: str, description: str, option_type: str, default_value: Any, on_changed_func = None):
actual_type = None
if option_type == "boolean":
actual_type = bool
elif option_type == "string":
actual_type = str
elif option_type == "float":
actual_type = float
elif option_type == "integer":
actual_type = int
else:
raise Exception(f"Invalid type name \"{option_type}\".")
if plugin_name in self.plugin_defined_settings:
self.plugin_defined_settings[plugin_name][option_name] = {
"option_name": option_name,
"description": description,
"type": actual_type,
"default_value": default_value,
"on_changed_func": on_changed_func
}
else:
self.plugin_defined_settings[plugin_name] = {option_name: {
"option_name": option_name,
"description": description,
"type": actual_type,
"default_value": default_value,
"on_changed_func": on_changed_func
}}
# user hasnt used this plugin before
if self.get("plugin_" + plugin_name, option_name) == None:
self.set("plugin_" + plugin_name, option_name, default_value)
def ensure_hidden_config_dir(self): def ensure_hidden_config_dir(self):
config_dir = Path.home() / ".berry" config_dir = Path.home() / ".berry"
@@ -19,10 +55,18 @@ class ConfigHandler:
return config_dir return config_dir
def get(self, section: str, option: str): def get(self, section: str, option: str):
return self.config.get(section, option) return self.config.get(section, option, fallback=None)
def set(self, section: str, option: str, new_value: str): 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.config.set(section, option, new_value)
if section.startswith("plugin_"):
section = section.removeprefix("plugin_")
if self.plugin_defined_settings[section].get("on_changed_func", None) != None:
self.plugin_defined_settings[section]["on_changed_func"](new_value)
self.write_settings() self.write_settings()
def apply_settings(self): def apply_settings(self):