2025-10-29 13:11:51 +11:00
from textual . screen import ModalScreen
2025-10-30 17:45:30 +11:00
from textual . widgets import Label , Select , TabbedContent , TabPane , Switch , Input , Rule , Static
from textual . containers import Vertical , HorizontalGroup , VerticalGroup , VerticalScroll
2025-10-29 13:11:51 +11:00
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 ;
}
2025-10-30 17:45:30 +11:00
Rule {
color : $ boost ;
}
. thingy {
margin - top : 2 ;
text - style : bold ;
}
2025-10-29 13:11:51 +11:00
. setting {
padding : 0 2 ;
2025-10-29 21:06:34 +11:00
content - align : center middle ;
2025-10-30 07:53:19 +11:00
margin - bottom : 1 ;
2025-10-29 21:06:34 +11:00
2025-10-29 13:11:51 +11:00
. setting - name {
text - style : bold ;
}
2025-10-29 21:06:34 +11:00
2025-10-29 13:11:51 +11:00
. setting - desc {
text - style : dim ;
2025-10-29 15:31:56 +11:00
width : 100 % ;
min - width : 30 ;
2025-10-29 13:11:51 +11:00
}
2025-10-29 21:06:34 +11:00
2025-10-29 13:11:51 +11:00
VerticalGroup {
2025-10-29 15:31:56 +11:00
width : 50 % ;
min - width : 20 ;
2025-10-30 07:53:19 +11:00
margin - right : 1 ;
2025-10-29 15:31:56 +11:00
}
2025-10-30 07:53:19 +11:00
Select , Input {
2025-10-29 21:06:34 +11:00
max - width : 30 ;
2025-10-29 13:11:51 +11:00
}
}
}
"""
BINDINGS = [
Binding ( " escape " , " close " , " Close " )
]
2025-10-30 06:59:20 +11:00
def __init__ ( self ) :
super ( ) . __init__ ( )
2025-10-29 13:11:51 +11:00
def action_close ( self ) :
self . dismiss ( )
2025-10-30 06:59:20 +11:00
def on_switch_changed ( self , event : Switch . Changed ) :
if event . switch . id == " word-wrap " :
self . app . query_one ( " #code-editor " ) . soft_wrap = event . value
2025-10-30 07:20:01 +11:00
self . app . config_handler . set ( " editor " , " word_wrap " , str ( int ( event . value ) ) )
2025-10-30 07:53:19 +11:00
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 " )
2025-10-30 12:38:14 +11:00
elif event . switch . id == " plugins-log " :
self . app . config_handler . set ( " plugins " , " log " , str ( int ( event . value ) ) )
2025-10-30 17:45:30 +11:00
elif event . switch . has_class ( " plugin-option " ) :
if event . switch . berry_changed_func != None :
event . switch . berry_changed_func ( event . switch . value )
2025-10-30 06:59:20 +11:00
def on_select_changed ( self , event : Select . Changed ) :
if event . select . id == " colour-theme " :
self . app . theme = event . value
2025-10-30 07:20:01 +11:00
self . app . config_handler . set ( " appearance " , " colour_theme " , str ( event . value ) )
2025-10-30 06:59:20 +11:00
2025-10-30 12:38:14 +11:00
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 ) )
2025-10-30 17:45:30 +11:00
elif event . input . has_class ( " plugin-option " ) :
if event . input . berry_changed_func != None :
event . input . berry_changed_func ( event . input . value )
2025-10-30 12:38:14 +11:00
2025-10-29 13:11:51 +11:00
def compose ( self ) :
with Vertical ( id = " window " ) as window :
window . border_title = " Settings "
with TabbedContent ( ) :
2025-10-29 15:31:56 +11:00
with TabPane ( " Appearance " ) :
2025-10-30 17:45:30 +11:00
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 " )
2025-10-30 06:59:20 +11:00
2025-10-30 17:45:30 +11:00
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
)
2025-10-30 06:59:20 +11:00
2025-10-29 13:11:51 +11:00
with TabPane ( " Editor " ) :
2025-10-30 17:45:30 +11:00
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 " )
2025-10-29 21:06:34 +11:00
with TabPane ( " Plugins " ) :
2025-10-30 17:45:30 +11:00
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 = " thick " )
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