Client: Windows support

This commit is contained in:
sarah committed 2026-10-09 14:25:03 +02:00
1 parent de362bacd0
commit 954bf39c8f
3 files changed
+58 -7

No files matched your search

+54 -6
View File
@@ -44,8 +44,20 @@ AppName = "SaraView"
BundledFontFamily = "Fixedsys Excelsior"
ActiveFontFamily = BundledFontFamily # set by ApplyAppearance, read by BuildTheme
IsWindows = sys.platform == "win32"
# Each system's own per-user folders, like SaraSuite's: ~/.config, ~/.cache
# and ~/.local/share on Linux; %APPDATA% and %LOCALAPPDATA% on Windows.
Home = Path.home()
SharedConfigDir = Home / ".config" / "SaraSuite"
if IsWindows:
ConfigHome = Path(os.environ.get("APPDATA") or Home / "AppData" / "Roaming")
DataHome = Path(os.environ.get("LOCALAPPDATA") or Home / "AppData" / "Local")
CacheHome = DataHome
else:
ConfigHome = Path(os.environ.get("XDG_CONFIG_HOME") or Home / ".config")
DataHome = Path(os.environ.get("XDG_DATA_HOME") or Home / ".local" / "share")
CacheHome = Path(os.environ.get("XDG_CACHE_HOME") or Home / ".cache")
SharedConfigDir = ConfigHome / "SaraSuite"
ThemesDir = SharedConfigDir / "Themes"
SharedConfigPath = SharedConfigDir / "Config.toml"
ConfigDir = SharedConfigDir / AppName
@@ -54,8 +66,7 @@ ConfigPath = ConfigDir / "Config.toml"
# never carries it.
SessionPath = ConfigDir / "Session.toml"
# Server files opened in another program are downloaded here first.
CacheDir = Home / ".cache" / AppName
DataHome = Home / ".local" / "share"
CacheDir = CacheHome / AppName / "Cache" if IsWindows else CacheHome / AppName
# Inside a PyInstaller build, bundled files are unpacked to sys._MEIPASS.
BundleDir = Path(getattr(sys, "_MEIPASS", Path(__file__).resolve().parent))
@@ -307,16 +318,53 @@ def ChildEnvironment() -> dict[str, str]:
Environment.pop("LD_LIBRARY_PATH", None)
for Key in ("PYTHONHOME", "PYTHONPATH"):
Environment.pop(Key, None)
if IsWindows:
Environment["PYTHONUTF8"] = "1" # every Sara program reads and writes UTF-8
return Environment
def OpenExternally(FilePath: Path) -> None:
"""Hands a file to the system's default program. Raises OSError."""
if IsWindows:
os.startfile(str(FilePath))
return
subprocess.Popen(["xdg-open", str(FilePath)], env=ChildEnvironment(), start_new_session=True)
def _WindowsFonts() -> set[str]:
"""Font families from the system and per-user Fonts registry keys
("Arial Bold (TrueType)" -> "Arial")."""
import re
import winreg
Families: set[str] = set()
Key = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts"
for Root in (winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER):
try:
with winreg.OpenKey(Root, Key) as Fonts:
Index = 0
while True:
try:
Name = winreg.EnumValue(Fonts, Index)[0]
except OSError:
break
Index += 1
Name = re.sub(r"\s*\(.*\)$", "", Name)
Name = re.sub(r"\s+(Bold|Italic|Light|Semibold|SemiBold|Black|Regular|Medium|Thin)\b.*$", "", Name)
if Name and "&" not in Name:
Families.add(Name.strip())
except OSError:
continue
return Families
def InstalledFontFamilies() -> list[str]:
"""Font families fontconfig knows about, with the bundled font first."""
"""Font families fontconfig (or, on Windows, the registry) knows about,
with the bundled font first."""
if IsWindows:
Families = _WindowsFonts()
Families.discard(BundledFontFamily)
return [BundledFontFamily] + sorted(Families, key=str.lower)
try:
Result = subprocess.run(
["fc-list", ":", "family"], capture_output=True, text=True, timeout=5, env=ChildEnvironment(),
@@ -489,9 +537,9 @@ def HumanSize(NumBytes: Optional[int]) -> str:
def DisplayPath(PathText: str) -> str:
"""/home/sarah/Notes -> ~/Notes, for labels."""
"""/home/sarah/Notes -> ~/Notes, for labels (C:\\Users\\x\\Notes -> ~\\Notes)."""
HomeText = str(Home)
if PathText == HomeText or PathText.startswith(HomeText + "/"):
if PathText == HomeText or PathText.startswith(HomeText + os.sep):
return "~" + PathText[len(HomeText):]
return PathText
+4 -1
View File
@@ -5,6 +5,7 @@ the right."""
import os
import subprocess
import sys
from typing import Optional
import flet as ft
@@ -22,7 +23,7 @@ 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"):
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"],
@@ -48,6 +49,8 @@ def Main(Page: ft.Page) -> None:
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
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB