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" BundledFontFamily = "Fixedsys Excelsior"
ActiveFontFamily = BundledFontFamily # set by ApplyAppearance, read by BuildTheme 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() 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" ThemesDir = SharedConfigDir / "Themes"
SharedConfigPath = SharedConfigDir / "Config.toml" SharedConfigPath = SharedConfigDir / "Config.toml"
ConfigDir = SharedConfigDir / AppName ConfigDir = SharedConfigDir / AppName
@@ -54,8 +66,7 @@ ConfigPath = ConfigDir / "Config.toml"
# never carries it. # never carries it.
SessionPath = ConfigDir / "Session.toml" SessionPath = ConfigDir / "Session.toml"
# Server files opened in another program are downloaded here first. # Server files opened in another program are downloaded here first.
CacheDir = Home / ".cache" / AppName CacheDir = CacheHome / AppName / "Cache" if IsWindows else CacheHome / AppName
DataHome = Home / ".local" / "share"
# Inside a PyInstaller build, bundled files are unpacked to sys._MEIPASS. # Inside a PyInstaller build, bundled files are unpacked to sys._MEIPASS.
BundleDir = Path(getattr(sys, "_MEIPASS", Path(__file__).resolve().parent)) BundleDir = Path(getattr(sys, "_MEIPASS", Path(__file__).resolve().parent))
@@ -307,16 +318,53 @@ def ChildEnvironment() -> dict[str, str]:
Environment.pop("LD_LIBRARY_PATH", None) Environment.pop("LD_LIBRARY_PATH", None)
for Key in ("PYTHONHOME", "PYTHONPATH"): for Key in ("PYTHONHOME", "PYTHONPATH"):
Environment.pop(Key, None) Environment.pop(Key, None)
if IsWindows:
Environment["PYTHONUTF8"] = "1" # every Sara program reads and writes UTF-8
return Environment return Environment
def OpenExternally(FilePath: Path) -> None: def OpenExternally(FilePath: Path) -> None:
"""Hands a file to the system's default program. Raises OSError.""" """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) 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]: 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: try:
Result = subprocess.run( Result = subprocess.run(
["fc-list", ":", "family"], capture_output=True, text=True, timeout=5, env=ChildEnvironment(), ["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: 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) 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[len(HomeText):]
return PathText return PathText
+4 -1
View File
@@ -5,6 +5,7 @@ the right."""
import os import os
import subprocess import subprocess
import sys
from typing import Optional from typing import Optional
import flet as ft import flet as ft
@@ -22,7 +23,7 @@ from Settings import ShowSettingsDialog
def _SetCursorThemeFallback() -> None: def _SetCursorThemeFallback() -> None:
"""GTK reads XCURSOR_THEME directly and doesn't always inherit it (shows as """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.""" '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 return
try: try:
Result = subprocess.run(["gsettings", "get", "org.gnome.desktop.interface", "cursor-theme"], 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.bgcolor = Palette.Base
Page.window.width = 1180 Page.window.width = 1180
Page.window.height = 760 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_width = 720
Page.window.min_height = 480 Page.window.min_height = 480
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB