From 86245ad48485b721535fa7a941e687899e690477 Mon Sep 17 00:00:00 2001 From: Greg Date: Thu, 15 Aug 2019 09:16:00 -0400 Subject: [PATCH] Refactor into a package. --- cmd/hello/main.go | 75 ++++++++++++++ main.go | 245 ++++++++++++++++++++++++++++++---------------- 2 files changed, 235 insertions(+), 85 deletions(-) create mode 100644 cmd/hello/main.go diff --git a/cmd/hello/main.go b/cmd/hello/main.go new file mode 100644 index 0000000..52cbdc3 --- /dev/null +++ b/cmd/hello/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "git.wow.st/gmp/giowrap" + + "log" + "time" + + "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" +) + +func main() { + go func() { + w := app.NewWindow(nil) + regular, err := sfnt.Parse(goregular.TTF) + if err != nil { + log.Fatal("Cannot parse font.") + } + ctx := giowrap.NewContext(w) + t := time.NewTicker(time.Second/30) + e1 := giowrap.NewEditor(ctx.Faces.For(regular, ui.Sp(24)), true) + e1.SetText("hi there") + e1.Focus() + + e2 := giowrap.NewEditor(ctx.Faces.For(regular, ui.Sp(24)), true) + e2.SetText("ok bye") + + 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)) + + lbl := giowrap.NewButton( + ctx.Faces.For(regular, ui.Sp(24)), + "centered", + text.Center, + ) + bg := giowrap.NewBackground(giowrap.Rgb(0x3c98c6), ui.Dp(4)) + btn := bg(lbl) + + for { + select { + case <-t.C: + w.Invalidate() + case e := <-w.Events(): + switch e := e.(type) { + case app.DestroyEvent: + return + case app.DrawEvent: + ctx.Reset(e) + ctx = giowrap.LayoutWithContext(ctx, + OuterInset( + f( + e1, + f.Flexible(5), + InnerInset(e2), + f.Flexible(10), + btn, + ))) + ctx.Draw() + if lbl.Clicked(ctx) { + log.Print("Clicked: " + e2.Text() ) + } + } + } + } + }() + app.Main() +} + diff --git a/main.go b/main.go index 14c92fc..4af0bf0 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ -package main +package giowrap import ( + "image/color" "log" - "time" "gioui.org/ui" "gioui.org/ui/app" @@ -11,23 +11,27 @@ import ( "gioui.org/ui/measure" "gioui.org/ui/text" - "golang.org/x/image/font/gofont/goregular" - "golang.org/x/image/font/sfnt" + "gioui.org/ui/f32" + gdraw "gioui.org/ui/draw" + "gioui.org/ui/gesture" + "gioui.org/ui/pointer" ) type Context struct { + Faces measure.Faces + w *app.Window c *app.Config q input.Queue ops *ui.Ops cs layout.Constraints dims layout.Dimens - faces measure.Faces extra map[string]interface{} } func NewContext(w *app.Window) Context { return Context{ + w: w, ops: new(ui.Ops), q: w.Queue(), extra: make(map[string]interface{}), @@ -38,7 +42,30 @@ func (ctx *Context) Reset(e app.DrawEvent) { ctx.c = &e.Config ctx.ops.Reset() ctx.cs = layout.RigidConstraints(e.Size) - ctx.faces.Reset(ctx.c) + ctx.Faces.Reset(ctx.c) +} + +func (ctx Context) Draw() { + ctx.w.Draw(ctx.ops) +} + +type Layout func(Context) Context + +type LayoutCombinator func(...Layout) Layout + +func LayoutWithContext(ctx Context, ls ...Widget) Context { + for _, l := range ls { + ctx = l.Layout(ctx) + } + return ctx +} + +type fWidget struct { + l Layout +} + +func NewfWidget(l Layout) fWidget { + return fWidget{ l: l } } func (fw fWidget) Layout(ctx Context) Context { @@ -51,30 +78,37 @@ type Widget interface { type WidgetCombinator func(...Widget) Widget -type fWidget struct { - l Layout -} - -type Layout func(Context) Context - -type LayoutCombinator func(...Layout) Layout - -func LayoutWithContext(ctx Context, ls ...Layout) Context { - for _, l := range ls { - ctx = l(ctx) - } - return ctx -} - -type Label struct { +type Button struct { l *text.Label + click gesture.Click } -func (l *Label) Layout(ctx Context) Context { - ctx.dims = l.l.Layout(ctx.ops, ctx.cs) +func NewButton(face text.Face, t string, alignment text.Alignment) *Button { + ret := &Button{} + ret.l = &text.Label{ + Face: face, + Text: t, + Alignment: alignment, + } + return ret +} + +func (b *Button) Layout(ctx Context) Context { + ctx.dims = b.l.Layout(ctx.ops, ctx.cs) + pointer.RectAreaOp{Size: ctx.dims.Size}.Add(ctx.ops) + b.click.Add(ctx.ops) return ctx } +func (b *Button) Clicked(ctx Context) bool { + for _,e := range b.click.Events(ctx.q) { + if e.Type == gesture.TypeClick { + return true + } + } + return false +} + type Editor struct { e *text.Editor } @@ -90,57 +124,24 @@ func (e *Editor) Layout(ctx Context) Context { return ctx } -func main() { - go func() { - w := app.NewWindow(nil) - regular, err := sfnt.Parse(goregular.TTF) - if err != nil { - log.Fatal("Cannot parse font.") - } - ctx := NewContext(w) - t := time.NewTicker(time.Second/30) - e1 := NewEditor(ctx.faces.For(regular, ui.Sp(24)), true) - e1.e.SetText("hi there") - e1.e.Focus() +func (e *Editor) Text() string { + return e.e.Text() +} - e2 := NewEditor( ctx.faces.For(regular, ui.Sp(24)), true) - e2.e.SetText("ok bye") +func (e *Editor) SetText(s string) { + e.e.SetText(s) +} - f := NewFlex(layout.Vertical, layout.Start, layout.Start) - ins10a := NewInset(ui.Dp(10),ui.Dp(10),ui.Dp(10),ui.Dp(10)) - ins10b := NewInset(ui.Dp(10),ui.Dp(10),ui.Dp(10),ui.Dp(10)) +func (e *Editor) Focus() { + e.e.Focus() +} - btn := &Label{} - btn.l = &text.Label{ - Face: ctx.faces.For(regular, ui.Sp(24)), - Text: "centered", - Alignment: text.Center, - } +func Rgb(c uint32) color.RGBA { + return Argb((0xff << 24) | c) +} - for { - select { - case <-t.C: - w.Invalidate() - case e := <-w.Events(): - switch e := e.(type) { - case app.DestroyEvent: - return - case app.DrawEvent: - ctx.Reset(e) - LayoutWithContext(ctx, - ins10a(f( - e1, - Flexible(5), - ins10b(e2), - Flexible(10), - btn, - )).Layout) - w.Draw(ctx.ops) - } - } - } - }() - app.Main() +func Argb(c uint32) color.RGBA { + return color.RGBA{A: uint8(c >> 24), R: uint8(c >> 16), G: uint8(c >> 8), B: uint8(c)} } type FlexChild struct { @@ -148,24 +149,23 @@ type FlexChild struct { w Widget } -func Flexible(v float32) Widget { - ret := fWidget{} - ret.l = func(ctx Context) Context { +type Flex WidgetCombinator + +func (f Flex) Flexible(v float32) Widget { + return NewfWidget(func(ctx Context) Context { ctx.extra["Flexible"] = v return ctx - } - return ret + }) } -func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, crossAxisAlignment layout.CrossAxisAlignment) WidgetCombinator { +func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, crossAxisAlignment layout.CrossAxisAlignment) Flex { f := layout.Flex{ Axis: axis, MainAxisAlignment: mainAxisAlignment, CrossAxisAlignment: crossAxisAlignment, } return func(ws ...Widget) Widget { - ret := fWidget{} - ret.l = func(ctx Context) Context { + return NewfWidget(func(ctx Context) Context { f.Init(ctx.ops, ctx.cs) fcs := make([]layout.FlexChild,len(ws)) for i, w := range ws { @@ -185,16 +185,14 @@ func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, cross ctx.dims = f.Layout(fcs...) delete(ctx.extra, "Flexible") return ctx - } - return ret + }) } } func NewInset(top, right, bottom, left ui.Value) WidgetCombinator { ins := layout.Inset{ Top: top, Right: right, Bottom: bottom, Left: left } return func(ws ...Widget) Widget { - ret := fWidget{} - ret.l = func(ctx Context) Context { + return NewfWidget(func(ctx Context) Context { ctx.cs = ins.Begin(ctx.c, ctx.ops, ctx.cs) var dims layout.Dimens for _, w := range ws { @@ -202,8 +200,85 @@ func NewInset(top, right, bottom, left ui.Value) WidgetCombinator { } ctx.dims = ins.End(dims) return ctx - } - return ret + }) } } +type Background struct { + Color color.RGBA + Radius ui.Value + Inset layout.Inset + + macro ui.MacroOp +} + +type Enclosure interface { + Begin(Context) Context + End(Context) Context +} + +func Enclose(e Enclosure, ws ...Widget) Widget { + return NewfWidget(func(ctx Context) Context { + ctx = e.Begin(ctx) + for _,w := range ws { + ctx = w.Layout(ctx) + } + ctx = e.End(ctx) + return ctx + }) +} + +func NewBackground(color color.RGBA, radius ui.Value) WidgetCombinator { + bg := &Background{ + Color: color, + Radius: radius, + Inset: layout.UniformInset(radius), // FIXME: need to be able to + } // do math ops on Values + return func(ws ...Widget) Widget { + return Enclose(bg, ws...) + } +} + +func (bg *Background) Begin(ctx Context) Context { + bg.macro.Record(ctx.ops) + ctx.cs = bg.Inset.Begin(ctx.c, ctx.ops, ctx.cs) + return ctx +} + +func (bg *Background) End(ctx Context) Context { + ctx.dims = bg.Inset.End(ctx.dims) + bg.macro.Stop() + //var stack ui.StackOp + w, h := float32(ctx.dims.Size.X), float32(ctx.dims.Size.Y) + if r := float32(ctx.c.Px(bg.Radius)); r > 0 { + if r > w / 2 { + r = w / 2 + } + if r > h / 2 { + r = h / 2 + } + rrect(ctx.ops, w, h, r, r, r, r) + } + gdraw.ColorOp{Color: bg.Color}.Add(ctx.ops) + gdraw.DrawOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(ctx.ops) + bg.macro.Add(ctx.ops) + return ctx +} + +// https://pomax.github.io/bezierinfo/#circles_cubic. +func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) { + w, h := float32(width), float32(height) + const c = 0.55228475 // 4*(sqrt(2)-1)/3 + var b gdraw.PathBuilder + b.Init(ops) + b.Move(f32.Point{X: w, Y: h - se}) + b.Cube(f32.Point{X: 0, Y: se * c}, f32.Point{X: -se + se*c, Y: se}, f32.Point{X: -se, Y: se}) // SE + b.Line(f32.Point{X: sw - w + se, Y: 0}) + b.Cube(f32.Point{X: -sw * c, Y: 0}, f32.Point{X: -sw, Y: -sw + sw*c}, f32.Point{X: -sw, Y: -sw}) // SW + b.Line(f32.Point{X: 0, Y: nw - h + sw}) + b.Cube(f32.Point{X: 0, Y: -nw * c}, f32.Point{X: nw - nw*c, Y: -nw}, f32.Point{X: nw, Y: -nw}) // NW + b.Line(f32.Point{X: w - ne - nw, Y: 0}) + b.Cube(f32.Point{X: ne * c, Y: 0}, f32.Point{X: ne, Y: ne - ne*c}, f32.Point{X: ne, Y: ne}) // NE + b.End() +} +