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