mirror of
https://git.srhtnin.garden/sarah/SaraFem.git
synced 2026-10-14 18:18:12 +02:00
List view metadata columns and drag-to-resize everywhere
- New optional list-view columns: Description, Size (now includes directories), Modified, Owner, Created (via statx birth time), Content (item/line count or image dimensions), Access (rwx) - Drag-to-resize: list view column headers, sidebar sections (Bookmarks/ Devices/Filetree), Preview/Tags split, and the sidebar/preview side panels themselves — all commit to Config on release, not per drag frame - Column resizing mutates existing row cells live instead of rebuilding, with metadata cached per (file, mtime) — fixes lag in large folders - Fix list view background click (rows were full ListTile width with no empty space to click); bookmark selected-background now covers the triple-dot menu too; batch rename/new-file dialogs pre-select their default text instead of just pre-filling it - Toolbar scrolls instead of clipping when buttons don't fit; toolbar and breadcrumbs are independently toggleable - Add a "Server" bookmark icon Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
a5f5c5ca79
commit
31b6c554e6
6 files changed
+699
-89
No files matched your search
+67
-30
@@ -9,15 +9,15 @@ from typing import Callable, Optional
|
||||
|
||||
import flet as ft
|
||||
|
||||
from Bookmarks import IconChoices, LoadBookmarks, SaveBookmarks
|
||||
from Bookmarks import IconChoices, IconLabelOverrides, LoadBookmarks, SaveBookmarks
|
||||
from Core import (
|
||||
AppConfig,
|
||||
ApplyHeightBounds,
|
||||
ChipButtonStyle,
|
||||
DragGroup,
|
||||
MoveIntoDirectory,
|
||||
NotificationStyle,
|
||||
Palette,
|
||||
ResizeHandle,
|
||||
)
|
||||
from Devices import DevicesPanel
|
||||
from Filetree import Filetree
|
||||
@@ -40,6 +40,11 @@ class Sidebar(ft.Container):
|
||||
|
||||
Sections: list[ft.Control] = []
|
||||
SplitEvenly = Config.SplitBookmarksFiletreeEvenly and Config.ShowBookmarks and Config.ShowFiletree
|
||||
# Section boxes a resize-drag can mutate .height on directly — only
|
||||
# built (and only get a drag handle between them) outside SplitEvenly,
|
||||
# which is a 50/50 flex split with no single fixed height to drag.
|
||||
BookmarksBox: Optional[ft.Container] = None
|
||||
DevicesBox: Optional[ft.Container] = None
|
||||
|
||||
if Config.ShowBookmarks:
|
||||
self.BookmarksListView = ft.ReorderableListView(
|
||||
@@ -52,21 +57,28 @@ class Sidebar(ft.Container):
|
||||
self.BookmarksListView.expand = 1
|
||||
Sections.append(self.BookmarksListView)
|
||||
else:
|
||||
Sections.append(
|
||||
ApplyHeightBounds(self.BookmarksListView, Config.BookmarksMinHeight, Config.BookmarksMaxHeight)
|
||||
BookmarksBox = ft.Container(
|
||||
content=self.BookmarksListView,
|
||||
height=Config.BookmarksMaxHeight or Config.BookmarksMinHeight or None,
|
||||
)
|
||||
Sections.append(BookmarksBox)
|
||||
|
||||
if Config.ShowBookmarks and (Config.ShowDevices or Config.ShowFiletree):
|
||||
Sections.append(ft.Divider(height=1, color=Palette.Surface1))
|
||||
if BookmarksBox is not None:
|
||||
Sections.append(self._BuildSectionDivider(BookmarksBox, "BookmarksMaxHeight"))
|
||||
else:
|
||||
Sections.append(ft.Divider(height=1, color=Palette.Surface1))
|
||||
|
||||
if Config.ShowDevices:
|
||||
self.DevicesView = DevicesPanel(OnNavigate=OnNavigate, Config=Config)
|
||||
Sections.append(
|
||||
ApplyHeightBounds(self.DevicesView, Config.DevicesMinHeight, Config.DevicesMaxHeight)
|
||||
DevicesBox = ft.Container(
|
||||
content=self.DevicesView,
|
||||
height=Config.DevicesMaxHeight or Config.DevicesMinHeight or None,
|
||||
)
|
||||
Sections.append(DevicesBox)
|
||||
|
||||
if Config.ShowDevices and Config.ShowFiletree:
|
||||
Sections.append(ft.Divider(height=1, color=Palette.Surface1))
|
||||
Sections.append(self._BuildSectionDivider(DevicesBox, "DevicesMaxHeight"))
|
||||
|
||||
if Config.ShowFiletree:
|
||||
self.FiletreeView = Filetree(
|
||||
@@ -77,11 +89,9 @@ class Sidebar(ft.Container):
|
||||
)
|
||||
if SplitEvenly:
|
||||
self.FiletreeView.expand = 1
|
||||
Sections.append(self.FiletreeView)
|
||||
else:
|
||||
Sections.append(
|
||||
ApplyHeightBounds(self.FiletreeView, Config.FiletreeMinHeight, Config.FiletreeMaxHeight)
|
||||
)
|
||||
self.FiletreeView.height = Config.FiletreeMaxHeight or Config.FiletreeMinHeight or None
|
||||
Sections.append(self.FiletreeView)
|
||||
|
||||
BorderSide = ft.BorderSide(1, Palette.Surface1)
|
||||
Border = ft.Border(left=BorderSide) if Config.SidebarSide == "right" else ft.Border(right=BorderSide)
|
||||
@@ -94,6 +104,25 @@ class Sidebar(ft.Container):
|
||||
border=Border,
|
||||
)
|
||||
|
||||
DefaultSectionHeight = 150 # starting point for a drag when a section had no explicit height yet
|
||||
|
||||
def _BuildSectionDivider(self, Box: ft.Container, MaxHeightAttr: str) -> ft.Control:
|
||||
"""A resize handle for the section above it (Box) — drags resize that
|
||||
section, whatever's below just reflows into the space that leaves,
|
||||
same "resize what's above the divider" convention as the file list's
|
||||
column dividers resize the column to their left."""
|
||||
|
||||
def OnDrag(Delta: float) -> None:
|
||||
Current = Box.height or self.DefaultSectionHeight
|
||||
Box.height = max(40, Current + Delta)
|
||||
Box.update()
|
||||
|
||||
def OnDragEnd() -> None:
|
||||
setattr(self.Config, MaxHeightAttr, int(Box.height))
|
||||
self.Config.Save()
|
||||
|
||||
return ResizeHandle("vertical", self.Config.SidebarWidth, OnDrag, OnDragEnd)
|
||||
|
||||
def _BuildBookmarkTiles(self) -> list[ft.Control]:
|
||||
self.LocationTiles = {}
|
||||
return [self._BuildBookmarkTile(B) for B in LoadBookmarks()]
|
||||
@@ -117,6 +146,10 @@ class Sidebar(ft.Container):
|
||||
# Not a ListTile: the drag-to-reorder handle needs to wrap everything
|
||||
# except the triple-dot menu, which ListTile's fixed leading/title/
|
||||
# trailing layout doesn't let us carve out — so this is a plain Row.
|
||||
# The selected background lives on OuterContainer (wrapping the whole
|
||||
# Row, triple-dot included) rather than just RowContent — it used to
|
||||
# stop short of the triple-dot, leaving its area unstyled ("I think
|
||||
# the background of the button should continue to the menu").
|
||||
NameLabel = ft.Text(Bookmark.Name, size=14, color=Palette.Text)
|
||||
RowContent = ft.Container(
|
||||
content=ft.Row(
|
||||
@@ -127,34 +160,35 @@ class Sidebar(ft.Container):
|
||||
spacing=12,
|
||||
),
|
||||
padding=ft.Padding.symmetric(horizontal=16, vertical=10),
|
||||
border_radius=4,
|
||||
ink=True,
|
||||
on_click=lambda e, p=Bookmark.Path: self.OnNavigate(p),
|
||||
expand=True,
|
||||
)
|
||||
|
||||
MenuButton = ft.PopupMenuButton(
|
||||
icon=ft.Icons.MORE_VERT,
|
||||
items=[
|
||||
ft.PopupMenuItem(content="Edit", on_click=lambda e, b=Bookmark: self._ShowEditBookmarkDialog(b)),
|
||||
],
|
||||
)
|
||||
|
||||
OuterContainer = ft.Container(
|
||||
content=ft.Row(
|
||||
[ft.Container(ft.ReorderableDragHandle(content=RowContent), expand=True), MenuButton],
|
||||
spacing=0,
|
||||
),
|
||||
border_radius=4,
|
||||
)
|
||||
|
||||
def SetSelected(IsSelected: bool) -> None:
|
||||
RowContent.bgcolor = Palette.Surface1 if IsSelected else None
|
||||
OuterContainer.bgcolor = Palette.Surface1 if IsSelected else None
|
||||
NameLabel.color = Palette.Mauve if IsSelected else Palette.Text
|
||||
|
||||
self.LocationTiles[Bookmark.Path] = SetSelected
|
||||
|
||||
Row = ft.Row(
|
||||
[
|
||||
ft.Container(ft.ReorderableDragHandle(content=RowContent), expand=True),
|
||||
ft.PopupMenuButton(
|
||||
icon=ft.Icons.MORE_VERT,
|
||||
items=[
|
||||
ft.PopupMenuItem(
|
||||
content="Edit", on_click=lambda e, b=Bookmark: self._ShowEditBookmarkDialog(b)
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
spacing=0,
|
||||
)
|
||||
return ft.DragTarget(
|
||||
group=DragGroup,
|
||||
content=Row,
|
||||
content=OuterContainer,
|
||||
on_accept=lambda e, dest=Bookmark.Path: self._OnEntryDropped(e, dest),
|
||||
)
|
||||
|
||||
@@ -178,7 +212,10 @@ class Sidebar(ft.Container):
|
||||
IconDropdown = ft.Dropdown(
|
||||
value=Bookmark.Icon,
|
||||
label="Icon",
|
||||
options=[ft.DropdownOption(key=Name, text=Name.split("_")[0].title()) for Name in IconChoices],
|
||||
options=[
|
||||
ft.DropdownOption(key=Name, text=IconLabelOverrides.get(Name, Name.split("_")[0].title()))
|
||||
for Name in IconChoices
|
||||
],
|
||||
)
|
||||
|
||||
def DoSave(e=None) -> None:
|
||||
|
||||
Reference in new issue
Block a user