package main import ( "log" "time" "gioui.org/ui" "gioui.org/ui/app" "gioui.org/ui/input" "gioui.org/ui/layout" "gioui.org/ui/measure" "gioui.org/ui/text" "golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/sfnt" ) type Context struct { 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{ ops: new(ui.Ops), q: w.Queue(), extra: make(map[string]interface{}), } } 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) } func (fw fWidget) Layout(ctx Context) Context { return fw.l(ctx) } type Widget interface { Layout(Context) Context } 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 { l *text.Label } func (l *Label) Layout(ctx Context) Context { ctx.dims = l.l.Layout(ctx.ops, ctx.cs) return ctx } type Editor struct { e *text.Editor } func NewEditor(face text.Face, singleline bool) *Editor { ret := &Editor{} ret.e = &text.Editor{ Face: face, SingleLine: singleline } return ret } func (e *Editor) Layout(ctx Context) Context { ctx.dims = e.e.Layout(ctx.c, ctx.q, ctx.ops, ctx.cs) 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() e2 := NewEditor( ctx.faces.For(regular, ui.Sp(24)), true) e2.e.SetText("ok bye") 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)) btn := &Label{} btn.l = &text.Label{ Face: ctx.faces.For(regular, ui.Sp(24)), Text: "centered", Alignment: text.Center, } 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() } type FlexChild struct { x float32 w Widget } func Flexible(v float32) Widget { ret := fWidget{} ret.l = func(ctx Context) Context { ctx.extra["Flexible"] = v return ctx } return ret } func NewFlex(axis layout.Axis, mainAxisAlignment layout.MainAxisAlignment, crossAxisAlignment layout.CrossAxisAlignment) WidgetCombinator { f := layout.Flex{ Axis: axis, MainAxisAlignment: mainAxisAlignment, CrossAxisAlignment: crossAxisAlignment, } return func(ws ...Widget) Widget { ret := fWidget{} ret.l = func(ctx Context) Context { 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") } } else { ctx.cs = f.Rigid() } ctx = w.Layout(ctx) fcs[i] = f.End(ctx.dims) } 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 { ctx.cs = ins.Begin(ctx.c, ctx.ops, ctx.cs) var dims layout.Dimens for _, w := range ws { ctx = w.Layout(ctx) } ctx.dims = ins.End(dims) return ctx } return ret } }