mirror of
https://git.srhtnin.garden/sarah/SaraFem.git
synced 2026-10-13 18:18:11 +02:00
349 lines
14 KiB
Python
349 lines
14 KiB
Python
"""In-app preferences dialog (Features/Major: "Edit preferences in SaraFem").
|
|
|
|
Laid out as a category sidebar + detail panel (Features/Minor: "Make settings
|
|
look good... sidebar with areas, then a much bigger main box"), with the
|
|
left/right/top/bottom-style choices as icon-button pickers instead of text
|
|
dropdowns ("some way to choose layout in a more graphical... way")."""
|
|
|
|
from typing import Callable
|
|
|
|
import flet as ft
|
|
|
|
from Core import AppConfig, AvailableThemes, ChipButtonStyle, Palette
|
|
|
|
# Settings Browser can already hot-apply live (dotfiles, breadcrumbs, preview, tags).
|
|
# The window title bar, sidebar (bookmarks/filetree) layout, theme, and SaraTag's
|
|
# dynamically-loaded module need a restart to take effect.
|
|
RestartRequiredNote = "Window title bar, Theme, Sidebar and SaraTag settings need a restart to take effect."
|
|
|
|
DialogWidth = 700
|
|
DialogHeight = 520
|
|
CategoryListWidth = 150
|
|
|
|
|
|
def _MinMaxRow(Label: str, MinValue: int, MaxValue: int) -> tuple[ft.Row, ft.TextField, ft.TextField]:
|
|
MinField = ft.TextField(value=str(MinValue), label=f"{Label} min", keyboard_type=ft.KeyboardType.NUMBER, expand=True)
|
|
MaxField = ft.TextField(value=str(MaxValue), label=f"{Label} max", keyboard_type=ft.KeyboardType.NUMBER, expand=True)
|
|
return ft.Row([MinField, MaxField]), MinField, MaxField
|
|
|
|
|
|
def _SectionLabel(Text: str) -> ft.Text:
|
|
return ft.Text(Text, color=Palette.Overlay0, size=12)
|
|
|
|
|
|
def _UnselectedPickerStyle() -> ft.ButtonStyle:
|
|
return ft.ButtonStyle(color=Palette.Overlay0, shape=ft.RoundedRectangleBorder(radius=4))
|
|
|
|
|
|
def _OptionPicker(Options: list[tuple[str, str, str]], Initial: str) -> tuple[ft.Control, Callable[[], str]]:
|
|
"""A row of icon buttons standing in for a text dropdown — Options is a
|
|
list of (key, label, Icons-attribute-name). Returns (control, get_value)."""
|
|
State = {"value": Initial}
|
|
Buttons: dict[str, ft.OutlinedButton] = {}
|
|
|
|
def Select(Key: str) -> None:
|
|
State["value"] = Key
|
|
for OptionKey, Button in Buttons.items():
|
|
Button.style = ChipButtonStyle() if OptionKey == Key else _UnselectedPickerStyle()
|
|
Row.update()
|
|
|
|
for Key, Label, IconName in Options:
|
|
Buttons[Key] = ft.OutlinedButton(
|
|
content=Label,
|
|
icon=getattr(ft.Icons, IconName),
|
|
style=ChipButtonStyle() if Key == Initial else _UnselectedPickerStyle(),
|
|
on_click=lambda e, k=Key: Select(k),
|
|
)
|
|
|
|
Row = ft.Row(list(Buttons.values()), wrap=True)
|
|
return Row, lambda: State["value"]
|
|
|
|
|
|
def BuildSettingsDialog(
|
|
Config: AppConfig,
|
|
OnSave: Callable[[], None],
|
|
OnDismiss: Callable[[], None],
|
|
) -> ft.AlertDialog:
|
|
ThemeDropdown = ft.Dropdown(
|
|
value=Config.Theme,
|
|
label="Theme (shared across SaraSuite)",
|
|
options=[ft.DropdownOption(key=Name, text=Name) for Name in AvailableThemes()],
|
|
)
|
|
DotfilesSwitch = ft.Switch(label="Show dotfiles", value=Config.ShowDotfiles)
|
|
TitleBarSwitch = ft.Switch(label="Hide window title bar", value=Config.HideWindowTitleBar)
|
|
BatchRenamePatternField = ft.TextField(
|
|
value=Config.BatchRenamePattern,
|
|
label="Batch rename pattern (~ = name, ! = counter, ^ = extension)",
|
|
)
|
|
DeleteZipSwitch = ft.Switch(
|
|
label="Delete zip after extracting, by default", value=Config.DeleteZipAfterExtract
|
|
)
|
|
ArchiveFormatPicker, GetArchiveFormat = _OptionPicker(
|
|
[("zip", "Zip", "FOLDER_ZIP_OUTLINED"), ("targz", "Tar.gz", "ARCHIVE_OUTLINED")],
|
|
Config.DefaultArchiveFormat,
|
|
)
|
|
|
|
NewDirectoryButtonSwitch = ft.Switch(label="Show New Directory button", value=Config.ShowNewDirectoryButton)
|
|
PasteButtonSwitch = ft.Switch(label="Show Paste button", value=Config.ShowPasteButton)
|
|
ViewToggleButtonSwitch = ft.Switch(label="Show Grid/List button", value=Config.ShowViewToggleButton)
|
|
OptionsButtonSwitch = ft.Switch(label="Show Options button", value=Config.ShowOptionsButton)
|
|
ToolbarSwitch = ft.Switch(label="Show toolbar (breadcrumbs + buttons bar)", value=Config.ShowToolbar)
|
|
BreadcrumbsSwitch = ft.Switch(label="Show breadcrumbs", value=Config.ShowBreadcrumbs)
|
|
BreadcrumbsPositionPicker, GetBreadcrumbsPosition = _OptionPicker(
|
|
[("top", "Top", "NORTH"), ("bottom", "Bottom", "SOUTH")], Config.BreadcrumbsPosition
|
|
)
|
|
|
|
SidebarSwitch = ft.Switch(label="Show sidebar", value=Config.ShowSidebar)
|
|
SidebarSidePicker, GetSidebarSide = _OptionPicker(
|
|
[("left", "Left", "WEST"), ("right", "Right", "EAST")], Config.SidebarSide
|
|
)
|
|
SidebarWidthField = ft.TextField(
|
|
value=str(Config.SidebarWidth), label="Sidebar width", keyboard_type=ft.KeyboardType.NUMBER
|
|
)
|
|
BookmarksSwitch = ft.Switch(label="Show bookmarks section", value=Config.ShowBookmarks)
|
|
BookmarksHeightRow, BookmarksMinHeightField, BookmarksMaxHeightField = _MinMaxRow(
|
|
"Bookmarks height", Config.BookmarksMinHeight, Config.BookmarksMaxHeight
|
|
)
|
|
DevicesSwitch = ft.Switch(label="Show devices section", value=Config.ShowDevices)
|
|
AutoMountSwitch = ft.Switch(label="Mount devices when plugged in", value=Config.AutoMountDevices)
|
|
InternalDrivesSwitch = ft.Switch(label="Also show internal drives", value=Config.ShowInternalDrives)
|
|
DevicesHeightRow, DevicesMinHeightField, DevicesMaxHeightField = _MinMaxRow(
|
|
"Devices height", Config.DevicesMinHeight, Config.DevicesMaxHeight
|
|
)
|
|
FiletreeSwitch = ft.Switch(label="Show filetree section", value=Config.ShowFiletree)
|
|
FiletreeRootField = ft.TextField(value=Config.FiletreeRoot, label="Filetree root", hint_text="/")
|
|
FiletreeHeightRow, FiletreeMinHeightField, FiletreeMaxHeightField = _MinMaxRow(
|
|
"Filetree height", Config.FiletreeMinHeight, Config.FiletreeMaxHeight
|
|
)
|
|
SplitSidebarEvenlySwitch = ft.Switch(
|
|
label="Split Bookmarks/Filetree evenly (overrides the heights above)",
|
|
value=Config.SplitBookmarksFiletreeEvenly,
|
|
)
|
|
|
|
PreviewSwitch = ft.Switch(label="Show preview pane", value=Config.ShowPreview)
|
|
PreviewSidePicker, GetPreviewSide = _OptionPicker(
|
|
[("left", "Left", "WEST"), ("right", "Right", "EAST")], Config.PreviewSide
|
|
)
|
|
PreviewWidthField = ft.TextField(
|
|
value=str(Config.PreviewWidth), label="Preview width", keyboard_type=ft.KeyboardType.NUMBER
|
|
)
|
|
PreviewHeightRow, PreviewMinHeightField, PreviewMaxHeightField = _MinMaxRow(
|
|
"Preview height (only when Tags is under preview)", Config.PreviewMinHeight, Config.PreviewMaxHeight
|
|
)
|
|
SplitEvenlySwitch = ft.Switch(
|
|
label="Split Preview/Tags evenly (overrides the heights above)",
|
|
value=Config.SplitPreviewTagsEvenly,
|
|
)
|
|
|
|
TagsSwitch = ft.Switch(label="Show SaraTag tags", value=Config.ShowTags)
|
|
TagsPositionPicker, GetTagsPosition = _OptionPicker(
|
|
[
|
|
("under_preview", "Under Preview", "VIEW_AGENDA_OUTLINED"),
|
|
("top", "Top", "NORTH"),
|
|
("bottom", "Bottom", "SOUTH"),
|
|
],
|
|
Config.TagsPosition,
|
|
)
|
|
TagsLayoutPicker, GetTagsLayout = _OptionPicker(
|
|
[("flex", "Flex", "VIEW_STREAM_OUTLINED"), ("vertical", "Vertical", "VIEW_COLUMN_OUTLINED")],
|
|
Config.TagsLayout,
|
|
)
|
|
TagMinWidthField = ft.TextField(
|
|
value=str(Config.TagMinWidth), label="Tag min width (0 = none)", keyboard_type=ft.KeyboardType.NUMBER
|
|
)
|
|
TagMaxWidthField = ft.TextField(
|
|
value=str(Config.TagMaxWidth), label="Tag max width (0 = none)", keyboard_type=ft.KeyboardType.NUMBER
|
|
)
|
|
TagPanelHeightRow, TagPanelMinHeightField, TagPanelMaxHeightField = _MinMaxRow(
|
|
"Tags box height", Config.TagPanelMinHeight, Config.TagPanelMaxHeight
|
|
)
|
|
|
|
SaraTagSwitch = ft.Switch(label="Enable SaraTag integration", value=Config.SaraTag.Enabled)
|
|
SaraTagPathField = ft.TextField(
|
|
value=Config.SaraTag.CoreModulePath,
|
|
label="SaraTag .saratag path",
|
|
hint_text="/home/sarah/Applications/SaraTag/.saratag",
|
|
)
|
|
|
|
Categories: dict[str, ft.Control] = {
|
|
"General": ft.Column(
|
|
[
|
|
ThemeDropdown,
|
|
DotfilesSwitch,
|
|
TitleBarSwitch,
|
|
BatchRenamePatternField,
|
|
DeleteZipSwitch,
|
|
_SectionLabel("Default compress format"),
|
|
ArchiveFormatPicker,
|
|
],
|
|
spacing=12,
|
|
),
|
|
"Toolbar": ft.Column(
|
|
[
|
|
ToolbarSwitch,
|
|
BreadcrumbsSwitch,
|
|
_SectionLabel("Position"),
|
|
BreadcrumbsPositionPicker,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
NewDirectoryButtonSwitch,
|
|
PasteButtonSwitch,
|
|
ViewToggleButtonSwitch,
|
|
OptionsButtonSwitch,
|
|
],
|
|
spacing=12,
|
|
),
|
|
"Sidebar": ft.Column(
|
|
[
|
|
SidebarSwitch,
|
|
_SectionLabel("Side"),
|
|
SidebarSidePicker,
|
|
SidebarWidthField,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
BookmarksSwitch,
|
|
BookmarksHeightRow,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
DevicesSwitch,
|
|
AutoMountSwitch,
|
|
InternalDrivesSwitch,
|
|
DevicesHeightRow,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
FiletreeSwitch,
|
|
FiletreeRootField,
|
|
FiletreeHeightRow,
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
SplitSidebarEvenlySwitch,
|
|
],
|
|
spacing=12,
|
|
scroll=ft.ScrollMode.AUTO,
|
|
),
|
|
"Preview": ft.Column(
|
|
[
|
|
PreviewSwitch,
|
|
_SectionLabel("Side"),
|
|
PreviewSidePicker,
|
|
PreviewWidthField,
|
|
PreviewHeightRow,
|
|
],
|
|
spacing=12,
|
|
),
|
|
"Tags": ft.Column(
|
|
[
|
|
TagsSwitch,
|
|
_SectionLabel("Position"),
|
|
TagsPositionPicker,
|
|
_SectionLabel("Layout"),
|
|
TagsLayoutPicker,
|
|
TagMinWidthField,
|
|
TagMaxWidthField,
|
|
TagPanelHeightRow,
|
|
SplitEvenlySwitch,
|
|
],
|
|
spacing=12,
|
|
scroll=ft.ScrollMode.AUTO,
|
|
),
|
|
"SaraTag": ft.Column([SaraTagSwitch, SaraTagPathField], spacing=12),
|
|
}
|
|
|
|
DetailArea = ft.Container(Categories["General"], 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()
|
|
CategoryListView.update()
|
|
|
|
for Name in Categories:
|
|
CategoryTiles[Name] = ft.ListTile(
|
|
title=Name,
|
|
selected=Name == "General",
|
|
selected_tile_color=Palette.Surface1,
|
|
selected_color=Palette.Mauve,
|
|
on_click=lambda e, n=Name: SelectCategory(n),
|
|
)
|
|
|
|
CategoryListView = ft.Column(list(CategoryTiles.values()), spacing=0)
|
|
CategorySidebar = ft.Container(
|
|
CategoryListView, width=CategoryListWidth, bgcolor=Palette.Surface0, border_radius=4, padding=4
|
|
)
|
|
|
|
def DoSave(e=None) -> None:
|
|
Config.Theme = ThemeDropdown.value
|
|
Config.ShowDotfiles = DotfilesSwitch.value
|
|
Config.HideWindowTitleBar = TitleBarSwitch.value
|
|
Config.BatchRenamePattern = BatchRenamePatternField.value.strip() or "~!^"
|
|
Config.DeleteZipAfterExtract = DeleteZipSwitch.value
|
|
Config.DefaultArchiveFormat = GetArchiveFormat()
|
|
|
|
Config.ShowNewDirectoryButton = NewDirectoryButtonSwitch.value
|
|
Config.ShowPasteButton = PasteButtonSwitch.value
|
|
Config.ShowViewToggleButton = ViewToggleButtonSwitch.value
|
|
Config.ShowOptionsButton = OptionsButtonSwitch.value
|
|
Config.ShowToolbar = ToolbarSwitch.value
|
|
Config.ShowBreadcrumbs = BreadcrumbsSwitch.value
|
|
Config.BreadcrumbsPosition = GetBreadcrumbsPosition()
|
|
|
|
Config.ShowSidebar = SidebarSwitch.value
|
|
Config.SidebarSide = GetSidebarSide()
|
|
Config.ShowBookmarks = BookmarksSwitch.value
|
|
Config.ShowDevices = DevicesSwitch.value
|
|
Config.AutoMountDevices = AutoMountSwitch.value
|
|
Config.ShowInternalDrives = InternalDrivesSwitch.value
|
|
Config.ShowFiletree = FiletreeSwitch.value
|
|
Config.SplitBookmarksFiletreeEvenly = SplitSidebarEvenlySwitch.value
|
|
Config.FiletreeRoot = FiletreeRootField.value.strip() or "/"
|
|
|
|
Config.PreviewSide = GetPreviewSide()
|
|
Config.ShowPreview = PreviewSwitch.value
|
|
Config.SplitPreviewTagsEvenly = SplitEvenlySwitch.value
|
|
|
|
Config.TagsPosition = GetTagsPosition()
|
|
Config.TagsLayout = GetTagsLayout()
|
|
Config.ShowTags = TagsSwitch.value
|
|
|
|
Config.SaraTag.Enabled = SaraTagSwitch.value
|
|
Config.SaraTag.CoreModulePath = SaraTagPathField.value.strip()
|
|
|
|
for Field, TargetAttr in (
|
|
(SidebarWidthField, "SidebarWidth"),
|
|
(BookmarksMinHeightField, "BookmarksMinHeight"),
|
|
(BookmarksMaxHeightField, "BookmarksMaxHeight"),
|
|
(DevicesMinHeightField, "DevicesMinHeight"),
|
|
(DevicesMaxHeightField, "DevicesMaxHeight"),
|
|
(FiletreeMinHeightField, "FiletreeMinHeight"),
|
|
(FiletreeMaxHeightField, "FiletreeMaxHeight"),
|
|
(PreviewWidthField, "PreviewWidth"),
|
|
(PreviewMinHeightField, "PreviewMinHeight"),
|
|
(PreviewMaxHeightField, "PreviewMaxHeight"),
|
|
(TagMinWidthField, "TagMinWidth"),
|
|
(TagMaxWidthField, "TagMaxWidth"),
|
|
(TagPanelMinHeightField, "TagPanelMinHeight"),
|
|
(TagPanelMaxHeightField, "TagPanelMaxHeight"),
|
|
):
|
|
try:
|
|
setattr(Config, TargetAttr, int(Field.value))
|
|
except ValueError:
|
|
pass # keep the previous value rather than crash on bad input
|
|
|
|
Config.Save()
|
|
OnSave()
|
|
|
|
return ft.AlertDialog(
|
|
title="Options",
|
|
content=ft.Column(
|
|
[
|
|
ft.Row([CategorySidebar, DetailArea], expand=True),
|
|
ft.Divider(height=1, color=Palette.Surface1),
|
|
ft.Text(RestartRequiredNote, color=Palette.Overlay0, size=12),
|
|
],
|
|
width=DialogWidth,
|
|
height=DialogHeight,
|
|
),
|
|
actions=[
|
|
ft.OutlinedButton(content="Cancel", style=ChipButtonStyle(), on_click=lambda e: OnDismiss()),
|
|
ft.OutlinedButton(content="Save", style=ChipButtonStyle(), on_click=DoSave),
|
|
],
|
|
)
|