From f134a1f21e862813ce08da69ad35fe640b3d82be Mon Sep 17 00:00:00 2001 From: Greg Date: Fri, 30 Aug 2019 14:51:08 -0400 Subject: [PATCH] Add form example, add /examples directory. --- .gitignore | 1 + cmd/form/main.go | 227 +++++++++++++++++++++++++++++++++++++++++++++++ examples/ctx.go | 36 ++++++++ 3 files changed, 264 insertions(+) create mode 100644 cmd/form/main.go create mode 100644 examples/ctx.go diff --git a/.gitignore b/.gitignore index 0a1017e..3c57862 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ cmd/hello/hello cmd/cal/cal cmd/scroll/scroll cmd/grid/grid +cmd/form/form *.apk memprofile* diff --git a/cmd/form/main.go b/cmd/form/main.go new file mode 100644 index 0000000..6dea555 --- /dev/null +++ b/cmd/form/main.go @@ -0,0 +1,227 @@ +package main + +import ( + "git.wow.st/gmp/giowrap" + + "fmt" + //"image" + "image/color" + "log" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" + + "gioui.org/ui" + "gioui.org/ui/app" + "gioui.org/ui/text" + + "golang.org/x/image/font/gofont/goregular" + "golang.org/x/image/font/sfnt" +) + +var ( + FPS int = 100 + frames int64 + frametime int64 // nanoseconds + maxframetime int64 + mux sync.Mutex + Profile bool = true + RecordMallocs bool = false +) + +func NewButton(face text.Face, t string, c color.RGBA) giowrap.Clickable { + lbl := giowrap.NewLabel(t, giowrap.Face(face), giowrap.Align(text.Middle)) + bg := giowrap.NewBackground(giowrap.Color(c), giowrap.Radius(ui.Dp(4))) + ins := giowrap.NewInset(giowrap.Size(ui.Dp(4))) + return giowrap.AsClickable(ins(bg(lbl))) +} + +func main() { + go func() { + ms := &runtime.MemStats{} + var a1 uint64 + for { + time.Sleep(time.Second * 3) + mux.Lock() + if frames == 0 { + mux.Unlock() + continue + } + runtime.ReadMemStats(ms) + a1 = ms.Alloc + runtime.GC() + runtime.ReadMemStats(ms) + log.Printf("Alloc: %d - %d = %d (%d per frame)", a1, ms.Alloc, a1-ms.Alloc, (a1-ms.Alloc)/(3*uint64(FPS))) + log.Printf("Frametime: %d max Frametime: %d us\n", frametime/(frames*1000), maxframetime/1000) + frames = 0 + frametime = 0 + maxframetime = 0 + mux.Unlock() + } + }() + go main1() + app.Main() +} + +func main1() { + w := app.NewWindow() + regular, err := sfnt.Parse(goregular.TTF) + if err != nil { + log.Fatal("Cannot parse font.") + } + ctx := giowrap.NewContext(w) + t := time.NewTicker(time.Second / time.Duration(FPS)) + + graybg := giowrap.NewBackground( + giowrap.Color(color.RGBA{A: 0xff, R: 0xd0, G: 0xd0, B: 0xd0}), + giowrap.Radius(ui.Dp(4))) + whitebg := giowrap.NewBackground( + giowrap.Color(color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}), + giowrap.Radius(ui.Dp(4))) + margin := giowrap.NewInset(giowrap.Size(ui.Dp(10))) + buffer := giowrap.NewInset(giowrap.Bottom(ui.Dp(10))) + f1 := giowrap.NewFlex(giowrap.Vertical) + row := giowrap.NewFlex(giowrap.Horizontal) + + more := NewButton(ctx.Faces.For(regular, ui.Sp(24)), + "more", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}) + submit := NewButton(ctx.Faces.For(regular, ui.Sp(24)), + "submit", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}) + back := NewButton(ctx.Faces.For(regular, ui.Sp(24)), + "back", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}) + + numfields := 5 + eds := make([]*giowrap.Editor,0) + btns := make([]giowrap.Clickable,0) + entryrows := make([]giowrap.Widget,0) + submitrows := make([]giowrap.Widget,0) + + var entryPage giowrap.Widget + var submitPage giowrap.Widget + + newRow := func(i int) { + eds = append(eds, giowrap.NewEditor(fmt.Sprintf("text %d",i), giowrap.Face(ctx.Faces.For(regular, ui.Sp(24))))) + btns = append(btns, NewButton(ctx.Faces.For(regular, ui.Sp(18)), "X", + color.RGBA{A: 0xff, R: 0x80, G: 0x80, B: 0x80})) + } + + for i := 0; i < numfields; i++ { + newRow(i) + } + updatePages := func() { + entryrows = entryrows[0:0] + entryrows = append(entryrows, buffer(row(more, submit))) + for i := 0; i < len(btns); i++ { + entryrows = append(entryrows, row(btns[i], giowrap.FillWidth(whitebg(eds[i])))) + } + entryPage = graybg(margin(giowrap.VScroll(f1)(entryrows...))) + + submitrows = submitrows[0:0] + submitrows = append(submitrows, buffer(row(back))) + for i := 0; i < len(btns); i++ { + submitrows = append(submitrows, giowrap.NewLabel(eds[i].Text(), giowrap.Face(ctx.Faces.For(regular, ui.Sp(24))))) + } + submitPage = graybg(margin(giowrap.VScroll(f1)(submitrows...))) + } + updatePages() + + page := &entryPage + + profiled := false + startTime := time.Now() + + for { + select { + case <-t.C: + w.Invalidate() + case e := <-w.Events(): + mux.Lock() + stime := time.Now() + switch e := e.(type) { + case app.DestroyEvent: + return + case app.UpdateEvent: + profileNow := false + var ms runtime.MemStats + if Profile && time.Since(startTime) > time.Second*2 { + startTime = time.Now() + profileNow = true + } + if profileNow { + if !profiled { + prof(0) + } + runtime.ReadMemStats(&ms) + if RecordMallocs { + giowrap.RecordMallocs() + } + } + ctx = ctx.Reset(&e) + giowrap.Mallocs("pre-layout") + ctx = (*page).Layout(ctx) + giowrap.Mallocs("post-layout") + ctx.Update() + for i, w := range btns { + if w.Clicked(ctx) { + log.Printf("%d clicked\n",i) + btns = append(btns[:i], btns[i+1:]...) + eds = append(eds[:i], eds[i+1:]...) + entryrows = append(entryrows[:i], entryrows[i+1:]...) + numfields-- + updatePages() + } + } + if more.Clicked(ctx) { + log.Print("More") + newRow(len(btns)) + updatePages() + } + if submit.Clicked(ctx) { + log.Print("Submit") + updatePages() + page = &submitPage + } + if back.Clicked(ctx) { + page = &entryPage + } + if profileNow { + for _, i := range giowrap.AllMallocs { + fmt.Printf("mallocs: %d (%s)\n", i.Value, i.Text) + } + giowrap.AllMallocs = nil + mallocs := ms.Mallocs + runtime.ReadMemStats(&ms) + mallocs = ms.Mallocs - mallocs + log.Printf("Mallocs = %d\n", mallocs) + if !profiled { + prof(1) + profiled = true + } + } + } + dur := time.Since(stime).Nanoseconds() + frames = frames + 1 + frametime = frametime + dur + if dur > maxframetime { + maxframetime = dur + } + mux.Unlock() + } + } +} + +func prof(x int) { + names := []string{"memprofile-1.pprof", "memprofile-2.pprof"} + f1, err := os.Create(names[x]) + if err != nil { + log.Fatal("could not create memory profile: ", err) + } + if err := pprof.WriteHeapProfile(f1); err != nil { + log.Fatal("could not write memory profile: ", err) + } + f1.Close() + log.Print("Memory profile written") +} + diff --git a/examples/ctx.go b/examples/ctx.go new file mode 100644 index 0000000..d16611b --- /dev/null +++ b/examples/ctx.go @@ -0,0 +1,36 @@ +{ + ctx.Reset(&e) // ctx keeps a pointer to e.Config, so we can do... + ctx.RigidConstraints() + { + f1 := layout.Flex{Axis: layout.Vertical} // maybe pass ctx in here? + ins := layout.UniformInset(ui.Dp(10)) + ins.Begin(ctx) // containers like Insets and Flex keep a pointer to ctx + f1.Init(ctx) + f1.Rigid() + editor1.Layout(ctx) + f1.End() + { + f1.Flexible(0.33) + ins := layout.UniformInset(ui.Dp(10)) + ins.Begin(ctx) + editor2.Layout(ctx) + ins.End() + } + f1.End() + { + f1.Flexible(0.67) + f2 := layout.Flex{Axis: layout.Horizontal} + f2.Init(ctx) + f2.Rigid() + button1.Layout(ctx) + c1 := f2.End() + f2.Flexible(0.5) + button2.Layout(ctx) + c2 := f2.End() + f2.Layout(c1, c2) + } + c3 := f1.End() + f1.Layout(c1, c2, c3) + ins.End() + } +}