303 lines
9.5 KiB
Go
303 lines
9.5 KiB
Go
package main
|
|
|
|
import (
|
|
"cmp"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
mujica "mujicaui"
|
|
)
|
|
|
|
var dataOffices = []string{"Matins", "Lauds", "Prime", "Terce", "Sext", "None", "Vespers", "Compline"}
|
|
|
|
func dataListDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
big, grid, multiple bool
|
|
status, query string
|
|
list mujica.ListState[int]
|
|
}
|
|
s := ui.Local(c.Root(), "070", func() state { return state{status: "Ready"} })
|
|
n := 24
|
|
if s.big {
|
|
n = 100_000
|
|
}
|
|
label := func(i int) string { return fmt.Sprintf("%s %d", dataOffices[i%len(dataOffices)], i+1) }
|
|
st, show := dataStatus(s.status)
|
|
if !show {
|
|
n = 0
|
|
}
|
|
dataSection(c, "List", func() {
|
|
dataControls(c, func() {
|
|
ui.Checkbox(c, &s.big, "100,000 items")
|
|
ui.Checkbox(c, &s.grid, "Grid")
|
|
ui.Checkbox(c, &s.multiple, "Multiple")
|
|
ui.Select(c, &s.status, dataStatuses)
|
|
})
|
|
dataControls(c, func() {
|
|
ui.TextInput(c, &s.query).Width(200).Placeholder("Find by label")
|
|
if mujica.Button(c, "Find", mujica.ButtonOptions{Variant: mujica.Secondary}).Clicked() {
|
|
for i := range n {
|
|
if strings.Contains(strings.ToLower(label(i)), strings.ToLower(s.query)) {
|
|
s.list.ScrollTo(i)
|
|
log(fmt.Sprintf("found %q at %d", s.query, i))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
})
|
|
v := mujica.List(c, &s.list, n, mujica.ListOptions[int]{
|
|
Key: func(i int) int { return i }, Label: label, Grid: s.grid, Multiple: s.multiple,
|
|
ItemWidth: 140, ItemHeight: 72, Status: st,
|
|
}, func(i int) {
|
|
ui.Text(c, label(i)).SingleLine()
|
|
if s.grid {
|
|
ui.Text(c, "Office").TextColor(mujica.Tokens(c).TextMuted)
|
|
}
|
|
})
|
|
v.Size(480, 320)
|
|
if v.Changed() {
|
|
k, _ := s.list.Selected()
|
|
log(fmt.Sprintf("selected %d, %d chosen", k, s.list.Selection.Len()))
|
|
}
|
|
if v.Submitted() {
|
|
k, _ := s.list.Selected()
|
|
log(fmt.Sprintf("submitted %d", k))
|
|
}
|
|
if v.Retried() {
|
|
log("retry")
|
|
}
|
|
})
|
|
}
|
|
|
|
type dataRelic struct {
|
|
ID int
|
|
Name string
|
|
Office string
|
|
Year int
|
|
Sealed bool
|
|
}
|
|
|
|
func dataRelics(n int) []dataRelic {
|
|
rows := make([]dataRelic, n)
|
|
for i := range rows {
|
|
rows[i] = dataRelic{ID: i + 1, Name: fmt.Sprintf("Relic %05d", (i*7919)%n+1), Office: dataOffices[i%len(dataOffices)],
|
|
Year: 1200 + (i*37)%600, Sealed: i%3 == 0}
|
|
}
|
|
return rows
|
|
}
|
|
|
|
func dataTableDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
big, multiple bool
|
|
status string
|
|
small, large []dataRelic
|
|
table mujica.DataTableState[int]
|
|
}
|
|
s := ui.Local(c.Root(), "071", func() state { return state{status: "Ready", small: dataRelics(60)} })
|
|
rows := s.small
|
|
if s.big {
|
|
if s.large == nil {
|
|
s.large = dataRelics(100_000)
|
|
}
|
|
rows = s.large
|
|
}
|
|
st, show := dataStatus(s.status)
|
|
if !show {
|
|
rows = nil
|
|
}
|
|
k := mujica.Tokens(c)
|
|
cols := []mujica.DataColumn[dataRelic]{
|
|
{ID: "id", Title: "ID", Width: 80, Align: ui.End, Sortable: true, Text: func(r dataRelic) string { return strconv.Itoa(r.ID) },
|
|
Compare: func(a, b dataRelic) int { return cmp.Compare(a.ID, b.ID) }},
|
|
{ID: "name", Title: "Name", MinWidth: 120, Sortable: true, Text: func(r dataRelic) string { return r.Name },
|
|
Compare: func(a, b dataRelic) int { return cmp.Compare(a.Name, b.Name) }},
|
|
{ID: "office", Title: "Office", Width: 110, Sortable: true, Text: func(r dataRelic) string { return r.Office },
|
|
Compare: func(a, b dataRelic) int { return cmp.Compare(a.Office, b.Office) }},
|
|
{ID: "year", Title: "Year", Width: 80, Align: ui.End, Sortable: true, Text: func(r dataRelic) string { return strconv.Itoa(r.Year) },
|
|
Compare: func(a, b dataRelic) int { return cmp.Compare(a.Year, b.Year) }},
|
|
{ID: "seal", Title: "Seal", Width: 100, Text: func(r dataRelic) string { return map[bool]string{true: "Sealed", false: "Open"}[r.Sealed] },
|
|
Cell: func(r dataRelic) {
|
|
color := k.Success
|
|
if r.Sealed {
|
|
color = k.Warning
|
|
}
|
|
ui.Text(c, map[bool]string{true: "◆ Sealed", false: "◇ Open"}[r.Sealed]).TextColor(color).SingleLine()
|
|
}},
|
|
}
|
|
dataSection(c, "DataTable", func() {
|
|
dataControls(c, func() {
|
|
ui.Checkbox(c, &s.big, "100,000 rows")
|
|
ui.Checkbox(c, &s.multiple, "Multiple")
|
|
ui.Select(c, &s.status, dataStatuses)
|
|
ui.TextInput(c, &s.table.Filter).Width(180).Placeholder("Filter")
|
|
})
|
|
v := mujica.DataTable(c, &s.table, rows, mujica.DataTableOptions[dataRelic, int]{
|
|
Columns: cols, Key: func(r dataRelic) int { return r.ID }, Multiple: s.multiple, Status: st,
|
|
})
|
|
v.Size(620, 360)
|
|
ui.Text(c, fmt.Sprintf("%d of %d rows shown", s.table.Rows(), len(rows))).TextColor(k.TextMuted)
|
|
if v.SortChanged() {
|
|
log(fmt.Sprintf("sort %s descending=%v", s.table.Sort.Column, s.table.Sort.Descending))
|
|
}
|
|
if v.Changed() {
|
|
id, _ := s.table.Selected()
|
|
log(fmt.Sprintf("selected row %d, %d chosen", id, s.table.Selection.Len()))
|
|
}
|
|
if v.Submitted() {
|
|
id, _ := s.table.Selected()
|
|
log(fmt.Sprintf("opened row %d", id))
|
|
}
|
|
if v.Retried() {
|
|
log("retry")
|
|
}
|
|
})
|
|
}
|
|
|
|
// dataLazy simulates children that arrive 600 ms after they are asked for.
|
|
type dataLazy struct {
|
|
kids map[string][]string
|
|
status map[string]mujica.DataStatus
|
|
due map[string]time.Time
|
|
failed map[string]bool
|
|
}
|
|
|
|
func newDataLazy() *dataLazy {
|
|
return &dataLazy{
|
|
kids: map[string][]string{"Nave": {"Pulpit", "Pews", "Font"}, "Crypt": {}, "Archive": {},
|
|
"Pulpit": nil, "Pews": nil, "Font": nil, "Sacristy": nil},
|
|
status: map[string]mujica.DataStatus{"Crypt": mujica.DataUnloaded, "Archive": mujica.DataUnloaded},
|
|
due: map[string]time.Time{}, failed: map[string]bool{},
|
|
}
|
|
}
|
|
|
|
func (d *dataLazy) load(c *ui.Context, log func(string)) func(string) {
|
|
return func(n string) {
|
|
d.status[n] = mujica.DataLoading
|
|
d.due[n] = c.Now().Add(600 * time.Millisecond)
|
|
log("load " + n)
|
|
}
|
|
}
|
|
|
|
// tick finishes due loads; the Archive fails once to show retry.
|
|
func (d *dataLazy) tick(c *ui.Context) {
|
|
for n, at := range d.due {
|
|
if c.Now().Before(at) {
|
|
c.After(at.Sub(c.Now()))
|
|
continue
|
|
}
|
|
delete(d.due, n)
|
|
if n == "Archive" && !d.failed[n] {
|
|
d.failed[n], d.status[n] = true, mujica.DataFailed
|
|
continue
|
|
}
|
|
d.status[n] = mujica.DataReady
|
|
d.kids[n] = []string{n + " north", n + " south"}
|
|
}
|
|
}
|
|
|
|
func dataTreeDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
big, multiple bool
|
|
lazy *dataLazy
|
|
tree mujica.TreeState[string]
|
|
bigTree mujica.TreeState[string]
|
|
}
|
|
s := ui.Local(c.Root(), "072", func() state { return state{lazy: newDataLazy()} })
|
|
s.lazy.tick(c)
|
|
opts := mujica.TreeOptions[string]{
|
|
Roots: []string{"Nave", "Crypt", "Archive", "Sacristy"}, Children: func(n string) []string { return s.lazy.kids[n] },
|
|
Label: func(n string) string { return n }, ItemStatus: func(n string) mujica.DataStatus { return s.lazy.status[n] },
|
|
Load: s.lazy.load(c, log), Multiple: s.multiple,
|
|
}
|
|
ts := &s.tree
|
|
if s.big {
|
|
opts = dataBigTree(s.multiple)
|
|
ts = &s.bigTree
|
|
}
|
|
dataSection(c, "Tree", func() {
|
|
dataControls(c, func() {
|
|
ui.Checkbox(c, &s.big, "100,000 nodes")
|
|
ui.Checkbox(c, &s.multiple, "Multiple")
|
|
})
|
|
v := mujica.Tree(c, ts, opts)
|
|
v.Size(420, 340)
|
|
if v.Changed() {
|
|
n, _ := ts.Selected()
|
|
log(fmt.Sprintf("selected %s, %d chosen", n, ts.Selection.Len()))
|
|
}
|
|
if v.Submitted() {
|
|
n, _ := ts.Selected()
|
|
log("submitted " + n)
|
|
}
|
|
})
|
|
}
|
|
|
|
// dataBigTree has 100 roots of 999 children: 100,000 nodes, built as they open.
|
|
func dataBigTree(multiple bool) mujica.TreeOptions[string] {
|
|
roots := make([]string, 100)
|
|
for i := range roots {
|
|
roots[i] = fmt.Sprintf("Wing %d", i+1)
|
|
}
|
|
return mujica.TreeOptions[string]{
|
|
Roots: roots, Label: func(n string) string { return n }, Multiple: multiple,
|
|
Children: func(n string) []string {
|
|
if !strings.HasPrefix(n, "Wing ") || strings.Contains(n, "·") {
|
|
return nil
|
|
}
|
|
return dataWing(n)
|
|
},
|
|
}
|
|
}
|
|
|
|
var dataWings = map[string][]string{}
|
|
|
|
func dataWing(n string) []string {
|
|
if kids, ok := dataWings[n]; ok {
|
|
return kids
|
|
}
|
|
kids := make([]string, 999)
|
|
for i := range kids {
|
|
kids[i] = fmt.Sprintf("%s · cell %d", n, i+1)
|
|
}
|
|
dataWings[n] = kids
|
|
return kids
|
|
}
|
|
|
|
func dataTreeTableDemo(c *ui.Context, log func(string)) {
|
|
type state struct {
|
|
multiple bool
|
|
lazy *dataLazy
|
|
table mujica.TreeTableState[string]
|
|
}
|
|
s := ui.Local(c.Root(), "073", func() state { return state{lazy: newDataLazy()} })
|
|
s.lazy.tick(c)
|
|
size := func(n string) int { return len(n)*7 + len(s.lazy.kids[n])*13 }
|
|
text := func(n string) { ui.Text(c, n).SingleLine() }
|
|
dataSection(c, "TreeTable", func() {
|
|
dataControls(c, func() { ui.Checkbox(c, &s.multiple, "Multiple") })
|
|
v := mujica.TreeTable(c, &s.table, mujica.TreeTableOptions[string]{
|
|
Roots: []string{"Nave", "Crypt", "Archive", "Sacristy"}, Children: func(n string) []string { return s.lazy.kids[n] },
|
|
Label: func(n string) string { return n }, ItemStatus: func(n string) mujica.DataStatus { return s.lazy.status[n] },
|
|
Load: s.lazy.load(c, log), Multiple: s.multiple,
|
|
Columns: []mujica.TreeColumn[string]{
|
|
{ID: "name", Title: "Name", MinWidth: 160, Cell: text, Compare: cmp.Compare[string]},
|
|
{ID: "size", Title: "Size", Width: 90, Align: ui.End, Cell: func(n string) { ui.Text(c, fmt.Sprintf("%d m²", size(n))) },
|
|
Compare: func(a, b string) int { return cmp.Compare(size(a), size(b)) }},
|
|
{ID: "kids", Title: "Rooms", Width: 80, Align: ui.End, Cell: func(n string) { ui.Text(c, strconv.Itoa(len(s.lazy.kids[n]))) }},
|
|
},
|
|
})
|
|
v.Size(520, 320)
|
|
if v.SortChanged() {
|
|
log(fmt.Sprintf("sort %s descending=%v", s.table.Sort.Column, s.table.Sort.Descending))
|
|
}
|
|
if v.Changed() {
|
|
n, _ := s.table.Selected()
|
|
log(fmt.Sprintf("selected %s, %d chosen", n, s.table.Selection.Len()))
|
|
}
|
|
})
|
|
}
|