mirror of
https://git.srhtnin.garden/sarah/SaraFem.git
synced 2026-10-13 18:18:11 +02:00
Windows support
This commit is contained in:
1 parent
6b2b35f5da
commit
b295db3fb6
9 files changed
+136
-26
No files matched your search
+7
-4
@@ -5,10 +5,13 @@ from pathlib import Path
|
|||||||
|
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
from Core import BookmarksPath, ConfigDir
|
from Core import BookmarksPath, ConfigDir, IsWindows
|
||||||
|
|
||||||
DefaultIcon = "BOOKMARK_OUTLINE"
|
DefaultIcon = "BOOKMARK_OUTLINE"
|
||||||
TrashPath = Path.home() / ".local" / "share" / "Trash" / "files"
|
# The freedesktop trash folder (Linux). Windows' Recycle Bin isn't a folder
|
||||||
|
# that can be browsed, so there's no Trash bookmark there (deleting still goes
|
||||||
|
# to the Recycle Bin, through send2trash).
|
||||||
|
TrashPath = None if IsWindows else Path.home() / ".local" / "share" / "Trash" / "files"
|
||||||
|
|
||||||
# One-time marker: without it, re-adding the Trash bookmark to old files (see
|
# One-time marker: without it, re-adding the Trash bookmark to old files (see
|
||||||
# LoadBookmarks) would run on every load and undo the user removing it.
|
# LoadBookmarks) would run on every load and undo the user removing it.
|
||||||
@@ -41,7 +44,7 @@ def DefaultBookmarks() -> list[Bookmark]:
|
|||||||
Bookmark("Documents", Path.home() / "Documents", "DESCRIPTION_OUTLINED"),
|
Bookmark("Documents", Path.home() / "Documents", "DESCRIPTION_OUTLINED"),
|
||||||
Bookmark("Downloads", Path.home() / "Downloads", "DOWNLOAD_OUTLINED"),
|
Bookmark("Downloads", Path.home() / "Downloads", "DOWNLOAD_OUTLINED"),
|
||||||
Bookmark("Pictures", Path.home() / "Pictures", "IMAGE_OUTLINED"),
|
Bookmark("Pictures", Path.home() / "Pictures", "IMAGE_OUTLINED"),
|
||||||
Bookmark("Trash", TrashPath, "DELETE_OUTLINE"),
|
*([Bookmark("Trash", TrashPath, "DELETE_OUTLINE")] if TrashPath else []),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -74,7 +77,7 @@ def LoadBookmarks() -> list[Bookmark]:
|
|||||||
# Trash used to be a hardcoded Sidebar entry, not a real bookmark (so it
|
# Trash used to be a hardcoded Sidebar entry, not a real bookmark (so it
|
||||||
# couldn't be edited). Migrate existing bookmark files that predate that —
|
# couldn't be edited). Migrate existing bookmark files that predate that —
|
||||||
# but only once, so deliberately removing Trash afterwards actually sticks.
|
# but only once, so deliberately removing Trash afterwards actually sticks.
|
||||||
if not _TrashMigrationMarker.exists():
|
if TrashPath is not None and not _TrashMigrationMarker.exists():
|
||||||
if not any(B.Path == TrashPath for B in Bookmarks):
|
if not any(B.Path == TrashPath for B in Bookmarks):
|
||||||
Bookmarks.append(Bookmark("Trash", TrashPath, "DELETE_OUTLINE"))
|
Bookmarks.append(Bookmark("Trash", TrashPath, "DELETE_OUTLINE"))
|
||||||
SaveBookmarks(Bookmarks)
|
SaveBookmarks(Bookmarks)
|
||||||
|
|||||||
+14
-4
@@ -5,7 +5,7 @@ import asyncio
|
|||||||
import ctypes
|
import ctypes
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import os
|
import os
|
||||||
import pwd
|
import sys
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -108,8 +108,8 @@ def _EntryModified(Entry: Path) -> str:
|
|||||||
|
|
||||||
def _EntryOwner(Entry: Path) -> str:
|
def _EntryOwner(Entry: Path) -> str:
|
||||||
try:
|
try:
|
||||||
return pwd.getpwuid(Entry.stat().st_uid).pw_name
|
return Entry.owner() # works on Windows too (Python 3.13+), unlike pwd
|
||||||
except (OSError, KeyError):
|
except (OSError, KeyError, NotImplementedError):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@@ -150,6 +150,13 @@ except OSError:
|
|||||||
|
|
||||||
|
|
||||||
def _EntryCreated(Entry: Path) -> str:
|
def _EntryCreated(Entry: Path) -> str:
|
||||||
|
# Windows (and macOS) record it in plain stat() as st_birthtime.
|
||||||
|
try:
|
||||||
|
BirthTime = getattr(Entry.stat(), "st_birthtime", None)
|
||||||
|
except OSError:
|
||||||
|
BirthTime = None
|
||||||
|
if BirthTime:
|
||||||
|
return datetime.fromtimestamp(BirthTime).strftime("%d/%m/%y %H:%M")
|
||||||
if _Libc is None:
|
if _Libc is None:
|
||||||
return ""
|
return ""
|
||||||
Buf = _StatxStruct()
|
Buf = _StatxStruct()
|
||||||
@@ -915,7 +922,10 @@ class Browser(ft.Row):
|
|||||||
self._OpenInEditor(EntryPath)
|
self._OpenInEditor(EntryPath)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
subprocess.Popen(["xdg-open", str(EntryPath)])
|
if sys.platform == "win32":
|
||||||
|
os.startfile(str(EntryPath)) # the default app
|
||||||
|
else:
|
||||||
|
subprocess.Popen(["xdg-open", str(EntryPath)])
|
||||||
except OSError as Error:
|
except OSError as Error:
|
||||||
self._Notify(f"Couldn't open {EntryPath.name}: {Error}")
|
self._Notify(f"Couldn't open {EntryPath.name}: {Error}")
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ SaraView/Backend/DefaultThemes/CatppuccinMocha.toml and Frontend/src/Styles/Them
|
|||||||
one accent colour (Purple/Mauve) used everywhere, not a mauve+lavender mix.
|
one accent colour (Purple/Mauve) used everywhere, not a mauve+lavender mix.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
from dataclasses import asdict, dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
@@ -45,7 +47,19 @@ AppName = "SaraFem"
|
|||||||
BundledFontFamily = "Fixedsys Excelsior"
|
BundledFontFamily = "Fixedsys Excelsior"
|
||||||
ActiveFontFamily = BundledFontFamily # set by ApplyAppearance, read by BuildTheme
|
ActiveFontFamily = BundledFontFamily # set by ApplyAppearance, read by BuildTheme
|
||||||
|
|
||||||
CacheDir = Path.home() / ".cache" / "SaraFem"
|
IsWindows = sys.platform == "win32"
|
||||||
|
|
||||||
|
# Each system's own per-user folders, like SaraSuite's: ~/.config and ~/.cache
|
||||||
|
# on Linux; %APPDATA% (settings) and %LOCALAPPDATA% (caches) on Windows.
|
||||||
|
if IsWindows:
|
||||||
|
ConfigHome = Path(os.environ.get("APPDATA") or Path.home() / "AppData" / "Roaming")
|
||||||
|
CacheDir = Path(os.environ.get("LOCALAPPDATA") or Path.home() / "AppData" / "Local") / "SaraFem" / "Cache"
|
||||||
|
# Where the file tree starts by default: the system drive (C:\), not "/".
|
||||||
|
DefaultFiletreeRoot = (os.environ.get("SystemDrive") or "C:") + "\\"
|
||||||
|
else:
|
||||||
|
ConfigHome = Path(os.environ.get("XDG_CONFIG_HOME") or Path.home() / ".config")
|
||||||
|
CacheDir = Path(os.environ.get("XDG_CACHE_HOME") or Path.home() / ".cache") / "SaraFem"
|
||||||
|
DefaultFiletreeRoot = "/"
|
||||||
ThumbnailCacheDir = CacheDir / "Thumbnails"
|
ThumbnailCacheDir = CacheDir / "Thumbnails"
|
||||||
|
|
||||||
# Copy/Cut/Paste uses the real OS clipboard (Clipboard.set_files/get_files —
|
# Copy/Cut/Paste uses the real OS clipboard (Clipboard.set_files/get_files —
|
||||||
@@ -72,7 +86,7 @@ def GetClipboardMode() -> str:
|
|||||||
|
|
||||||
# Config moved from ~/.config/SaraFem/ into a SaraSuite-wide layout so other
|
# Config moved from ~/.config/SaraFem/ into a SaraSuite-wide layout so other
|
||||||
# Sara* programs (SaraTag, SaraView) can eventually read from the same place.
|
# Sara* programs (SaraTag, SaraView) can eventually read from the same place.
|
||||||
SharedConfigDir = Path.home() / ".config" / "SaraSuite"
|
SharedConfigDir = ConfigHome / "SaraSuite"
|
||||||
ThemesDir = SharedConfigDir / "Themes"
|
ThemesDir = SharedConfigDir / "Themes"
|
||||||
ConfigDir = SharedConfigDir / "SaraFem"
|
ConfigDir = SharedConfigDir / "SaraFem"
|
||||||
ConfigPath = ConfigDir / "Config.toml"
|
ConfigPath = ConfigDir / "Config.toml"
|
||||||
@@ -328,7 +342,7 @@ class AppConfig:
|
|||||||
# always showing it with a "No devices." placeholder.
|
# always showing it with a "No devices." placeholder.
|
||||||
AutoHideEmptyDevices: bool = False
|
AutoHideEmptyDevices: bool = False
|
||||||
ShowFiletree: bool = True
|
ShowFiletree: bool = True
|
||||||
FiletreeRoot: str = "/"
|
FiletreeRoot: str = DefaultFiletreeRoot
|
||||||
BookmarksMinHeight: int = 0 # 0 = no constraint
|
BookmarksMinHeight: int = 0 # 0 = no constraint
|
||||||
BookmarksMaxHeight: int = 0
|
BookmarksMaxHeight: int = 0
|
||||||
DevicesMinHeight: int = 0
|
DevicesMinHeight: int = 0
|
||||||
@@ -552,9 +566,41 @@ def ApplyAppearance(Settings: SharedSettings) -> None:
|
|||||||
ActiveFontFamily = Settings.FontFamily or BundledFontFamily
|
ActiveFontFamily = Settings.FontFamily or BundledFontFamily
|
||||||
|
|
||||||
|
|
||||||
|
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, for the font picker. The
|
"""Font families fontconfig (or, on Windows, the registry) knows about,
|
||||||
bundled font is always first, since it isn't installed system-wide."""
|
for the font picker. The bundled font is always first, since it isn't
|
||||||
|
installed system-wide."""
|
||||||
|
if IsWindows:
|
||||||
|
Families = _WindowsFonts()
|
||||||
|
Families.discard(BundledFontFamily)
|
||||||
|
return [BundledFontFamily] + sorted(Families, key=str.lower)
|
||||||
try:
|
try:
|
||||||
Result = subprocess.run(["fc-list", ":", "family"], capture_output=True, text=True, timeout=5)
|
Result = subprocess.run(["fc-list", ":", "family"], capture_output=True, text=True, timeout=5)
|
||||||
Families = {Line.split(",")[0].strip() for Line in Result.stdout.splitlines() if Line.strip()}
|
Families = {Line.split(",")[0].strip() for Line in Result.stdout.splitlines() if Line.strip()}
|
||||||
|
|||||||
+51
-4
@@ -17,15 +17,23 @@ Falls back to polling /run/media/<user> if udisks2 isn't reachable.
|
|||||||
Everything runs on the page's own event loop (Page.run_task), so calling
|
Everything runs on the page's own event loop (Page.run_task), so calling
|
||||||
.update() from here is safe."""
|
.update() from here is safe."""
|
||||||
|
|
||||||
|
from __future__ import annotations # the D-Bus type hints below don't exist on Windows
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import getpass
|
import getpass
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Optional
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
from dbus_next import BusType, Message, MessageType, Variant
|
|
||||||
from dbus_next.aio import MessageBus
|
IsWindows = sys.platform == "win32"
|
||||||
|
if not IsWindows:
|
||||||
|
# udisks2 is Linux-only; Windows lists its drives itself (ListWindowsDrives).
|
||||||
|
from dbus_next import BusType, Message, MessageType, Variant
|
||||||
|
from dbus_next.aio import MessageBus
|
||||||
|
|
||||||
from Core import AppConfig, NotificationStyle, Palette
|
from Core import AppConfig, NotificationStyle, Palette
|
||||||
|
|
||||||
@@ -35,7 +43,7 @@ BlockInterface = f"{UDisks}.Block"
|
|||||||
FilesystemInterface = f"{UDisks}.Filesystem"
|
FilesystemInterface = f"{UDisks}.Filesystem"
|
||||||
DriveInterface = f"{UDisks}.Drive"
|
DriveInterface = f"{UDisks}.Drive"
|
||||||
|
|
||||||
PollInterval = 3.0 # seconds, only for the no-udisks2 fallback
|
PollInterval = 3.0 # seconds, for Windows and the no-udisks2 fallback
|
||||||
MountRoots = [Path("/run/media") / getpass.getuser(), Path("/media") / getpass.getuser()]
|
MountRoots = [Path("/run/media") / getpass.getuser(), Path("/media") / getpass.getuser()]
|
||||||
|
|
||||||
|
|
||||||
@@ -71,6 +79,39 @@ def _UserMount(MountPoint: Path) -> bool:
|
|||||||
return any(Root in MountPoint.parents for Root in MountRoots)
|
return any(Root in MountPoint.parents for Root in MountRoots)
|
||||||
|
|
||||||
|
|
||||||
|
def ListWindowsDrives(ShowInternal: bool, ShowSystemDrive: bool) -> list[Device]:
|
||||||
|
"""Windows: removable drives (USB sticks, memory cards, discs with a disc
|
||||||
|
in), plus fixed and network drives with ShowInternal, and the system drive
|
||||||
|
(C:) with ShowSystemDrive. Windows mounts drives itself, so there's nothing
|
||||||
|
to mount; ejecting is left to Windows' own "Safely remove"."""
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
Kernel = ctypes.windll.kernel32
|
||||||
|
Removable, Fixed, Remote, CdRom = 2, 3, 4, 5
|
||||||
|
SystemDrive = (os.environ.get("SystemDrive") or "C:").upper().rstrip("\\") + "\\"
|
||||||
|
Devices: list[Device] = []
|
||||||
|
Mask = Kernel.GetLogicalDrives()
|
||||||
|
for Index in range(26):
|
||||||
|
if not Mask & (1 << Index):
|
||||||
|
continue
|
||||||
|
Root = f"{chr(65 + Index)}:\\"
|
||||||
|
Kind = Kernel.GetDriveTypeW(Root)
|
||||||
|
IsSystem = Root.upper() == SystemDrive
|
||||||
|
if IsSystem and not ShowSystemDrive:
|
||||||
|
continue
|
||||||
|
if Kind in (Fixed, Remote) and not (ShowInternal or IsSystem):
|
||||||
|
continue
|
||||||
|
if Kind not in (Removable, Fixed, Remote, CdRom):
|
||||||
|
continue
|
||||||
|
Name = ctypes.create_unicode_buffer(261)
|
||||||
|
# Fails for an empty card reader or disc drive: nothing to show then.
|
||||||
|
if not Kernel.GetVolumeInformationW(Root, Name, 261, None, None, None, None, 0):
|
||||||
|
continue
|
||||||
|
Fallback = {Removable: "Removable drive", CdRom: "Disc", Remote: "Network drive"}.get(Kind, "Local disk")
|
||||||
|
Devices.append(Device(Root, f"{Name.value or Fallback} ({Root[:2]})", Path(Root), IsSystemMount=IsSystem))
|
||||||
|
return Devices
|
||||||
|
|
||||||
|
|
||||||
def ListFallbackDevices() -> list[Device]:
|
def ListFallbackDevices() -> list[Device]:
|
||||||
Devices: list[Device] = []
|
Devices: list[Device] = []
|
||||||
for Root in MountRoots:
|
for Root in MountRoots:
|
||||||
@@ -210,6 +251,9 @@ class DevicesPanel(ft.Container):
|
|||||||
self.Client = None
|
self.Client = None
|
||||||
|
|
||||||
async def _Start(self) -> None:
|
async def _Start(self) -> None:
|
||||||
|
if IsWindows:
|
||||||
|
await self._PollLoop() # no udisks2: Windows' own drive list, checked every few seconds
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
self.Client = await UDisksClient.Connect()
|
self.Client = await UDisksClient.Connect()
|
||||||
await self.Client.Watch(self._ScheduleRefresh)
|
await self.Client.Watch(self._ScheduleRefresh)
|
||||||
@@ -221,7 +265,10 @@ class DevicesPanel(ft.Container):
|
|||||||
|
|
||||||
async def _PollLoop(self) -> None:
|
async def _PollLoop(self) -> None:
|
||||||
while not self.Unmounted:
|
while not self.Unmounted:
|
||||||
self._Show(ListFallbackDevices())
|
if IsWindows:
|
||||||
|
self._Show(ListWindowsDrives(self.Config.ShowInternalDrives, self.Config.ShowSystemDrive))
|
||||||
|
else:
|
||||||
|
self._Show(ListFallbackDevices())
|
||||||
await asyncio.sleep(PollInterval)
|
await asyncio.sleep(PollInterval)
|
||||||
|
|
||||||
def _ScheduleRefresh(self) -> None:
|
def _ScheduleRefresh(self) -> None:
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,7 @@ from typing import Callable, Optional
|
|||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from Core import AppConfig, DragGroup, MoveIntoDirectory, NotificationStyle, Palette
|
from Core import AppConfig, DefaultFiletreeRoot, DragGroup, MoveIntoDirectory, NotificationStyle, Palette
|
||||||
|
|
||||||
|
|
||||||
class _FiletreeNode:
|
class _FiletreeNode:
|
||||||
@@ -119,7 +119,7 @@ class Filetree(ft.Container):
|
|||||||
):
|
):
|
||||||
self.OnNavigate = OnNavigate
|
self.OnNavigate = OnNavigate
|
||||||
self.Config = Config
|
self.Config = Config
|
||||||
self.RootPath = RootPath or Path("/")
|
self.RootPath = RootPath or Path(DefaultFiletreeRoot)
|
||||||
self.OnFilesMoved = OnFilesMoved
|
self.OnFilesMoved = OnFilesMoved
|
||||||
self.SelectedNode: Optional[_FiletreeNode] = None
|
self.SelectedNode: Optional[_FiletreeNode] = None
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ from Sidebar import Sidebar
|
|||||||
def _SetCursorThemeFallback() -> None:
|
def _SetCursorThemeFallback() -> None:
|
||||||
"""GTK's cursor lookup reads XCURSOR_THEME directly; it isn't always inherited
|
"""GTK's cursor lookup reads XCURSOR_THEME directly; it isn't always inherited
|
||||||
from the desktop session (shows as 'Unable to load ... from the cursor theme').
|
from the desktop session (shows as 'Unable to load ... from the cursor theme').
|
||||||
Fill it in from gsettings if missing, so the warning has nowhere to come from."""
|
Fill it in from gsettings if missing, so the warning has nowhere to come from.
|
||||||
if os.environ.get("XCURSOR_THEME"):
|
GTK/GNOME only: nothing to do on Windows."""
|
||||||
|
if os.environ.get("XCURSOR_THEME") or sys.platform == "win32":
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
Result = subprocess.run(
|
Result = subprocess.run(
|
||||||
@@ -233,6 +234,9 @@ def Main(Page: ft.Page) -> None:
|
|||||||
Page.padding = 0
|
Page.padding = 0
|
||||||
Page.window.width = 1100
|
Page.window.width = 1100
|
||||||
Page.window.height = 700
|
Page.window.height = 700
|
||||||
|
if sys.platform == "win32":
|
||||||
|
# The taskbar/title bar icon (on Linux it comes from the desktop entry).
|
||||||
|
Page.window.icon = str(Path(__file__).resolve().parent / "assets" / "Icon.ico")
|
||||||
Page.window.title_bar_hidden = Appearance.HideWindowTitleBar
|
Page.window.title_bar_hidden = Appearance.HideWindowTitleBar
|
||||||
|
|
||||||
PickerArgs = _ParsePickerArgs()
|
PickerArgs = _ParsePickerArgs()
|
||||||
@@ -246,7 +250,7 @@ def Main(Page: ft.Page) -> None:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if "--portal-service" in sys.argv:
|
if "--portal-service" in sys.argv and sys.platform != "win32": # Linux's file-picker portal only
|
||||||
# No Flet page at all here — the D-Bus FileChooser backend is a
|
# No Flet page at all here — the D-Bus FileChooser backend is a
|
||||||
# plain asyncio service (see PortalService.py) that
|
# plain asyncio service (see PortalService.py) that
|
||||||
# spawns *this same executable* again with --pick-* flags for the
|
# spawns *this same executable* again with --pick-* flags for the
|
||||||
|
|||||||
+3
-3
@@ -11,7 +11,7 @@ from typing import Callable
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from Core import (
|
from Core import (
|
||||||
AppConfig, AvailableThemes, ChipButtonStyle, FolderIconBand, InstalledFontFamilies, LoadSharedSettings, Palette,
|
AppConfig, AvailableThemes, ChipButtonStyle, DefaultFiletreeRoot, FolderIconBand, InstalledFontFamilies, LoadSharedSettings, Palette,
|
||||||
SharedSettings,
|
SharedSettings,
|
||||||
)
|
)
|
||||||
from FolderIcons import FolderIconChoices, FolderIconLabelOverrides
|
from FolderIcons import FolderIconChoices, FolderIconLabelOverrides
|
||||||
@@ -294,7 +294,7 @@ def BuildSettingsDialog(
|
|||||||
"Devices height", Config.DevicesMinHeight, Config.DevicesMaxHeight
|
"Devices height", Config.DevicesMinHeight, Config.DevicesMaxHeight
|
||||||
)
|
)
|
||||||
FiletreeSwitch = ft.Switch(label="Show filetree section", value=Config.ShowFiletree)
|
FiletreeSwitch = ft.Switch(label="Show filetree section", value=Config.ShowFiletree)
|
||||||
FiletreeRootField = ft.TextField(value=Config.FiletreeRoot, label="Filetree root", hint_text="/")
|
FiletreeRootField = ft.TextField(value=Config.FiletreeRoot, label="Filetree root", hint_text=DefaultFiletreeRoot)
|
||||||
FiletreeHeightRow, FiletreeMinHeightField, FiletreeMaxHeightField = _MinMaxRow(
|
FiletreeHeightRow, FiletreeMinHeightField, FiletreeMaxHeightField = _MinMaxRow(
|
||||||
"Filetree height", Config.FiletreeMinHeight, Config.FiletreeMaxHeight
|
"Filetree height", Config.FiletreeMinHeight, Config.FiletreeMaxHeight
|
||||||
)
|
)
|
||||||
@@ -630,7 +630,7 @@ def BuildSettingsDialog(
|
|||||||
Config.AutoHideEmptyDevices = AutoHideEmptyDevicesSwitch.value
|
Config.AutoHideEmptyDevices = AutoHideEmptyDevicesSwitch.value
|
||||||
Config.ShowFiletree = FiletreeSwitch.value
|
Config.ShowFiletree = FiletreeSwitch.value
|
||||||
Config.SplitBookmarksFiletreeEvenly = SplitSidebarEvenlySwitch.value
|
Config.SplitBookmarksFiletreeEvenly = SplitSidebarEvenlySwitch.value
|
||||||
Config.FiletreeRoot = FiletreeRootField.value.strip() or "/"
|
Config.FiletreeRoot = FiletreeRootField.value.strip() or DefaultFiletreeRoot
|
||||||
|
|
||||||
Config.PreviewSide = GetPreviewSide()
|
Config.PreviewSide = GetPreviewSide()
|
||||||
Config.ShowPreview = PreviewSwitch.value
|
Config.ShowPreview = PreviewSwitch.value
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
+1
-1
@@ -3,4 +3,4 @@ flet-desktop>=1.0,<2.0 # the desktop client; otherwise Flet fetches it on first
|
|||||||
pillow
|
pillow
|
||||||
send2trash
|
send2trash
|
||||||
toml
|
toml
|
||||||
dbus-next
|
dbus-next; sys_platform == "linux" # udisks2 and the file-picker portal (Linux only)
|
||||||
Reference in new issue
Block a user