mirror of
https://git.srhtnin.garden/sarah/SaraFem.git
synced 2026-10-15 10:23:17 +02:00
Initial commit
This commit is contained in:
commit
4c8d272719
31 files changed
+3870
No files matched your search
+96
@@ -0,0 +1,96 @@
|
||||
"""Preview pane: shows the content of the currently selected file.
|
||||
SaraTag tags live in their own panel now (Tags.py), not here."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import flet as ft
|
||||
|
||||
from Core import AppConfig, Palette
|
||||
from Thumbnails import GetThumbnail, IsThumbnailable
|
||||
|
||||
TextExtensions = {
|
||||
".txt", ".md", ".py", ".json", ".toml", ".yaml", ".yml", ".js", ".ts",
|
||||
".css", ".html", ".htm", ".csv", ".log", ".sh", ".fish", ".cfg", ".ini",
|
||||
".scad", ".xml", ".ipynb",
|
||||
}
|
||||
TextPreviewCharLimit = 4000
|
||||
|
||||
|
||||
def HumanSize(NumBytes: int) -> str:
|
||||
Size = float(NumBytes)
|
||||
for Unit in ("B", "KB", "MB", "GB"):
|
||||
if Size < 1024 or Unit == "GB":
|
||||
return f"{Size:.0f} {Unit}" if Unit == "B" else f"{Size:.1f} {Unit}"
|
||||
Size /= 1024
|
||||
return f"{Size:.1f} TB"
|
||||
|
||||
|
||||
class Preview(ft.Container):
|
||||
def __init__(self, Config: AppConfig, Side: str = "right"):
|
||||
self.Config = Config
|
||||
self.Body = ft.Column(
|
||||
expand=True,
|
||||
alignment=ft.MainAxisAlignment.CENTER,
|
||||
horizontal_alignment=ft.CrossAxisAlignment.CENTER,
|
||||
)
|
||||
BorderSide = ft.BorderSide(1, Palette.Surface1)
|
||||
Border = ft.Border(right=BorderSide) if Side == "left" else ft.Border(left=BorderSide)
|
||||
|
||||
self.Body.controls = self._BuildContent(None)
|
||||
|
||||
super().__init__(
|
||||
content=self.Body,
|
||||
width=Config.PreviewWidth,
|
||||
padding=16,
|
||||
bgcolor=Palette.Mantle,
|
||||
border=Border,
|
||||
)
|
||||
|
||||
def ShowPath(self, EntryPath: Optional[Path]) -> None:
|
||||
self.Body.controls = self._BuildContent(EntryPath)
|
||||
self.update()
|
||||
|
||||
def _BuildContent(self, EntryPath: Optional[Path]) -> list[ft.Control]:
|
||||
if EntryPath is None:
|
||||
return [ft.Text("Nothing selected.", color=Palette.Overlay0)]
|
||||
|
||||
if not EntryPath.exists():
|
||||
return [ft.Text("This no longer exists.", color=Palette.Overlay0)]
|
||||
|
||||
if EntryPath.is_dir():
|
||||
return [
|
||||
ft.Icon(ft.Icons.FOLDER, size=48, color=Palette.Mauve),
|
||||
ft.Text(EntryPath.name, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
|
||||
ft.Text("Directory", color=Palette.Overlay0),
|
||||
]
|
||||
|
||||
if IsThumbnailable(EntryPath):
|
||||
ThumbnailPath = GetThumbnail(EntryPath, Size=(512, 512))
|
||||
if ThumbnailPath is not None:
|
||||
return [
|
||||
ft.Image(src=str(ThumbnailPath), fit=ft.BoxFit.CONTAIN, expand=True),
|
||||
ft.Text(EntryPath.name, text_align=ft.TextAlign.CENTER),
|
||||
]
|
||||
|
||||
if EntryPath.suffix.lower() in TextExtensions:
|
||||
try:
|
||||
Content = EntryPath.read_text(errors="replace")[:TextPreviewCharLimit]
|
||||
except OSError:
|
||||
Content = None
|
||||
if Content is not None:
|
||||
return [
|
||||
ft.Text(EntryPath.name, weight=ft.FontWeight.BOLD),
|
||||
ft.Container(
|
||||
content=ft.Text(Content, selectable=True, size=12),
|
||||
expand=True,
|
||||
alignment=ft.Alignment.TOP_LEFT,
|
||||
),
|
||||
]
|
||||
|
||||
Stat = EntryPath.stat()
|
||||
return [
|
||||
ft.Icon(ft.Icons.INSERT_DRIVE_FILE_OUTLINED, size=48, color=Palette.Overlay0),
|
||||
ft.Text(EntryPath.name, weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER),
|
||||
ft.Text(HumanSize(Stat.st_size), color=Palette.Overlay0),
|
||||
]
|
||||
Reference in new issue
Block a user