mirror of
https://git.srhtnin.garden/sarah/SaraSuite.git
synced 2026-10-13 18:18:12 +02:00
200 lines
7.8 KiB
Python
200 lines
7.8 KiB
Python
"""Settings dialog: the shared SaraSuite settings, SaraSuite's own, and the
|
|
install defaults for each program. Laid out like SaraFem's Settings (a
|
|
category list on the left, the chosen category on the right)."""
|
|
|
|
import subprocess
|
|
from typing import Callable
|
|
|
|
import flet as ft
|
|
|
|
from Core import (
|
|
AppConfig, BundledFontFamily, ChildEnvironment, ChipButtonStyle, LoadSharedSettings, Palette,
|
|
SharedSettings, ThemesDir,
|
|
)
|
|
from Installer import DefaultAnswers, DefaultOptions
|
|
from Manifests import Program
|
|
|
|
DialogWidth = 700
|
|
DialogHeight = 480
|
|
CategoryListWidth = 150
|
|
RestartNote = "Theme, font and title bar changes apply the next time a program starts."
|
|
|
|
|
|
def _Note(Text: str) -> ft.Text:
|
|
return ft.Text(Text, color=Palette.Overlay0, size=12)
|
|
|
|
|
|
def _Themes() -> list[str]:
|
|
Names = {Path.stem for Path in ThemesDir.glob("*.toml")} if ThemesDir.is_dir() else set()
|
|
Names.add("CatppuccinMocha") # built in, even before SaraFem seeds the folder
|
|
return sorted(Names)
|
|
|
|
|
|
def _Fonts() -> list[str]:
|
|
"""Installed font families (fontconfig), with the bundled font first."""
|
|
try:
|
|
Result = subprocess.run(
|
|
["fc-list", ":", "family"], capture_output=True, text=True, timeout=5, env=ChildEnvironment(),
|
|
)
|
|
Families = {Line.split(",")[0].strip() for Line in Result.stdout.splitlines() if Line.strip()}
|
|
except (OSError, subprocess.SubprocessError):
|
|
Families = set()
|
|
Families.discard(BundledFontFamily)
|
|
return [BundledFontFamily] + sorted(Families, key=str.lower)
|
|
|
|
|
|
def ShowSettingsDialog(Page: ft.Page, Config: AppConfig, Programs: list[Program], OnSaved: Callable[[], None]) -> None:
|
|
Themes = _Themes()
|
|
Fonts = _Fonts()
|
|
|
|
def ThemePicker(Value: str) -> ft.Dropdown:
|
|
Names = Themes if Value in Themes else [Value] + Themes
|
|
return ft.Dropdown(value=Value, label="Theme", options=[ft.DropdownOption(key=Name, text=Name) for Name in Names])
|
|
|
|
def FontPicker(Value: str) -> ft.Dropdown:
|
|
# Type to filter the long list.
|
|
Names = Fonts if Value in Fonts else [Value] + Fonts
|
|
return ft.Dropdown(
|
|
value=Value, label="Font", editable=True, enable_filter=True, menu_height=320,
|
|
options=[ft.DropdownOption(key=Name, text=Name) for Name in Names],
|
|
)
|
|
|
|
# Shared: ~/.config/SaraSuite/Config.toml, read by every SaraSuite program.
|
|
Shared = LoadSharedSettings(Config)
|
|
UseSharedSwitch = ft.Switch(label="SaraSuite uses these shared settings", value=Config.UseSharedSettings)
|
|
SharedTheme = ThemePicker(Shared.Theme)
|
|
SharedFont = FontPicker(Shared.FontFamily)
|
|
SharedTitleBar = ft.Switch(label="Hide window title bar", value=Shared.HideWindowTitleBar)
|
|
|
|
# SaraSuite's own appearance, only used with shared settings off.
|
|
OwnTheme = ThemePicker(Config.Theme)
|
|
OwnFont = FontPicker(Config.FontFamily)
|
|
OwnTitleBar = ft.Switch(label="Hide window title bar", value=Config.HideWindowTitleBar)
|
|
OwnAppearance = ft.Column(
|
|
[_Note("SaraSuite's own appearance, used when shared settings are off"), OwnTheme, OwnFont, OwnTitleBar],
|
|
spacing=12,
|
|
disabled=Config.UseSharedSettings,
|
|
opacity=0.5 if Config.UseSharedSettings else 1,
|
|
)
|
|
|
|
def OnUseSharedChanged(e) -> None:
|
|
OwnAppearance.disabled = UseSharedSwitch.value
|
|
OwnAppearance.opacity = 0.5 if UseSharedSwitch.value else 1
|
|
OwnAppearance.update()
|
|
|
|
UseSharedSwitch.on_change = OnUseSharedChanged
|
|
GitHostField = ft.TextField(
|
|
value=Config.GitHost, label="Git host", hint_text="https://git.srhtnin.garden/sarah",
|
|
)
|
|
|
|
Categories: dict[str, ft.Control] = {
|
|
"Shared": ft.Column(
|
|
[
|
|
_Note("Shared by every SaraSuite program (~/.config/SaraSuite/Config.toml)."),
|
|
UseSharedSwitch,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
SharedTheme,
|
|
SharedFont,
|
|
SharedTitleBar,
|
|
],
|
|
spacing=12,
|
|
),
|
|
"SaraSuite": ft.Column(
|
|
[
|
|
GitHostField,
|
|
_Note("Where programs are installed and updated from; each program's repo is <Git host>/<Name>."),
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
OwnAppearance,
|
|
],
|
|
spacing=12,
|
|
scroll=ft.ScrollMode.AUTO,
|
|
),
|
|
}
|
|
|
|
# One tab per program: what its install dialog starts with.
|
|
DefaultBoxes: dict[str, dict[str, ft.Checkbox]] = {}
|
|
DefaultFields: dict[str, dict[str, ft.TextField]] = {}
|
|
for Current in Programs:
|
|
if not Current.Options and not Current.Questions:
|
|
continue
|
|
Answers = DefaultAnswers(Current)
|
|
Ticked = DefaultOptions(Current)
|
|
DefaultFields[Current.Name] = {
|
|
Question.Id: ft.TextField(label=Question.Prompt, value=Answers[Question.Id]) for Question in Current.Questions
|
|
}
|
|
DefaultBoxes[Current.Name] = {
|
|
Option.Id: ft.Checkbox(label=Option.Label, value=Ticked[Option.Id]) for Option in Current.Options
|
|
}
|
|
Categories[Current.Name] = ft.Column(
|
|
[_Note(f"What installing {Current.Name} starts with.")]
|
|
+ list(DefaultFields[Current.Name].values())
|
|
+ list(DefaultBoxes[Current.Name].values()),
|
|
spacing=12,
|
|
scroll=ft.ScrollMode.AUTO,
|
|
)
|
|
|
|
DetailArea = ft.Container(Categories["Shared"], expand=True, padding=ft.Padding.only(left=12))
|
|
CategoryTiles: dict[str, ft.ListTile] = {}
|
|
|
|
def SelectCategory(Name: str) -> None:
|
|
for CategoryName, Tile in CategoryTiles.items():
|
|
Tile.selected = CategoryName == Name
|
|
DetailArea.content = Categories[Name]
|
|
DetailArea.update()
|
|
CategoryList.update()
|
|
|
|
for Name in Categories:
|
|
CategoryTiles[Name] = ft.ListTile(
|
|
title=Name,
|
|
selected=Name == "Shared",
|
|
selected_tile_color=Palette.Surface1,
|
|
selected_color=Palette.Mauve,
|
|
on_click=lambda e, n=Name: SelectCategory(n),
|
|
)
|
|
CategoryList = ft.Column(list(CategoryTiles.values()), spacing=0)
|
|
|
|
def Save(e) -> None:
|
|
Config.UseSharedSettings = UseSharedSwitch.value
|
|
Config.Theme = OwnTheme.value
|
|
Config.FontFamily = OwnFont.value or Config.FontFamily
|
|
Config.HideWindowTitleBar = OwnTitleBar.value
|
|
Config.GitHost = (GitHostField.value or "").strip().rstrip("/") or Config.GitHost
|
|
for Name, Boxes in DefaultBoxes.items():
|
|
Config.OptionDefaults[Name] = {Id: bool(Box.value) for Id, Box in Boxes.items()}
|
|
for Name, Fields in DefaultFields.items():
|
|
if Fields:
|
|
Config.AnswerDefaults[Name] = {Id: (Field.value or "").strip() for Id, Field in Fields.items()}
|
|
Config.Save()
|
|
SharedSettings(
|
|
Theme=SharedTheme.value,
|
|
FontFamily=SharedFont.value or Shared.FontFamily,
|
|
HideWindowTitleBar=SharedTitleBar.value,
|
|
).Save()
|
|
Page.pop_dialog()
|
|
OnSaved()
|
|
|
|
Page.show_dialog(ft.AlertDialog(
|
|
title="Settings",
|
|
content=ft.Column(
|
|
[
|
|
ft.Row(
|
|
[
|
|
ft.Container(
|
|
CategoryList, width=CategoryListWidth, bgcolor=Palette.Surface0, border_radius=4, padding=4,
|
|
),
|
|
DetailArea,
|
|
],
|
|
expand=True,
|
|
),
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
_Note(RestartNote),
|
|
],
|
|
width=DialogWidth,
|
|
height=DialogHeight,
|
|
),
|
|
actions=[
|
|
ft.OutlinedButton(content="Cancel", style=ChipButtonStyle(), on_click=lambda e: Page.pop_dialog()),
|
|
ft.OutlinedButton(content="Save", style=ChipButtonStyle(), on_click=Save),
|
|
],
|
|
))
|