Files
SaraView/Client/Main.py
T
2026-10-13 17:10:21 +02:00

114 lines
4.2 KiB
Python

"""SaraView's desktop client: browse and edit files in a local root directory,
or on a SaraView server (the one on SpineS) through its API. Laid out like
SaraFem: a directory tree on the left, the file list (or an open file) on
the right."""
import os
import subprocess
import sys
from typing import Optional
import flet as ft
from App import App
from Browser import Browser
from Core import (
AppName, ApplyAppearance, AssetsDir, BuildTheme, BundledFontFamily, EffectiveSettings, LoadConfig, Palette,
ResizeHandle,
)
from Filetree import Filetree
from Settings import ShowSettingsDialog
def _SetCursorThemeFallback() -> None:
"""GTK reads XCURSOR_THEME directly and doesn't always inherit it (shows as
'Unable to load ... from the cursor theme'). Same fix as SaraFem's."""
if os.environ.get("XCURSOR_THEME") or sys.platform == "win32": # GTK/GNOME only
return
try:
Result = subprocess.run(["gsettings", "get", "org.gnome.desktop.interface", "cursor-theme"],
capture_output=True, text=True, timeout=2)
Theme = Result.stdout.strip().strip("'")
except (OSError, subprocess.SubprocessError):
Theme = ""
os.environ["XCURSOR_THEME"] = Theme or "Adwaita"
def Main(Page: ft.Page) -> None:
Config = LoadConfig()
Appearance = EffectiveSettings(Config)
ApplyAppearance(Appearance) # before anything reads Palette.X
Page.title = AppName
Page.theme_mode = ft.ThemeMode.DARK
Page.theme = BuildTheme()
Page.dark_theme = BuildTheme()
Page.fonts = {BundledFontFamily: "fonts/Fixedsys_Excelsior_Regular.ttf"}
Page.window.title_bar_hidden = Appearance.HideWindowTitleBar
Page.padding = 0
Page.bgcolor = Palette.Base
Page.window.width = 1180
Page.window.height = 760
if sys.platform == "win32":
Page.window.icon = str(AssetsDir / "Icon.ico") # taskbar/title bar (Linux: the desktop entry)
Page.window.min_width = 720
Page.window.min_height = 480
TheApp = App(Page, Config)
def OnPathChanged(Root: Optional[str], RelativePath: str, Reload: bool) -> None:
if Reload: # directories may have moved, so the tree starts over
Tree.Reload()
Tree.RevealPath(Root, RelativePath)
def ShowSettings(Category: str = "General") -> None:
ShowSettingsDialog(TheApp, OnSettingsSaved, Category)
def OnSettingsSaved(Reconnect: bool) -> None:
Tree.visible = Divider.visible = Config.ShowSidebar
Tree.SetEntryButtons(TheBrowser.EntryButtons, Config.NewEntryButtonsAtTop, Update=False)
if Reconnect:
TheApp.Connect() # its listeners reload the tree and the browser
else:
Body.update()
Tree.Reload() # "Show files in the tree" may have changed
TheBrowser.Refresh()
Tree.RevealPath(TheBrowser.Root, TheBrowser.Path)
def ToggleSidebar() -> None:
Config.ShowSidebar = not Config.ShowSidebar
Config.Save()
Tree.visible = Divider.visible = Config.ShowSidebar
Body.update()
def OnDrag(Delta: float) -> None:
Tree.width = max(140, (Tree.width or Config.SidebarWidth) + Delta)
Tree.update()
def OnDragEnd() -> None:
Config.SidebarWidth = int(Tree.width)
Config.Save()
TheBrowser = Browser(TheApp, OnPathChanged,
lambda Category=None: ShowSettings(Category or ("Connection" if TheApp.ConnectError else "General")),
ToggleSidebar)
Tree = Filetree(TheApp, TheBrowser.NavigateTo, TheBrowser.OpenFile)
Tree.SetEntryButtons(TheBrowser.EntryButtons, Config.NewEntryButtonsAtTop, Update=False)
Divider = ResizeHandle(OnDrag, OnDragEnd)
Tree.visible = Divider.visible = Config.ShowSidebar
# STRETCH so the divider gets the full height to be dragged along.
Body = ft.Row([Tree, Divider, TheBrowser], expand=True, spacing=0, vertical_alignment=ft.CrossAxisAlignment.STRETCH)
Page.add(Body)
def OnSourceChanged() -> None:
Tree.Reload()
TheBrowser.SourceChanged()
TheApp.Listeners.append(OnSourceChanged)
TheApp.Connect()
if __name__ == "__main__":
_SetCursorThemeFallback()
ft.run(Main, assets_dir=str(AssetsDir))