Files
p9/cmd/gui/cfg/config.go
Loki Verloren 0e2bba237a initial commit
2021-05-03 10:43:10 +02:00

589 lines
13 KiB
Go

package cfg
import (
"sort"
"golang.org/x/exp/shiny/materialdesign/icons"
"github.com/p9c/p9/pkg/gel/gio/text"
l "github.com/p9c/p9/pkg/gel/gio/layout"
"github.com/p9c/p9/pkg/gel"
)
type Item struct {
slug string
typ string
label string
description string
widget string
dataType string
options []string
Slot interface{}
}
func (it *Item) Item(ng *Config) l.Widget {
return func(gtx l.Context) l.Dimensions {
return ng.Theme.VFlex().Rigid(
ng.H6(it.label).Fn,
).Fn(gtx)
}
}
type ItemMap map[string]*Item
type GroupsMap map[string]ItemMap
type ListItem struct {
name string
widget func() []l.Widget
}
type ListItems []ListItem
func (l ListItems) Len() int {
return len(l)
}
func (l ListItems) Less(i, j int) bool {
return l[i].name < l[j].name
}
func (l ListItems) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
type List struct {
name string
items ListItems
}
type Lists []List
func (l Lists) Len() int {
return len(l)
}
func (l Lists) Less(i, j int) bool {
return l[i].name < l[j].name
}
func (l Lists) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
func (c *Config) Config() GroupsMap {
// schema := podcfg.GetConfigSchema(c.cx.Config)
tabNames := make(GroupsMap)
// // tabs := make(p9.WidgetMap)
// for i := range schema.Groups {
// for j := range schema.Groups[i].Fields {
// sgf := schema.Groups[i].Fields[j]
// if _, ok := tabNames[sgf.Group]; !ok {
// tabNames[sgf.Group] = make(ItemMap)
// }
// tabNames[sgf.Group][sgf.Slug] = &Item{
// slug: sgf.Slug,
// typ: sgf.Type,
// label: sgf.Label,
// description: sgf.Title,
// widget: sgf.Widget,
// dataType: sgf.Datatype,
// options: sgf.Options,
// Slot: c.cx.ConfigMap[sgf.Slug],
// }
// // D.S(sgf)
// // create all the necessary widgets required before display
// tgs := tabNames[sgf.Group][sgf.Slug]
// switch sgf.Widget {
// case "toggle":
// c.Bools[sgf.Slug] = c.Bool(*tgs.Slot.(*bool)).SetOnChange(
// func(b bool) {
// D.Ln(sgf.Slug, "submitted", b)
// bb := c.cx.ConfigMap[sgf.Slug].(*bool)
// *bb = b
// podcfg.Save(c.cx.Config)
// if sgf.Slug == "DarkTheme" {
// c.Theme.Colors.SetTheme(b)
// }
// },
// )
// case "integer":
// c.inputs[sgf.Slug] = c.Input(
// fmt.Sprint(*tgs.Slot.(*int)), sgf.Slug, "DocText", "DocBg", "PanelBg", func(txt string) {
// D.Ln(sgf.Slug, "submitted", txt)
// i := c.cx.ConfigMap[sgf.Slug].(*int)
// if n, e := strconv.Atoi(txt); !E.Chk(e) {
// *i = n
// }
// podcfg.Save(c.cx.Config)
// }, nil,
// )
// case "time":
// c.inputs[sgf.Slug] = c.Input(
// fmt.Sprint(
// *tgs.Slot.(*time.
// Duration),
// ), sgf.Slug, "DocText", "DocBg", "PanelBg", func(txt string) {
// D.Ln(sgf.Slug, "submitted", txt)
// tt := c.cx.ConfigMap[sgf.Slug].(*time.Duration)
// if d, e := time.ParseDuration(txt); !E.Chk(e) {
// *tt = d
// }
// podcfg.Save(c.cx.Config)
// }, nil,
// )
// case "float":
// c.inputs[sgf.Slug] = c.Input(
// strconv.FormatFloat(
// *tgs.Slot.(
// *float64), 'f', -1, 64,
// ), sgf.Slug, "DocText", "DocBg", "PanelBg", func(txt string) {
// D.Ln(sgf.Slug, "submitted", txt)
// ff := c.cx.ConfigMap[sgf.Slug].(*float64)
// if f, e := strconv.ParseFloat(txt, 64); !E.Chk(e) {
// *ff = f
// }
// podcfg.Save(c.cx.Config)
// }, nil,
// )
// case "string":
// c.inputs[sgf.Slug] = c.Input(
// *tgs.Slot.(*string), sgf.Slug, "DocText", "DocBg", "PanelBg", func(txt string) {
// D.Ln(sgf.Slug, "submitted", txt)
// ss := c.cx.ConfigMap[sgf.Slug].(*string)
// *ss = txt
// podcfg.Save(c.cx.Config)
// }, nil,
// )
// case "password":
// c.passwords[sgf.Slug] = c.Password(
// "password",
// tgs.Slot.(*string), "DocText", "DocBg", "PanelBg",
// func(txt string) {
// D.Ln(sgf.Slug, "submitted", txt)
// pp := c.cx.ConfigMap[sgf.Slug].(*string)
// *pp = txt
// podcfg.Save(c.cx.Config)
// },
// )
// case "multi":
// c.multis[sgf.Slug] = c.Multiline(
// tgs.Slot.(*cli.StringSlice), "DocText", "DocBg", "PanelBg", 30, func(txt []string) {
// D.Ln(sgf.Slug, "submitted", txt)
// sss := c.cx.ConfigMap[sgf.Slug].(*cli.StringSlice)
// *sss = txt
// podcfg.Save(c.cx.Config)
// },
// )
// // c.multis[sgf.Slug]
// case "radio":
// c.checkables[sgf.Slug] = c.Checkable()
// for i := range sgf.Options {
// c.checkables[sgf.Slug+sgf.Options[i]] = c.Checkable()
// }
// txt := *tabNames[sgf.Group][sgf.Slug].Slot.(*string)
// c.enums[sgf.Slug] = c.Enum().SetValue(txt).SetOnChange(
// func(value string) {
// rr := c.cx.ConfigMap[sgf.Slug].(*string)
// *rr = value
// podcfg.Save(c.cx.Config)
// },
// )
// c.lists[sgf.Slug] = c.List()
// }
// }
// }
// D.S(tabNames)
return tabNames // .Widget(c)
// return func(gtx l.Context) l.Dimensions {
// return l.Dimensions{}
// }
}
func (gm GroupsMap) Widget(ng *Config) l.Widget {
// _, file, line, _ := runtime.Caller(2)
// D.F("%s:%d", file, line)
var groups Lists
for i := range gm {
var li ListItems
gmi := gm[i]
for j := range gmi {
gmij := gmi[j]
li = append(
li, ListItem{
name: j,
widget: func() []l.Widget {
return ng.RenderConfigItem(gmij, len(li))
},
// },
},
)
}
sort.Sort(li)
groups = append(groups, List{name: i, items: li})
}
sort.Sort(groups)
var out []l.Widget
first := true
for i := range groups {
// D.Ln(groups[i].name)
g := groups[i]
if !first {
// put a space between the sections
out = append(
out, func(gtx l.Context) l.Dimensions {
dims := ng.VFlex().
// Rigid(
// // ng.Inset(0.25,
// ng.Fill("DocBg", l.Center, ng.TextSize.True, l.S, ng.Inset(0.25,
// gel.EmptyMaxWidth()).Fn,
// ).Fn,
// // ).Fn,
// ).
Rigid(ng.Inset(0.25, gel.EmptyMaxWidth()).Fn).
// Rigid(
// // ng.Inset(0.25,
// ng.Fill("DocBg", l.Center, ng.TextSize.True, l.N, ng.Inset(0.25,
// gel.EmptyMaxWidth()).Fn,
// ).Fn,
// // ).Fn,
// ).
Fn(gtx)
// ng.Fill("PanelBg", gel.EmptySpace(gtx.Constraints.Max.X, gtx.Constraints.Max.Y), l.Center, 0).Fn(gtx)
return dims
},
)
// out = append(out, func(gtx l.Context) l.Dimensions {
// return ng.th.ButtonInset(0.25, p9.EmptySpace(0, 0)).Fn(gtx)
// })
} else {
first = false
}
// put in the header
out = append(
out,
ng.Fill(
"Primary", l.Center, ng.TextSize.V*2, 0, ng.Flex().Flexed(
1,
ng.Inset(
0.75,
ng.H3(g.name).
Color("DocText").
Alignment(text.Start).
Fn,
).Fn,
).Fn,
).Fn,
)
// out = append(out, func(gtx l.Context) l.Dimensions {
// return ng.th.Fill("PanelBg",
// ng.th.ButtonInset(0.25,
// ng.th.Flex().Flexed(1,
// p9.EmptyMaxWidth(),
// ).Fn,
// ).Fn,
// ).Fn(gtx)
// })
// add the widgets
for j := range groups[i].items {
gi := groups[i].items[j]
for x := range gi.widget() {
k := x
out = append(
out, func(gtx l.Context) l.Dimensions {
if k < len(gi.widget()) {
return ng.Fill(
"DocBg", l.Center, ng.TextSize.V, 0, ng.Flex().
// Rigid(
// ng.Inset(0.25, gel.EmptySpace(0, 0)).Fn,
// ).
Rigid(
ng.Inset(
0.25,
gi.widget()[k],
).Fn,
).Fn,
).Fn(gtx)
}
return l.Dimensions{}
},
)
}
}
}
le := func(gtx l.Context, index int) l.Dimensions {
return out[index](gtx)
}
return func(gtx l.Context) l.Dimensions {
// clip.UniformRRect(f32.Rectangle{
// Max: f32.Pt(float32(gtx.Constraints.Max.X), float32(gtx.Constraints.Max.Y)),
// }, ng.TextSize.True/2).Add(gtx.Ops)
return ng.Fill(
"DocBg", l.Center, ng.TextSize.V, 0, ng.Inset(
0.25,
ng.lists["settings"].
Vertical().
Length(len(out)).
// Background("PanelBg").
// Color("DocBg").
// Active("Primary").
ListElement(le).
Fn,
).Fn,
).Fn(gtx)
}
}
// RenderConfigItem renders a config item. It takes a position variable which tells it which index it begins on
// the bigger config widget list, with this and its current data set the multi can insert and delete elements above
// its add button without rerendering the config item or worse, the whole config widget
func (c *Config) RenderConfigItem(item *Item, position int) []l.Widget {
switch item.widget {
case "toggle":
return c.RenderToggle(item)
case "integer":
return c.RenderInteger(item)
case "time":
return c.RenderTime(item)
case "float":
return c.RenderFloat(item)
case "string":
return c.RenderString(item)
case "password":
return c.RenderPassword(item)
case "multi":
return c.RenderMulti(item, position)
case "radio":
return c.RenderRadio(item)
}
D.Ln("fallthrough", item.widget)
return []l.Widget{func(l.Context) l.Dimensions { return l.Dimensions{} }}
}
func (c *Config) RenderToggle(item *Item) []l.Widget {
return []l.Widget{
func(gtx l.Context) l.Dimensions {
return c.Inset(
0.25,
c.Flex().
Rigid(
c.Switch(c.Bools[item.slug]).DisabledColor("Light").Fn,
).
Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).Fn(gtx)
},
}
}
func (c *Config) RenderInteger(item *Item) []l.Widget {
return []l.Widget{
func(gtx l.Context) l.Dimensions {
return c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.inputs[item.slug].Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).Fn(gtx)
},
}
}
func (c *Config) RenderTime(item *Item) []l.Widget {
return []l.Widget{
func(gtx l.Context) l.Dimensions {
return c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.inputs[item.slug].Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).
Fn(gtx)
},
}
}
func (c *Config) RenderFloat(item *Item) []l.Widget {
return []l.Widget{
func(gtx l.Context) l.Dimensions {
return c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.inputs[item.slug].Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).
Fn(gtx)
},
}
}
func (c *Config) RenderString(item *Item) []l.Widget {
return []l.Widget{
c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.inputs[item.slug].Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).
Fn,
}
}
func (c *Config) RenderPassword(item *Item) []l.Widget {
return []l.Widget{
c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.passwords[item.slug].Fn,
).
Rigid(
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).
Fn,
}
}
func (c *Config) RenderMulti(item *Item, position int) []l.Widget {
// D.Ln("rendering multi")
// c.multis[item.slug].
w := []l.Widget{
func(gtx l.Context) l.Dimensions {
return c.Inset(
0.25,
c.Flex().Flexed(
1,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.Caption(item.description).Fn,
).Fn,
).Fn,
).
Fn(gtx)
},
}
widgets := c.multis[item.slug].Widgets()
// D.Ln(widgets)
w = append(w, widgets...)
return w
}
func (c *Config) RenderRadio(item *Item) []l.Widget {
out := func(gtx l.Context) l.Dimensions {
var options []l.Widget
for i := range item.options {
var color string
color = "PanelBg"
if c.enums[item.slug].Value() == item.options[i] {
color = "Primary"
}
options = append(
options,
c.RadioButton(
c.checkables[item.slug+item.options[i]].
Color("DocText").
IconColor(color).
CheckedStateIcon(&icons.ToggleRadioButtonChecked).
UncheckedStateIcon(&icons.ToggleRadioButtonUnchecked),
c.enums[item.slug], item.options[i], item.options[i],
).Fn,
)
}
return c.Inset(
0.25,
c.VFlex().
Rigid(
c.Body1(item.label).Fn,
).
Rigid(
c.Flex().
Rigid(
func(gtx l.Context) l.Dimensions {
gtx.Constraints.Max.X = int(c.Theme.TextSize.Scale(10).V)
return c.lists[item.slug].DisableScroll(true).Slice(gtx, options...)(gtx)
// // return c.lists[item.slug].Length(len(options)).Vertical().ListElement(func(gtx l.Context, index int) l.Dimensions {
// // return options[index](gtx)
// // }).Fn(gtx)
// return c.lists[item.slug].Slice(gtx, options...)(gtx)
// // return l.Dimensions{}
},
).
Flexed(
1,
c.Caption(item.description).Fn,
).
Fn,
).Fn,
).
Fn(gtx)
}
return []l.Widget{out}
}