From 70e5529a9b1f6416c8906349c0dbe5fb37e9ff52 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 23 Aug 2019 10:46:59 -0400 Subject: [PATCH] Add List widget combinator. Add HScroll and VScroll functions. Add scroll demo to see a 2-way scrolling field of selectable labels. --- .gitignore | 1 + cmd/scroll/main.go | 152 +++++++++++++++++++++++++++++++++++++++++++++ main.go | 39 +++++++++++- 3 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 cmd/scroll/main.go diff --git a/.gitignore b/.gitignore index f02d41f..00de0bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ cmd/hello/hello cmd/cal/cal +cmd/scroll/scroll *.apk diff --git a/cmd/scroll/main.go b/cmd/scroll/main.go new file mode 100644 index 0000000..d0fc964 --- /dev/null +++ b/cmd/scroll/main.go @@ -0,0 +1,152 @@ +package main + +import ( + "image/color" + "fmt" + "log" + + gio "git.wow.st/gmp/giowrap" + + "gioui.org/ui" + "gioui.org/ui/app" + "gioui.org/ui/layout" + "gioui.org/ui/text" + + "golang.org/x/image/font/sfnt" + "golang.org/x/image/font/gofont/goregular" +) + +var ( + face text.Face + black color.RGBA = color.RGBA{ A: 0xff, R: 0x00, G: 0x00, B: 0x00 } + gray2 color.RGBA = color.RGBA{ A: 0xff, R: 0x70, G: 0x70, B: 0x70 } + gray1 color.RGBA = color.RGBA{ A: 0xff, R: 0x50, G: 0x50, B: 0x50 } +) + +type SLabel struct { + w gio.Clickable + bg *gio.Background + active *bool +} + +func NewSLabel(t string, active *bool, lops ...gio.LabelOption) *SLabel { + ret := &SLabel{} + lops = append([]gio.LabelOption{gio.Face(face)},lops...) + lbl := gio.NewLabel(t, lops...) + bg := &gio.Background{ + Color: gray2, + Radius: ui.Dp(4), + Inset: layout.UniformInset(ui.Dp(4)), + } + if *active { + bg.Color = gray1 + } + l := gio.AsClickable(gio.Enclose(bg,lbl)) + ret.w = l + ret.bg = bg + ret.active = active + return ret +} + +func (w *SLabel) Layout(ctx *gio.Context) { + if *w.active { + w.bg.Color = gray1 + } else { + w.bg.Color = gray2 + } + w.w.Layout(ctx) + if w.w.Clicked(ctx) { + if *w.active { + *w.active = false + } else { + *w.active = true + } + } +} + +func main() { + log.Print("Staring event loop") + go eventloop() + app.Main() + log.Print("App closed") +} + +func diffInsets(x, y app.Insets) bool { + return x.Top != y.Top || + x.Bottom != y.Bottom || + x.Left != y.Left || + x.Right != y.Right +} + +func eventloop() { + w := app.NewWindow( + app.WithWidth(ui.Dp(200)), + app.WithHeight(ui.Dp(400)), + app.WithTitle("Tickets")) + ctx := gio.NewContext(w) + + regular, err := sfnt.Parse(goregular.TTF) + face = ctx.Faces.For(regular, ui.Sp(16)) + if err != nil { log.Fatal("Cannot parse font.") } + + sysbg := gio.NewBackground(gio.Color(black)) + bg := gio.NewBackground(gio.Color(gray2)) + + margin := gio.NewInset(gio.Size(ui.Dp(10))) + sh := gio.HScroll(gio.NewFlex(gio.Axis(layout.Horizontal))) + sv := gio.VScroll(gio.NewFlex(gio.Axis(layout.Vertical))) + numlabs := 50 + labs := make([][]gio.Widget, numlabs) + sels := make([][]bool, numlabs) + for i := 0; i < 16; i++ { + labs[i] = make([]gio.Widget, numlabs) + sels[i] = make([]bool, numlabs) + for j := 0; j < numlabs; j++ { + labs[i][j] = NewSLabel(fmt.Sprintf("%03d",i * j), &sels[i][j]) + } + } + + sysinset := gio.NewInset(gio.Size(ui.Dp(0))) + resetSysinset := func(x app.Insets) { + sysinset = gio.NewInset(gio.Top(x.Top), gio.Bottom(x.Bottom), + gio.Left(x.Left), gio.Right(x.Right)) + } + + var oldInsets app.Insets + + for { select { + case e:= <-w.Events(): + switch e := e.(type) { + case app.DestroyEvent: + return + case app.UpdateEvent: + ctx.Reset(e) + if diffInsets(e.Insets, oldInsets) { + oldInsets = e.Insets + resetSysinset(e.Insets) + } + + sysbg(sysinset(bg(margin( + sh( + sv(labs[0]...), + sv(labs[1]...), + sv(labs[2]...), + sv(labs[3]...), + sv(labs[4]...), + sv(labs[5]...), + sv(labs[6]...), + sv(labs[7]...), + sv(labs[8]...), + sv(labs[9]...), + sv(labs[10]...), + sv(labs[11]...), + sv(labs[12]...), + sv(labs[13]...), + sv(labs[14]...), + sv(labs[15]...), + ))))).Layout(ctx) + ctx.Update() + } + }} +} + diff --git a/main.go b/main.go index 61574d6..61d06bc 100644 --- a/main.go +++ b/main.go @@ -184,7 +184,42 @@ func NewStack() Stack { } } -type Flex WidgetCombinator +type List = WidgetCombinator + +type AxisOpt struct { axis layout.Axis } +func Axis(x layout.Axis) AxisOpt { return AxisOpt{ x } } + +type ListOpts struct { + AxisOpt +} +type ListOption interface { DoListOption(*ListOpts) } +func (x AxisOpt) DoListOption(o *ListOpts) { o.axis = x.axis } + +func NewList(los ...ListOption) List { + opts := &ListOpts{} + for _,o := range los { o.DoListOption(opts) } + l := layout.List { Axis: opts.axis } + return func(ws ...Widget) Widget { + return NewfWidget(func(ctx *Context) { + for l.Init(ctx.c, ctx.q, ctx.ops, ctx.cs, len(ws)); l.More(); l.Next() { + ctx.cs = l.Constraints() + ws[l.Index()].Layout(ctx) + l.End(ctx.dims) + } + ctx.dims = l.Layout() + }) + } +} + +func HScroll(c WidgetCombinator) WidgetCombinator { + return NewList(Axis(layout.Horizontal)) +} + +func VScroll(c WidgetCombinator) WidgetCombinator { + return NewList(Axis(layout.Vertical)) +} + +type Flex = WidgetCombinator // This "Widget" does nothing except set the Flexible field of a FlexOpts // struct within the Extra data structure @@ -199,8 +234,6 @@ func Rigid() Widget { }) } -type AxisOpt struct { axis layout.Axis } -func Axis(x layout.Axis) AxisOpt { return AxisOpt{ x } } type AlignmentOpt struct { alignment layout.Alignment } func Alignment(x layout.Alignment) AlignmentOpt { return AlignmentOpt{ x } }