363 lines
12 KiB
Go
363 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
mujica "mujicaui"
|
|
)
|
|
|
|
var filesFolders = map[string][]string{
|
|
"/court": {"chronicles/", "decrees/", "seal.go", "notes.md"},
|
|
"/court/chronicles": {"first.md", "second.md"},
|
|
"/court/decrees": {"42.pdf", "crest.png"},
|
|
}
|
|
|
|
func filesEntries(dir string) []mujica.FileEntry {
|
|
var entries []mujica.FileEntry
|
|
for _, n := range filesFolders[dir] {
|
|
name := strings.TrimSuffix(n, "/")
|
|
entries = append(entries, mujica.FileEntry{Path: path.Join(dir, name), Name: name, Dir: name != n, Size: 2048})
|
|
}
|
|
return entries
|
|
}
|
|
|
|
func fileExplorerDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
explorer mujica.FileExplorerState
|
|
disabled, empty bool
|
|
}
|
|
s := ui.Local(c.Root(), "381", func() state { return state{} })
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
mediaCheck(c, log, &s.empty, "Empty folder")
|
|
r := mujica.FileExplorer(c, &s.explorer, mujica.FileExplorerOptions{
|
|
Roots: []string{"/court"}, Disabled: s.disabled,
|
|
Folders: func(dir string) []string {
|
|
var dirs []string
|
|
for _, e := range filesEntries(dir) {
|
|
if e.Dir {
|
|
dirs = append(dirs, e.Path)
|
|
}
|
|
}
|
|
return dirs
|
|
},
|
|
Entries: func(dir string) []mujica.FileEntry {
|
|
if s.empty {
|
|
return nil
|
|
}
|
|
return filesEntries(dir)
|
|
},
|
|
})
|
|
r.Element.Height(320).FillWidth()
|
|
if r.Navigated {
|
|
log("Navigate: " + s.explorer.Current)
|
|
}
|
|
if r.Changed {
|
|
log(fmt.Sprintf("Selected: %v", s.explorer.Selected()))
|
|
}
|
|
if r.Opened != nil {
|
|
log("Open: " + r.Opened.Path)
|
|
}
|
|
}
|
|
|
|
func fileGridDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
grid mujica.FileGridState
|
|
entries []mujica.FileEntry
|
|
disabled, thumbs, empty bool
|
|
}
|
|
s := ui.Local(c.Root(), "382", func() state {
|
|
return state{thumbs: true, entries: append(filesEntries("/court"), filesEntries("/court/decrees")...)}
|
|
})
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
mediaCheck(c, log, &s.thumbs, "Thumbnails")
|
|
mediaCheck(c, log, &s.empty, "Empty")
|
|
entries := s.entries
|
|
if s.empty {
|
|
entries = nil
|
|
}
|
|
r := mujica.FileGrid(c, entries, &s.grid, mujica.FileGridOptions{Disabled: s.disabled, Thumbnail: func(e mujica.FileEntry) bool {
|
|
if !s.thumbs || mujica.FileKindOf(e.Name) != mujica.FileImage {
|
|
return false
|
|
}
|
|
mujica.Image(c, mediaBitmaps[0], mujica.ImageOptions{Alt: e.Name}).Element.Fill()
|
|
return true
|
|
}})
|
|
r.Element.Height(320).FillWidth()
|
|
if r.Changed {
|
|
log(fmt.Sprintf("Selected: %v", s.grid.Selected()))
|
|
}
|
|
if r.Opened != nil {
|
|
log("Open: " + r.Opened.Path)
|
|
}
|
|
}
|
|
|
|
func fileIconDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
name string
|
|
folder, open bool
|
|
size float64
|
|
}
|
|
s := ui.Local(c.Root(), "383", func() state { return state{name: "seal.go", size: 32} })
|
|
if mujica.TextInput(c, &s.name, mujica.TextInputOptions{Label: "File name"}).Changed() {
|
|
log("File name changed")
|
|
}
|
|
mediaCheck(c, log, &s.folder, "Folder")
|
|
if s.folder {
|
|
mediaCheck(c, log, &s.open, "Open folder")
|
|
}
|
|
if mujica.NumberInput(c, &s.size, mujica.NumberInputOptions{Min: 20, Max: 64, Step: 4, Label: "Icon size"}).Changed() {
|
|
log(fmt.Sprintf("Size: %.0f", s.size))
|
|
}
|
|
mujica.FileIcon(c, s.name, mujica.FileIconOptions{Folder: s.folder, Open: s.folder && s.open, Size: float32(s.size)})
|
|
ui.Row(c).Gap(12).Children(func() {
|
|
for _, name := range []string{"seal.go", "notes.md", "crest.png", "deed.pdf", "ledger.zip", "hymn.mp3", "rite.mov", "blob"} {
|
|
mujica.FileIcon(c, name, mujica.FileIconOptions{Size: 32})
|
|
}
|
|
})
|
|
}
|
|
|
|
func filePreviewDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
kind int
|
|
status mujica.DataStatus
|
|
closed bool
|
|
}
|
|
s := ui.Local(c.Root(), "384", func() state { return state{} })
|
|
if mujica.Select(c, &s.kind, []mujica.SelectOption[int]{{Value: 0, Label: "Code"}, {Value: 1, Label: "Text"}, {Value: 2, Label: "Image"}, {Value: 3, Label: "Unsupported"}}, mujica.SelectOptions{Label: "Preview type"}).Changed() {
|
|
log("Preview type changed")
|
|
s.closed = false
|
|
}
|
|
filesStatus(c, log, &s.status)
|
|
if s.closed {
|
|
if mujica.Button(c, "Show preview", mujica.ButtonOptions{}).Clicked() {
|
|
s.closed = false
|
|
log("Preview reopened")
|
|
}
|
|
return
|
|
}
|
|
f := []mujica.PreviewFile{{Name: "seal.go", Text: "package court\n\nfunc Seal() {}"}, {Name: "notes.md", Text: "The seal of the court."}, {Name: "crest.png", Image: mediaBitmaps[0]}, {Name: "deed.pdf", Size: 4096}}[s.kind]
|
|
r := mujica.FilePreview(c, f, mujica.FilePreviewOptions{Status: s.status, Error: "Reading failed"})
|
|
r.Element.Height(280).FillWidth()
|
|
if r.Closed {
|
|
s.closed = true
|
|
log("Preview closed")
|
|
}
|
|
if r.Opened {
|
|
log("Open: " + f.Name)
|
|
}
|
|
if r.Retried {
|
|
s.status = mujica.DataReady
|
|
log("Read retried")
|
|
}
|
|
}
|
|
|
|
func filesStatus(c *ui.Context, log func(string), value *mujica.DataStatus) {
|
|
if mujica.Select(c, value, []mujica.SelectOption[mujica.DataStatus]{{Value: mujica.DataReady, Label: "Ready"}, {Value: mujica.DataLoading, Label: "Loading"}, {Value: mujica.DataFailed, Label: "Failed"}}, mujica.SelectOptions{Label: "Load status"}).Changed() {
|
|
log(fmt.Sprintf("Status: %d", *value))
|
|
}
|
|
}
|
|
|
|
func pathBarDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
path string
|
|
disabled bool
|
|
}
|
|
s := ui.Local(c.Root(), "385", func() state { return state{path: "/court/chronicles/royal/archives/seals"} })
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
if mujica.TextInput(c, &s.path, mujica.TextInputOptions{Label: "Path"}).Changed() {
|
|
log("Path changed")
|
|
}
|
|
if strings.Trim(s.path, "/") == "" {
|
|
ui.Text(c, "Enter a path")
|
|
return
|
|
}
|
|
if r := mujica.PathBar(c, s.path, mujica.PathBarOptions{Disabled: s.disabled}); r.Navigated != "" {
|
|
s.path = r.Navigated
|
|
log("Navigate: " + r.Navigated)
|
|
}
|
|
}
|
|
|
|
func renameInlineDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
name string
|
|
editing, disabled, folder bool
|
|
}
|
|
s := ui.Local(c.Root(), "386", func() state { return state{name: "seal.txt"} })
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
mediaCheck(c, log, &s.folder, "Folder")
|
|
if mujica.Button(c, "Rename", mujica.ButtonOptions{Disabled: s.disabled}).Clicked() {
|
|
s.editing = true
|
|
log("Rename started")
|
|
}
|
|
r := mujica.RenameInline(c, &s.name, &s.editing, mujica.RenameInlineOptions{Disabled: s.disabled, Folder: s.folder, Validate: func(name string) string {
|
|
if strings.ContainsAny(name, "/\\") {
|
|
return "Name contains a path separator"
|
|
}
|
|
return ""
|
|
}})
|
|
if r.Renamed {
|
|
log("Renamed: " + s.name)
|
|
}
|
|
if r.Canceled {
|
|
log("Rename canceled")
|
|
}
|
|
}
|
|
|
|
func fileOperationProgressDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
op mujica.FileOperation
|
|
disabled, conflict bool
|
|
canceled bool
|
|
}
|
|
s := ui.Local(c.Root(), "387", func() state {
|
|
return state{op: mujica.FileOperation{Kind: mujica.FileCopy, Total: 10, Current: "seal.go"}}
|
|
})
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
mediaCheck(c, log, &s.conflict, "Conflict")
|
|
if mujica.Select(c, &s.op.Kind, []mujica.SelectOption[mujica.FileOperationKind]{{Value: mujica.FileCopy, Label: "Copy"}, {Value: mujica.FileMove, Label: "Move"}, {Value: mujica.FileDelete, Label: "Delete"}}, mujica.SelectOptions{Label: "Operation"}).Changed() {
|
|
log("Operation changed")
|
|
}
|
|
if mujica.Button(c, "Advance", mujica.ButtonOptions{Disabled: s.op.Done == s.op.Total}).Clicked() {
|
|
s.op.Done++
|
|
log(fmt.Sprintf("Done: %d", s.op.Done))
|
|
}
|
|
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
|
s.op.Done = 0
|
|
s.canceled = false
|
|
log("Operation reset")
|
|
}
|
|
op := s.op
|
|
if s.conflict {
|
|
op.Conflict = "seal.go"
|
|
}
|
|
r := mujica.FileOperationProgress(c, op, mujica.FileOperationOptions{Disabled: s.disabled || s.canceled})
|
|
if r.Canceled {
|
|
s.canceled = true
|
|
log("Operation canceled")
|
|
}
|
|
if r.Resolved != mujica.ConflictNone {
|
|
s.conflict = false
|
|
log(fmt.Sprintf("Conflict: %d; apply all: %v", r.Resolved, r.ApplyAll))
|
|
}
|
|
}
|
|
|
|
func filesTransfers() []mujica.QueuedTransfer {
|
|
return []mujica.QueuedTransfer{
|
|
{ID: "a", Name: "ledger.zip", Upload: true, Status: mujica.TransferActive, Done: 30, Total: 120},
|
|
{ID: "b", Name: "crest.png", Status: mujica.TransferPaused, Done: 1, Total: 4},
|
|
{ID: "c", Name: "deed.pdf", Upload: true, Status: mujica.TransferFailed, Total: 2, Error: "Network lost"},
|
|
}
|
|
}
|
|
|
|
func transferQueueDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
items []mujica.QueuedTransfer
|
|
queue mujica.TransferQueueState
|
|
disabled bool
|
|
revision uint64
|
|
}
|
|
s := ui.Local(c.Root(), "388", func() state { return state{items: filesTransfers()} })
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
|
s.items = filesTransfers()
|
|
log("Queue reset")
|
|
}
|
|
r := mujica.TransferQueue(c, s.items, &s.queue, mujica.TransferQueueOptions{Disabled: s.disabled, Revision: s.revision})
|
|
r.Element.Height(260).FillWidth()
|
|
if r.Action == mujica.TransferNone {
|
|
return
|
|
}
|
|
for i := range s.items {
|
|
if s.items[i].ID == r.ID {
|
|
switch r.Action {
|
|
case mujica.TransferPause:
|
|
s.items[i].Status = mujica.TransferPaused
|
|
case mujica.TransferResume, mujica.TransferRetry:
|
|
s.items[i].Status = mujica.TransferActive
|
|
case mujica.TransferRemove:
|
|
s.items = append(s.items[:i:i], s.items[i+1:]...)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
s.revision++
|
|
log(fmt.Sprintf("Transfer %s: %d", r.ID, r.Action))
|
|
}
|
|
|
|
func storageUsageDemo(c *ui.Context, log func(string)) {
|
|
type state struct{ photos float64 }
|
|
s := ui.Local(c.Root(), "389", func() state { return state{photos: 40} })
|
|
if mujica.NumberInput(c, &s.photos, mujica.NumberInputOptions{Min: 0, Max: 92, Step: 1, Label: "Photos (GB)"}).Changed() {
|
|
log(fmt.Sprintf("Photos: %.0f GB", s.photos))
|
|
}
|
|
mujica.StorageUsage(c, []mujica.StorageSegment{{Label: "Documents", Bytes: 24 << 30}, {Label: "Photos", Bytes: int64(s.photos) * (1 << 30)}, {Label: "Apps", Bytes: 12 << 30}}, mujica.StorageUsageOptions{Capacity: 128 << 30, Label: "Court disk"}).FillWidth()
|
|
}
|
|
|
|
func filesRecent() []mujica.RecentFile {
|
|
now := time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)
|
|
return []mujica.RecentFile{{Path: "/court/seal.go", Opened: now}, {Path: "/court/notes.md", Opened: now.AddDate(0, 0, -1)}, {Path: "/court/crest.png", Opened: now.AddDate(0, 0, -4)}, {Path: "/court/charter.pdf", Opened: now.AddDate(0, -1, 0)}}
|
|
}
|
|
|
|
func recentFilesDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
files []mujica.RecentFile
|
|
recent mujica.RecentFilesState
|
|
disabled bool
|
|
}
|
|
s := ui.Local(c.Root(), "390", func() state { return state{files: filesRecent()} })
|
|
mediaCheck(c, log, &s.disabled, "Disabled")
|
|
if mujica.Button(c, "Reset", mujica.ButtonOptions{}).Clicked() {
|
|
s.files = filesRecent()
|
|
log("Recent files reset")
|
|
}
|
|
r := mujica.RecentFiles(c, s.files, &s.recent, mujica.RecentFilesOptions{Disabled: s.disabled, Now: time.Date(2026, 10, 5, 15, 0, 0, 0, time.UTC)})
|
|
r.Element.Height(320).FillWidth()
|
|
if r.Opened != nil {
|
|
log("Open: " + r.Opened.Path)
|
|
}
|
|
if r.Cleared {
|
|
s.files = nil
|
|
log("Recent files cleared")
|
|
}
|
|
if r.Removed != "" {
|
|
for i, f := range s.files {
|
|
if f.Path == r.Removed {
|
|
s.files = append(s.files[:i:i], s.files[i+1:]...)
|
|
break
|
|
}
|
|
}
|
|
log("Removed: " + r.Removed)
|
|
}
|
|
}
|
|
|
|
func archiveViewerDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
archive mujica.ArchiveViewerState
|
|
entries []mujica.ArchiveEntry
|
|
status mujica.DataStatus
|
|
empty bool
|
|
}
|
|
s := ui.Local(c.Root(), "391", func() state {
|
|
return state{entries: []mujica.ArchiveEntry{{Path: "court/seal.go", Size: 4000, Packed: 1000}, {Path: "court/decrees/42.pdf", Size: 6000, Packed: 5000}, {Path: "README.md", Size: 1000, Packed: 400}}}
|
|
})
|
|
filesStatus(c, log, &s.status)
|
|
mediaCheck(c, log, &s.empty, "Empty")
|
|
entries := s.entries
|
|
if s.empty {
|
|
entries = nil
|
|
}
|
|
r := mujica.ArchiveViewer(c, entries, &s.archive, mujica.ArchiveViewerOptions{Status: s.status, Error: "Invalid archive"})
|
|
r.Element.Height(280).FillWidth()
|
|
if r.Opened != nil {
|
|
log("Open archive entry: " + r.Opened.Path)
|
|
}
|
|
if r.Retried {
|
|
s.status = mujica.DataReady
|
|
log("Archive read retried")
|
|
}
|
|
}
|