Date & Time: components 051-058 (wip)
This commit is contained in:
45 files changed
+3943
-1
No files matched your search
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user