183 lines
5.4 KiB
Go
183 lines
5.4 KiB
Go
package mujica
|
|
|
|
import (
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"mujicaui/internal"
|
|
"mujicaui/theme"
|
|
)
|
|
|
|
// datetimeGridConfig describes a page of month or year cells, addressed by ordinal.
|
|
type datetimeGridConfig struct {
|
|
// pageOf returns the first ordinal and count of the page holding o.
|
|
pageOf func(o int) (start, n int)
|
|
cols int
|
|
page int
|
|
title func(start int) string
|
|
prev string
|
|
next string
|
|
text func(o int) string
|
|
label func(o int) string
|
|
chosen func(o int) bool
|
|
current func(o int) bool
|
|
enabled func(o int) bool
|
|
// muted greys cells of the page that belong to a neighbor page.
|
|
muted func(o, start int) bool
|
|
autoFocus bool
|
|
}
|
|
|
|
// datetimeGrid draws the page of *cursor and returns the ordinal picked, if any.
|
|
func datetimeGrid(c *ui.Context, cursor *int, cfg datetimeGridConfig) (picked int, ok bool) {
|
|
k := Tokens(c)
|
|
d := Density(c)
|
|
root := ui.Column(c).Gap(d.Unit() * 2).Shrink(0).Width(d.ControlHeight() * 7)
|
|
keyed := ui.Local(root, "datetime-grid-keys", func() bool { return false })
|
|
root.Children(func() {
|
|
start, _ := cfg.pageOf(*cursor)
|
|
ui.Row(c).AlignItems(ui.Center).Gap(d.Unit()).BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border).
|
|
PaddingY(d.Unit()).Children(func() {
|
|
if datetimeIconButton(c, "chevron-left", cfg.prev).Clicked() {
|
|
*cursor, *keyed = *cursor-cfg.page, false
|
|
}
|
|
ui.Text(c, cfg.title(start)).Font(theme.SerifFont).FontSize(theme.BodySize + 2).Bold().
|
|
TextColor(k.Text).Grow(1).TextAlign(ui.Center).SingleLine()
|
|
if datetimeIconButton(c, "chevron-right", cfg.next).Clicked() {
|
|
*cursor, *keyed = *cursor+cfg.page, false
|
|
}
|
|
})
|
|
cells := ui.Column(c).Gap(d.Unit()).FocusGroup(ui.Horizontal | ui.Vertical)
|
|
moved := true
|
|
switch {
|
|
case cells.Shortcut(0, ui.KeyLeft):
|
|
*cursor--
|
|
case cells.Shortcut(0, ui.KeyRight):
|
|
*cursor++
|
|
case cells.Shortcut(0, ui.KeyUp):
|
|
*cursor -= cfg.cols
|
|
case cells.Shortcut(0, ui.KeyDown):
|
|
*cursor += cfg.cols
|
|
case cells.Shortcut(0, ui.KeyPageUp):
|
|
*cursor -= cfg.page
|
|
case cells.Shortcut(0, ui.KeyPageDown):
|
|
*cursor += cfg.page
|
|
case cells.Shortcut(0, ui.KeyHome):
|
|
*cursor, _ = cfg.pageOf(*cursor)
|
|
case cells.Shortcut(0, ui.KeyEnd):
|
|
s, n := cfg.pageOf(*cursor)
|
|
*cursor = s + n - 1
|
|
default:
|
|
moved = false
|
|
}
|
|
if moved {
|
|
*keyed = true
|
|
}
|
|
start, n := cfg.pageOf(*cursor)
|
|
w := d.ControlHeight() * 7 / float32(cfg.cols)
|
|
cells.Children(func() {
|
|
for r := 0; r < n; r += cfg.cols {
|
|
ui.Row(c).Children(func() {
|
|
for o := start + r; o < start+min(r+cfg.cols, n); o++ {
|
|
if datetimeGridCell(c, o, start, *cursor, cfg, w, moved, *keyed) {
|
|
picked, ok = o, true
|
|
*cursor, *keyed = o, false
|
|
}
|
|
}
|
|
})
|
|
}
|
|
})
|
|
})
|
|
return picked, ok
|
|
}
|
|
|
|
// datetimeGridCell draws one month or year cell and reports whether it was picked.
|
|
func datetimeGridCell(c *ui.Context, o, start, cursor int, cfg datetimeGridConfig, w float32, focus, keyed bool) bool {
|
|
k := Tokens(c)
|
|
d := Density(c)
|
|
enabled, chosen := cfg.enabled(o), cfg.chosen(o)
|
|
sel := o - 1
|
|
if chosen {
|
|
sel = o
|
|
}
|
|
cell := ui.RadioBase(c, &sel, o).Size(w, d.ControlHeight()).Center().Radius(theme.ControlRadius).
|
|
Label(cfg.label(o)).Disabled(!enabled).FocusRing(false)
|
|
if o == cursor && enabled {
|
|
if focus {
|
|
cell.Focus()
|
|
} else if cfg.autoFocus {
|
|
cell.AutoFocus()
|
|
}
|
|
}
|
|
fg := k.Text
|
|
if cfg.muted(o, start) {
|
|
fg = k.TextMuted
|
|
}
|
|
switch {
|
|
case chosen:
|
|
cell.Background(k.Accent)
|
|
fg = k.OnAccent
|
|
if cell.Hovered() {
|
|
cell.Background(k.AccentHover)
|
|
}
|
|
case cell.Pressed():
|
|
cell.Background(k.Selection)
|
|
case cell.Hovered():
|
|
cell.Background(k.SurfaceHover)
|
|
}
|
|
current := cfg.current(o)
|
|
ring := cell.FocusVisible() || cell.Focused() && keyed
|
|
cell.DrawOver(func(p *ui.Painter, r ui.Rect) {
|
|
if current {
|
|
p.FillPath(internal.Diamond(r.X+r.W/2, r.Y+r.H-6, 5), fg)
|
|
}
|
|
if ring {
|
|
internal.FocusRing(p, r, theme.ControlRadius, k.Focus)
|
|
}
|
|
})
|
|
cell.Children(func() {
|
|
t := ui.Text(c, cfg.text(o)).TextColor(fg).FontFeatures("tnum").SingleLine()
|
|
if current {
|
|
t.Bold()
|
|
}
|
|
if !enabled {
|
|
t.Strikethrough()
|
|
}
|
|
})
|
|
return cell.Clicked() && enabled
|
|
}
|
|
|
|
// datetimeGridField is the field and popup shared by MonthPicker and YearPicker.
|
|
type datetimeGridField struct {
|
|
text, placeholder, label string
|
|
clearable, hasValue bool
|
|
disabled bool
|
|
// home is the ordinal the popup opens on.
|
|
home int
|
|
}
|
|
|
|
// datetimeGridPicker draws f and its popup grid; it returns the column, a picked ordinal and whether Clear was pressed.
|
|
func datetimeGridPicker(c *ui.Context, f datetimeGridField, cfg datetimeGridConfig) (col *ui.Element, picked int, ok, cleared bool) {
|
|
d := Density(c)
|
|
col = ui.Column(c).Width(d.ControlHeight() * 6).Gap(d.Unit()).AlignItems(ui.Stretch).Disabled(f.disabled)
|
|
open := ui.Local(col, "datetime-grid-open", func() bool { return false })
|
|
cursor := ui.Local(col, "datetime-grid-cursor", func() int { return 0 })
|
|
col.Children(func() {
|
|
field := datetimeField(c, ui.Row(c), false)
|
|
field.Children(func() {
|
|
t := datetimeTrigger(c, f.text, f.placeholder, "calendar").Label(f.label).Description(f.text)
|
|
if t.Clicked() || !*open && t.Shortcut(0, ui.KeyDown) {
|
|
*open, *cursor = !*open, f.home
|
|
}
|
|
if f.clearable && f.hasValue && datetimeIconButton(c, "x", msg(c, "calendar.clear")).Clicked() {
|
|
cleared = true
|
|
}
|
|
})
|
|
datetimePopup(c, field, open, func() {
|
|
cfg.autoFocus = true
|
|
if picked, ok = datetimeGrid(c, cursor, cfg); ok {
|
|
*open = false
|
|
}
|
|
})
|
|
})
|
|
return col, picked, ok, cleared
|
|
}
|