Files
8bitMyGoUI/gallery/demos_storage.go
T
ZacharyZhang-NY 552b073e81 Add files components 406-420; Tree opens to a known choice
Fifteen file components: FileExplorer, FileGrid, FileIcon, PathBar,
MemoryCard, DiskUsage, UploadQueue, Trash, RecentFiles, FilePreview,
ArchiveViewer, FileDetails, SharePanel, SyncStatus and QuotaMeter.

FileIcon draws six kinds as hand-placed 12x12 pages and a folder. The
browsers build on data.Tree, List, GridView and Table; ArchiveViewer
owns its sort and keeps the caller's index on its entry. UploadQueue
keys rows by name so focus stays on its row. SharePanel never logs the
link. SyncStatus turns its arrows on Busy steps, so syncing moves under
Reduce Motion.

data.Tree now opens the closed branches above a chosen path it knows,
so a caller's starting path survives the first frame.
2026-10-12 09:27:26 -04:00

125 lines
4.6 KiB
Go

package gallery
import (
"time"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/files"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/input"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
var storageEntries = []Entry{
{414, "RecentFiles", "最近文件", "files", "What you touched lately, and how long ago.", demoRecentFiles},
{415, "FilePreview", "文件预览", "files", "Pictures shrink to fit, text scrolls, and the rest says so.", demoFilePreview},
{416, "ArchiveViewer", "压缩包", "files", "What is inside the zip, and how much squeezing saved. Headers sort.", demoArchiveViewer},
{417, "FileDetails", "文件详情", "files", "Name, kind, size to the byte, and when it all happened.", demoFileDetails},
{418, "SharePanel", "分享面板", "files", "A link to send and how much it lets them do. COPY takes it.", demoSharePanel},
{419, "SyncStatus", "同步状态", "files", "All saved, saving (the arrows turn), or something went wrong.", demoSyncStatus},
{420, "QuotaMeter", "配额", "files", "Storage as gems, like hearts in an old adventure. Low turns gold, then red.", demoQuotaMeter},
}
type storageDemo struct {
recent, entry, kind int
access files.Access
}
func storageState(c *ui.Context) *storageDemo {
return ui.Local(c.Root(), demoKey("storage"), func() storageDemo { return storageDemo{recent: -1, entry: -1} })
}
func demoRecentFiles(c *ui.Context, log func(string)) {
s := storageState(c)
now := pixel.K(c).Now()
recent := []files.File{
{Name: "SAVE1.SAV", Kind: files.Archive, Modified: now.Add(-20 * time.Second)},
{Name: "CHEATS.TXT", Kind: files.Text, Modified: now.Add(-12 * time.Minute)},
{Name: "MAP.PNG", Kind: files.Image, Modified: now.Add(-5 * time.Hour)},
{Name: "THEME.MID", Kind: files.Audio, Modified: now.Add(-30 * time.Hour)},
{Name: "MAIN.GO", Kind: files.Code, Modified: now.Add(-9 * 24 * time.Hour)},
}
r := files.RecentFiles(c, "Recent", recent, &s.recent, files.RecentFilesOptions{})
if r.Changed {
log("chose " + recent[s.recent].Name)
}
if r.Opened {
log("open " + recent[s.recent].Name)
}
}
const cheats = "UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT B A START.\n\nWorks on the title screen. Thirty lives, or so the playground said. Hold SELECT for the level select, but only on Tuesdays."
func demoFilePreview(c *ui.Context, log func(string)) {
k := pixel.K(c)
s := storageState(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
if input.Segmented(c, "File", []string{"CHEATS.TXT", "SUNSET.PNG", "THEME.MID"}, &s.kind, input.SegmentedOptions{}).Changed {
log([]string{"text", "picture", "no preview"}[s.kind])
}
switch s.kind {
case 0:
files.FilePreview(c, "CHEATS.TXT", files.Preview{Text: cheats}, files.FilePreviewOptions{H: 80})
case 1:
files.FilePreview(c, "SUNSET.PNG", files.Preview{Picture: sunset}, files.FilePreviewOptions{H: 80})
default:
files.FilePreview(c, "THEME.MID", files.Preview{}, files.FilePreviewOptions{H: 80})
}
})
}
func demoArchiveViewer(c *ui.Context, log func(string)) {
s := storageState(c)
entries := []files.Entry{
{Path: "LEVELS/1-1.MAP", Size: 18_432, Packed: 3_120},
{Path: "LEVELS/1-2.MAP", Size: 20_480, Packed: 3_604},
{Path: "MUSIC/THEME.MID", Size: 21_504, Packed: 19_800},
{Path: "SPRITES.PNG", Size: 65_536, Packed: 64_900},
{Path: "README.TXT", Size: 2_048, Packed: 900},
}
r := files.ArchiveViewer(c, "LOOT.ZIP", entries, &s.entry, files.ArchiveViewerOptions{Height: 80})
if r.Changed {
log("chose " + entries[s.entry].Path)
}
if r.Sorted {
log("sorted")
}
}
func demoFileDetails(c *ui.Context, log func(string)) {
files.FileDetails(c, files.File{
Name: "BOSS FIGHT.PNG", Kind: files.Image, Size: 1_234_567,
Created: time.Date(1998, 11, 21, 9, 30, 0, 0, time.UTC),
Modified: time.Date(2026, 4, 1, 23, 59, 0, 0, time.UTC),
}, files.FileDetailsOptions{})
}
func demoSharePanel(c *ui.Context, log func(string)) {
s := storageState(c)
r := files.SharePanel(c, "Share", "https://8bit.example/s/UUDDLRLRBA", &s.access, files.SharePanelOptions{})
if r.Copied {
log("link copied")
}
if r.Changed {
log([]string{"view", "comment", "edit"}[s.access])
}
}
func demoSyncStatus(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Column(c).Gap(k.D(6)).Children(func() {
for _, st := range []files.SyncState{files.Synced, files.Syncing, files.SyncFailed} {
files.SyncStatus(c, st, files.SyncStatusOptions{})
}
})
}
func demoQuotaMeter(c *ui.Context, log func(string)) {
k := pixel.K(c)
ui.Column(c).Gap(k.D(8)).Children(func() {
for _, used := range []int64{3 << 30, 13 << 30, 16 << 30} {
files.QuotaMeter(c, "Storage", used, 15<<30, files.QuotaMeterOptions{})
}
})
}