ui/pager.go

230 lines
4.3 KiB
Go

package ui
import (
"fmt"
"strconv"
"git.kirsle.net/go/render"
)
// Pager is a frame with Pagers for paginated UI.
type Pager struct {
BaseWidget
// Config settings. NOTE: these are copied in the constructor,
// be sure to update it there too if you add a new option!
Name string // totally optional name
Page int // default 1
Pages int
PerPage int // default 20
MaxPageButtons int // max no. of individual pages to show, 0 = no limit
Font render.Text
OnChange func(page, perPage int)
supervisor *Supervisor
child Widget
buttons []Widget
page string // radio button value of Page
// Private options.
hovering bool
clicked bool
}
// NewPager creates a new Pager.
func NewPager(config Pager) *Pager {
w := &Pager{
Page: config.Page,
Pages: config.Pages,
PerPage: config.PerPage,
MaxPageButtons: config.MaxPageButtons,
Font: config.Font,
OnChange: config.OnChange,
buttons: []Widget{},
}
// default settings
if w.Page == 0 {
w.Page = 1
}
if w.PerPage == 0 {
w.PerPage = 20
}
w.IDFunc(func() string {
return fmt.Sprintf("Pager<%d of %d>", w.Page, w.PerPage)
})
w.child = w.setup()
return w
}
// Supervise the pager to make its buttons work.
func (w *Pager) Supervise(s *Supervisor) {
w.supervisor = s
for _, btn := range w.buttons {
w.supervisor.Add(btn)
}
}
// setup the frame
func (w *Pager) setup() *Frame {
frame := NewFrame("Pager Frame")
frame.SetParent(w)
if w.Pages == 0 {
return frame
}
w.buttons = []Widget{}
w.page = fmt.Sprintf("%d", w.Page)
// Previous Page Button
prev := NewButton("Previous", NewLabel(Label{
Text: "<",
Font: w.Font,
}))
w.buttons = append(w.buttons, prev)
prev.Handle(Click, func(ed EventData) error {
return w.next(-1)
})
frame.Pack(prev, Pack{
Side: W,
})
// Draw the numbered buttons.
for i := 1; i <= w.Pages; i++ {
page := fmt.Sprintf("%d", i)
if w.MaxPageButtons > 0 {
if i > w.MaxPageButtons {
break
}
// The final button shown; make this one always reflect the
// current page IF the current page is greater than this one.
if w.Page >= i && w.Page != i {
continue
}
}
btn := NewRadioButton(
"Page "+page,
&w.page,
page,
NewLabel(Label{
Text: page,
Font: w.Font,
}))
w.buttons = append(w.buttons, btn)
btn.Handle(Click, func(ed EventData) error {
if w.OnChange != nil {
page, _ := strconv.Atoi(w.page)
w.OnChange(page, w.PerPage)
}
return nil
})
if w.supervisor != nil {
w.supervisor.Add(btn)
}
frame.Pack(btn, Pack{
Side: W,
})
}
// Next Page Button
next := NewButton("Next", NewLabel(Label{
Text: ">",
Font: w.Font,
}))
w.buttons = append(w.buttons, next)
next.Handle(Click, func(ed EventData) error {
return w.next(1)
})
frame.Pack(next, Pack{
Side: W,
})
return frame
}
// next (1) or previous (-1) button
func (w *Pager) next(value int) error {
fmt.Printf("next(%d)\n", value)
intvalue, _ := strconv.Atoi(w.page)
intvalue += value
if intvalue < 1 {
intvalue = 1
} else if intvalue > w.Pages {
intvalue = w.Pages
}
w.page = fmt.Sprintf("%d", intvalue)
if w.OnChange != nil {
w.OnChange(intvalue, w.PerPage)
}
return nil
}
// Compute the size of the Pager.
func (w *Pager) Compute(e render.Engine) {
// Compute the size of the inner widget first.
w.child.Compute(e)
// Auto-resize only if we haven't been given a fixed size.
if !w.FixedSize() {
size := w.child.Size()
w.Resize(render.Rect{
W: size.W + w.BoxThickness(2),
H: size.H + w.BoxThickness(2),
})
}
w.BaseWidget.Compute(e)
}
// Present the Pager.
func (w *Pager) Present(e render.Engine, P render.Point) {
if w.Hidden() {
return
}
w.Compute(e)
var (
S = w.Size()
ChildSize = w.child.Size()
)
// Draw the widget's border and everything.
w.DrawBox(e, P)
// Offset further if we are currently sunken.
var clickOffset int
if w.clicked {
clickOffset++
}
// Where to place the child widget.
moveTo := render.Point{
X: P.X + w.BoxThickness(1) + clickOffset,
Y: P.Y + w.BoxThickness(1) + clickOffset,
}
// If we're bigger than we need to be, center the child widget.
if S.Bigger(ChildSize) {
moveTo.X = P.X + (S.W / 2) - (ChildSize.W / 2)
}
// Draw the text label inside.
w.child.Present(e, moveTo)
w.BaseWidget.Present(e, P)
}