From 716cd6b5621527eaaa393fe15f44c549af56ae70 Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 16 Aug 2019 16:38:49 -0400 Subject: [PATCH] Functional options. --- cmd/hello/main.go | 44 +++++++----- main.go | 180 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 176 insertions(+), 48 deletions(-) diff --git a/cmd/hello/main.go b/cmd/hello/main.go index 8aabb23..9c6acaa 100644 --- a/cmd/hello/main.go +++ b/cmd/hello/main.go @@ -16,6 +16,12 @@ import ( "golang.org/x/image/font/gofont/goregular" ) +func NewButton(face text.Face, t string, c color.RGBA) giowrap.Clickable { + lbl := giowrap.NewLabel(t, giowrap.LabelFace(face), giowrap.LabelAlignment(text.Center)) + bg := giowrap.NewBackground(giowrap.BgColor(c), giowrap.BgRadius(ui.Dp(4))) + return giowrap.AsClickable(bg(lbl)) +} + func main() { go func() { w := app.NewWindow(nil) @@ -25,24 +31,21 @@ func main() { } ctx := giowrap.NewContext(w) t := time.NewTicker(time.Second/30) - e1 := giowrap.NewEditor(ctx.Faces.For(regular, ui.Sp(24)), true) - e1.SetText("text 1") + e1 := giowrap.NewEditor("text 1",giowrap.EditorFace(ctx.Faces.For(regular, ui.Sp(24)))) e1.Focus() - e2 := giowrap.NewEditor(ctx.Faces.For(regular, ui.Sp(24)), true) - e2.SetText("text 2") + e2 := giowrap.NewEditor("text 2",giowrap.EditorFace(ctx.Faces.For(regular, ui.Sp(24)))) - f := giowrap.NewFlex(layout.Vertical, layout.Start, layout.Start) - OuterInset := giowrap.NewInset(ui.Dp(10),ui.Dp(10),ui.Dp(10),ui.Dp(10)) - InnerInset := giowrap.NewInset(ui.Dp(10),ui.Dp(10),ui.Dp(10),ui.Dp(10)) + f1 := giowrap.NewFlex(giowrap.FlexAxis(layout.Vertical)) + OuterInset := giowrap.NewInset(giowrap.InsetSize(ui.Dp(10))) + InnerInset := giowrap.NewInset(giowrap.InsetSize(ui.Dp(10))) - lbl := giowrap.NewLabel( - ctx.Faces.For(regular, ui.Sp(24)), - "push", - text.Center, - ) - bg := giowrap.NewBackground(color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}, ui.Dp(4)) - btn := giowrap.Clickable(bg(lbl)) + f2 := giowrap.NewFlex(giowrap.FlexAxis(layout.Horizontal)) + + btn1 := NewButton(ctx.Faces.For(regular, ui.Sp(24)), + "push1", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}) + btn2 := NewButton(ctx.Faces.For(regular, ui.Sp(24)), + "push2", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}) for { select { @@ -55,15 +58,22 @@ func main() { case app.DrawEvent: ctx = ctx.Reset(e) ctx = OuterInset( - f( + f1( e1, giowrap.Flexible(5), InnerInset(e2), giowrap.Flexible(10), - btn, + f2( + InnerInset(btn1), + giowrap.Flexible(1), + InnerInset(btn2), + ), )).Layout(ctx) ctx.Draw() - if btn.Clicked(ctx) { + if btn1.Clicked(ctx) { + log.Print("Clicked: " + e1.Text() ) + } + if btn2.Clicked(ctx) { log.Print("Clicked: " + e2.Text() ) } } diff --git a/main.go b/main.go index 8e697af..84e18af 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,8 @@ package giowrap import ( "image/color" - "log" + //"log" + "sync" "gioui.org/ui" "gioui.org/ui/app" @@ -17,6 +18,24 @@ import ( "gioui.org/ui/pointer" ) +type Extra struct { + cur, max int + sync.Mutex + data []interface{} +} + +var extra Extra + +func (e *Extra) New() int { + e.Lock() + if e.data == nil { e.data = make([]interface{},0) } + ret := e.max + e.max = e.max + 1 + e.data = append(e.data,nil) + e.Unlock() + return ret +} + type Context struct { Faces measure.Faces @@ -26,16 +45,15 @@ type Context struct { ops *ui.Ops cs layout.Constraints dims layout.Dimens - extra map[string]interface{} } func NewContext(w *app.Window) Context { - return Context{ + ret := Context{ w: w, ops: new(ui.Ops), q: w.Queue(), - extra: make(map[string]interface{}), } + return ret } func (ctx Context) Reset(e app.DrawEvent) Context { @@ -62,12 +80,26 @@ type Label struct { l *text.Label } -func NewLabel(face text.Face, t string, alignment text.Alignment) *Label { +type LabelOpts struct { + face text.Face + alignment text.Alignment +} +type LabelOption func(*LabelOpts) +func LabelFace(x text.Face) LabelOption { + return func(o *LabelOpts) { o.face = x } +} +func LabelAlignment(x text.Alignment) LabelOption { + return func(o *LabelOpts) { o.alignment = x } +} + +func NewLabel(t string, lops ...LabelOption) *Label { ret := &Label{} + opts := &LabelOpts{} + for _,o := range lops { o(opts) } ret.l = &text.Label{ - Face: face, + Face: opts.face, Text: t, - Alignment: alignment, + Alignment: opts.alignment, } return ret } @@ -81,9 +113,24 @@ type Editor struct { e *text.Editor } -func NewEditor(face text.Face, singleline bool) *Editor { +type EditorOpts struct { + face text.Face + singleline bool +} +type EditorOption func(*EditorOpts) +func EditorFace(x text.Face) EditorOption { + return func(o *EditorOpts) { o.face = x } +} +func EditorSingleline() EditorOption { + return func(o *EditorOpts) { o.singleline = true } +} + +func NewEditor(t string, eops ...EditorOption) *Editor { ret := &Editor{} - ret.e = &text.Editor{ Face: face, SingleLine: singleline } + opts := &EditorOpts{} + for _,o := range eops { o(opts) } + ret.e = &text.Editor{ Face: opts.face, SingleLine: opts.singleline } + ret.SetText(t) return ret } @@ -110,33 +157,56 @@ func (fw fWidget) Layout(ctx Context) Context { type Flex WidgetCombinator -// This "Widget" does nothing except set the Flexible key of ctx.extra. +// This "Widget" does nothing except set the Flexible field of a FlexOpts +// struct within the Extra data structure func Flexible(v float32) Widget { return NewfWidget(func(ctx Context) Context { - ctx.extra["Flexible"] = v + extra.data[extra.cur].(*FlexOpts).flexible = v return ctx }) } +type FlexOpts struct { + axis layout.Axis + mainAxisAlignment layout.MainAxisAlignment + crossAxisAlignment layout.CrossAxisAlignment + flexible float32 +} + +type FlexOption func(*FlexOpts) + +func FlexAxis(x layout.Axis) FlexOption { + return func(o *FlexOpts) { o.axis = x } +} + +func MainAxisAlignment(x layout.MainAxisAlignment) FlexOption { + return func(o *FlexOpts) { o.mainAxisAlignment = x } +} +func CrossAxisAlignment(x layout.CrossAxisAlignment) FlexOption { + return func(o *FlexOpts) { o.crossAxisAlignment = x } +} + // NewFlex returns a WidgetCombinator that wraps the layout.Flex element. -func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, crossAxisAlignment layout.CrossAxisAlignment) Flex { +func NewFlex(fos ...FlexOption) Flex { + opts := &FlexOpts{} + for _,o := range fos { o(opts) } f := layout.Flex{ - Axis: axis, - MainAxisAlignment: mainAxisAlignment, - CrossAxisAlignment: crossAxisAlignment, + Axis: opts.axis, + MainAxisAlignment: opts.mainAxisAlignment, + CrossAxisAlignment: opts.crossAxisAlignment, } + index := extra.New() + extra.data[index] = opts + return func(ws ...Widget) Widget { return NewfWidget(func(ctx Context) Context { + extra.cur = index + opts := extra.data[index].(*FlexOpts) f.Init(ctx.ops, ctx.cs) fcs := make([]layout.FlexChild,len(ws)) for i, w := range ws { - if v,ok := ctx.extra["Flexible"]; ok { - switch v := v.(type) { - case float32: - ctx.cs = f.Flexible(v) - default: - log.Fatal("Type error") - } + if v := opts.flexible; v != 0 { + ctx.cs = f.Flexible(v) } else { ctx.cs = f.Rigid() } @@ -144,15 +214,43 @@ func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, cross fcs[i] = f.End(ctx.dims) } ctx.dims = f.Layout(fcs...) - delete(ctx.extra, "Flexible") return ctx }) } } +type InsetOpts struct { + top, right, bottom, left ui.Value +} + +type InsetOption func(*InsetOpts) + +func InsetTop(x ui.Value) InsetOption { + return func(o *InsetOpts) { o.top = x } +} +func InsetRight(x ui.Value) InsetOption { + return func(o *InsetOpts) { o.right = x } +} +func InsetBottom(x ui.Value) InsetOption { + return func(o *InsetOpts) { o.bottom = x } +} +func InsetLeft(x ui.Value) InsetOption { + return func(o *InsetOpts) { o.left = x } +} +func InsetSize(x ui.Value) InsetOption { + return func(o *InsetOpts) { + o.top = x + o.right = x + o.bottom = x + o.left = x + } +} + //NewInset returns a WidgetCombinator that wraps the layout.Inset element. -func NewInset(top, right, bottom, left ui.Value) WidgetCombinator { - ins := layout.Inset{ Top: top, Right: right, Bottom: bottom, Left: left } +func NewInset(insos ...InsetOption) WidgetCombinator { + opts := &InsetOpts{} + for _,o := range insos { o(opts) } + ins := layout.Inset{ Top: opts.top, Right: opts.right, Bottom: opts.bottom, Left: opts.left } return func(ws ...Widget) Widget { return NewfWidget(func(ctx Context) Context { ctx.cs = ins.Begin(ctx.c, ctx.ops, ctx.cs) @@ -190,12 +288,27 @@ func Enclose(e Enclosure, ws ...Widget) Widget { }) } -func NewBackground(color color.RGBA, radius ui.Value) WidgetCombinator { +type BackgroundOpts struct { + c color.RGBA + radius ui.Value +} + +type BackgroundOption func(*BackgroundOpts) +func BgColor(c color.RGBA) BackgroundOption { + return func(o *BackgroundOpts) { o.c = c } +} +func BgRadius(x ui.Value) BackgroundOption { + return func(o *BackgroundOpts) { o.radius = x } +} + +func NewBackground(bos ...BackgroundOption) WidgetCombinator { + opts := &BackgroundOpts{} + for _,o := range bos { o(opts) } bg := &Background{ - Color: color, - Radius: radius, - Inset: layout.UniformInset(radius), // FIXME: need to be able to - } // do math ops on Values + Color: opts.c, + Radius: opts.radius, + Inset: layout.UniformInset(opts.radius), + } return func(ws ...Widget) Widget { return Enclose(bg, ws...) } @@ -244,6 +357,11 @@ func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) { b.End() } +type Clickable interface { + Widget + Clicked(Context) bool +} + //cWidget is a clickable Widget that provides the Clicked() method. type cWidget struct { w Widget @@ -267,6 +385,6 @@ func (w cWidget) Clicked(ctx Context) bool { } //Clickable converts any Widget into a clickable Widget. -func Clickable(w Widget) cWidget { +func AsClickable(w Widget) cWidget { return cWidget{ w: w, click: new(gesture.Click) } }