Add profiling code, and implementation using basic widgets instead of closures.
This commit is contained in:
parent
a463c073ed
commit
5bef5fc95a
|
@ -5,17 +5,35 @@ import (
|
||||||
|
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gioui.org/ui"
|
"gioui.org/ui"
|
||||||
"gioui.org/ui/app"
|
"gioui.org/ui/app"
|
||||||
|
gdraw "gioui.org/ui/draw"
|
||||||
|
"gioui.org/ui/f32"
|
||||||
|
"gioui.org/ui/gesture"
|
||||||
"gioui.org/ui/layout"
|
"gioui.org/ui/layout"
|
||||||
|
"gioui.org/ui/measure"
|
||||||
|
"gioui.org/ui/pointer"
|
||||||
"gioui.org/ui/text"
|
"gioui.org/ui/text"
|
||||||
|
|
||||||
"golang.org/x/image/font/sfnt"
|
"golang.org/x/image/font/sfnt"
|
||||||
"golang.org/x/image/font/gofont/goregular"
|
"golang.org/x/image/font/gofont/goregular"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
FPS int = 100
|
||||||
|
frames int64
|
||||||
|
frametime int64 // nanoseconds
|
||||||
|
maxframetime int64
|
||||||
|
mux sync.Mutex
|
||||||
|
Profile bool = false
|
||||||
|
)
|
||||||
|
|
||||||
func NewButton(face text.Face, t string, c color.RGBA) giowrap.Clickable {
|
func NewButton(face text.Face, t string, c color.RGBA) giowrap.Clickable {
|
||||||
lbl := giowrap.NewLabel(t, giowrap.LabelFace(face), giowrap.LabelAlignment(text.Center))
|
lbl := giowrap.NewLabel(t, giowrap.LabelFace(face), giowrap.LabelAlignment(text.Center))
|
||||||
bg := giowrap.NewBackground(giowrap.BgColor(c), giowrap.BgRadius(ui.Dp(4)))
|
bg := giowrap.NewBackground(giowrap.BgColor(c), giowrap.BgRadius(ui.Dp(4)))
|
||||||
|
@ -24,13 +42,50 @@ func NewButton(face text.Face, t string, c color.RGBA) giowrap.Clickable {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go func() {
|
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()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "", "main1":
|
||||||
|
log.Print("main1()")
|
||||||
|
go main1()
|
||||||
|
case "main2":
|
||||||
|
log.Print("main2()")
|
||||||
|
go main2()
|
||||||
|
default:
|
||||||
|
log.Fatal(`Usage:
|
||||||
|
hello [main1|main2]
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
app.Main()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main1() {
|
||||||
w := app.NewWindow(nil)
|
w := app.NewWindow(nil)
|
||||||
regular, err := sfnt.Parse(goregular.TTF)
|
regular, err := sfnt.Parse(goregular.TTF)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Cannot parse font.")
|
log.Fatal("Cannot parse font.")
|
||||||
}
|
}
|
||||||
ctx := giowrap.NewContext(w)
|
ctx := giowrap.NewContext(w)
|
||||||
t := time.NewTicker(time.Second/30)
|
t := time.NewTicker(time.Second/time.Duration(FPS))
|
||||||
e1 := giowrap.NewEditor("text 1",giowrap.EditorFace(ctx.Faces.For(regular, ui.Sp(24))))
|
e1 := giowrap.NewEditor("text 1",giowrap.EditorFace(ctx.Faces.For(regular, ui.Sp(24))))
|
||||||
e1.Focus()
|
e1.Focus()
|
||||||
|
|
||||||
|
@ -47,17 +102,21 @@ func main() {
|
||||||
btn2 := NewButton(ctx.Faces.For(regular, ui.Sp(24)),
|
btn2 := NewButton(ctx.Faces.For(regular, ui.Sp(24)),
|
||||||
"push2", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6})
|
"push2", color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6})
|
||||||
|
|
||||||
|
profiled := false
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
w.Invalidate()
|
w.Invalidate()
|
||||||
case e := <-w.Events():
|
case e := <-w.Events():
|
||||||
|
stime := time.Now()
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case app.DestroyEvent:
|
case app.DestroyEvent:
|
||||||
return
|
return
|
||||||
case app.DrawEvent:
|
case app.DrawEvent:
|
||||||
ctx = ctx.Reset(e)
|
ctx.Reset(e)
|
||||||
ctx = OuterInset(
|
OuterInset(
|
||||||
f1(
|
f1(
|
||||||
e1,
|
e1,
|
||||||
giowrap.Flexible(5),
|
giowrap.Flexible(5),
|
||||||
|
@ -65,7 +124,7 @@ func main() {
|
||||||
giowrap.Flexible(10),
|
giowrap.Flexible(10),
|
||||||
f2(
|
f2(
|
||||||
InnerInset(btn1),
|
InnerInset(btn1),
|
||||||
giowrap.Flexible(1),
|
giowrap.Flexible(0.5),
|
||||||
InnerInset(btn2),
|
InnerInset(btn2),
|
||||||
),
|
),
|
||||||
)).Layout(ctx)
|
)).Layout(ctx)
|
||||||
|
@ -77,9 +136,173 @@ func main() {
|
||||||
log.Print("Clicked: " + e2.Text() )
|
log.Print("Clicked: " + e2.Text() )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dur := time.Since(stime).Nanoseconds()
|
||||||
|
mux.Lock()
|
||||||
|
frames = frames + 1
|
||||||
|
frametime = frametime + dur
|
||||||
|
if dur > maxframetime { maxframetime = dur }
|
||||||
|
mux.Unlock()
|
||||||
|
}
|
||||||
|
if profiled { continue }
|
||||||
|
if time.Since(startTime) < time.Second * 10 { continue }
|
||||||
|
if Profile {
|
||||||
|
profiled = true
|
||||||
|
f, err := os.Create("memprofile.pprof")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not create memory profile: ", err)
|
||||||
|
}
|
||||||
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
|
log.Fatal("could not write memory profile: ", err)
|
||||||
|
}
|
||||||
|
log.Print("Memory profile written")
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main2() {
|
||||||
|
w := app.NewWindow(nil)
|
||||||
|
q := w.Queue()
|
||||||
|
ops := new(ui.Ops)
|
||||||
|
var faces measure.Faces
|
||||||
|
regular, err := sfnt.Parse(goregular.TTF)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Cannot parse font.")
|
||||||
|
}
|
||||||
|
t := time.NewTicker(time.Second/time.Duration(FPS))
|
||||||
|
e1 := &text.Editor{
|
||||||
|
Face: faces.For(regular, ui.Sp(24)),
|
||||||
|
SingleLine: true,
|
||||||
|
}
|
||||||
|
e1.Focus()
|
||||||
|
e1.SetText("text 1")
|
||||||
|
|
||||||
|
e2 := &text.Editor{
|
||||||
|
Face: faces.For(regular, ui.Sp(24)),
|
||||||
|
SingleLine: true,
|
||||||
|
}
|
||||||
|
e2.Focus()
|
||||||
|
e2.SetText("text 2")
|
||||||
|
|
||||||
|
f1 := layout.Flex{ Axis: layout.Vertical }
|
||||||
|
OuterInset := layout.UniformInset(ui.Dp(10))
|
||||||
|
InnerInset := layout.UniformInset(ui.Dp(10))
|
||||||
|
|
||||||
|
f2 := layout.Flex{ Axis: layout.Horizontal }
|
||||||
|
|
||||||
|
btn1 := text.Label{
|
||||||
|
Face: faces.For(regular, ui.Sp(24)),
|
||||||
|
Text: "push1",
|
||||||
|
}
|
||||||
|
b1ins := layout.UniformInset(ui.Dp(4))
|
||||||
|
btn2 := text.Label{
|
||||||
|
Face: faces.For(regular, ui.Sp(24)),
|
||||||
|
Text: "push2",
|
||||||
|
}
|
||||||
|
b2ins := layout.UniformInset(ui.Dp(4))
|
||||||
|
var bg1, bg2 ui.MacroOp
|
||||||
|
click1 := new(gesture.Click)
|
||||||
|
click2 := new(gesture.Click)
|
||||||
|
|
||||||
|
profiled := false
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
w.Invalidate()
|
||||||
|
case e := <-w.Events():
|
||||||
|
stime := time.Now()
|
||||||
|
switch e := e.(type) {
|
||||||
|
case app.DestroyEvent:
|
||||||
|
return
|
||||||
|
case app.DrawEvent:
|
||||||
|
c := &e.Config
|
||||||
|
ops.Reset()
|
||||||
|
faces.Reset(c)
|
||||||
|
cs := layout.RigidConstraints(e.Size)
|
||||||
|
cs = OuterInset.Begin(c, ops, cs)
|
||||||
|
f1.Init(ops, cs)
|
||||||
|
cs = f1.Rigid()
|
||||||
|
dims := e1.Layout(c, q, ops, cs)
|
||||||
|
f1c1 := f1.End(dims)
|
||||||
|
cs = f1.Flexible(5)
|
||||||
|
cs = InnerInset.Begin(c, ops, cs)
|
||||||
|
dims = e2.Layout(c, q, ops, cs)
|
||||||
|
dims = InnerInset.End(dims)
|
||||||
|
f1c2 := f1.End(dims)
|
||||||
|
cs = f1.Flexible(10)
|
||||||
|
f2.Init(ops, cs)
|
||||||
|
cs = f2.Rigid()
|
||||||
|
cs = InnerInset.Begin(c, ops, cs)
|
||||||
|
bg1.Record(ops)
|
||||||
|
cs = b1ins.Begin(c, ops, cs)
|
||||||
|
dims = btn1.Layout(ops, cs)
|
||||||
|
dims = b1ins.End(dims)
|
||||||
|
pointer.RectAreaOp{Size: dims.Size}.Add(ops)
|
||||||
|
click1.Add(ops)
|
||||||
|
bg1.Stop()
|
||||||
|
wi, h := float32(dims.Size.X), float32(dims.Size.Y)
|
||||||
|
r := float32(c.Px(ui.Dp(4)))
|
||||||
|
giowrap.Rrect(ops, wi, h, r, r, r, r)
|
||||||
|
gdraw.ColorOp{Color: color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}}.Add(ops)
|
||||||
|
gdraw.DrawOp{Rect: f32.Rectangle{Max: f32.Point{X: wi, Y: h}}}.Add(ops)
|
||||||
|
bg1.Add(ops)
|
||||||
|
dims = InnerInset.End(dims)
|
||||||
|
f2c1 := f2.End(dims)
|
||||||
|
cs = f2.Flexible(0.5)
|
||||||
|
cs = InnerInset.Begin(c, ops, cs)
|
||||||
|
bg2.Record(ops)
|
||||||
|
cs = b2ins.Begin(c, ops, cs)
|
||||||
|
dims = btn2.Layout(ops, cs)
|
||||||
|
dims = b2ins.End(dims)
|
||||||
|
pointer.RectAreaOp{Size: dims.Size}.Add(ops)
|
||||||
|
click2.Add(ops)
|
||||||
|
bg2.Stop()
|
||||||
|
wi, h = float32(dims.Size.X), float32(dims.Size.Y)
|
||||||
|
giowrap.Rrect(ops, wi, h, r, r, r, r)
|
||||||
|
gdraw.ColorOp{Color: color.RGBA{A: 0xff, R: 0x3c, G: 0x98, B: 0xc6}}.Add(ops)
|
||||||
|
gdraw.DrawOp{Rect: f32.Rectangle{Max: f32.Point{X: wi, Y: h}}}.Add(ops)
|
||||||
|
bg2.Add(ops)
|
||||||
|
dims = InnerInset.End(dims)
|
||||||
|
f2c2 := f2.End(dims)
|
||||||
|
dims = f2.Layout(f2c1, f2c2)
|
||||||
|
f1c3 := f1.End(dims)
|
||||||
|
dims = f1.Layout(f1c1, f1c2, f1c3)
|
||||||
|
dims = OuterInset.End(dims)
|
||||||
|
w.Draw(ops)
|
||||||
|
for _,ev := range click1.Events(q) {
|
||||||
|
if ev.Type == gesture.TypeClick {
|
||||||
|
log.Print("Clicked: " + e1.Text() )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ev := range click2.Events(q) {
|
||||||
|
if ev.Type == gesture.TypeClick {
|
||||||
|
log.Print("Clicked: " + e2.Text() )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dur := time.Since(stime).Nanoseconds()
|
||||||
|
mux.Lock()
|
||||||
|
frames = frames + 1
|
||||||
|
frametime = frametime + dur
|
||||||
|
if dur > maxframetime { maxframetime = dur }
|
||||||
|
mux.Unlock()
|
||||||
|
}
|
||||||
|
if profiled { continue }
|
||||||
|
if time.Since(startTime) < time.Second * 10 { continue }
|
||||||
|
if Profile {
|
||||||
|
profiled = true
|
||||||
|
f, err := os.Create("memprofile.pprof")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("could not create memory profile: ", err)
|
||||||
|
}
|
||||||
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
|
log.Fatal("could not write memory profile: ", err)
|
||||||
|
}
|
||||||
|
log.Print("Memory profile written")
|
||||||
|
f.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
app.Main()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
86
main.go
86
main.go
|
@ -47,31 +47,29 @@ type Context struct {
|
||||||
dims layout.Dimens
|
dims layout.Dimens
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(w *app.Window) Context {
|
func NewContext(w *app.Window) *Context {
|
||||||
ret := Context{
|
return &Context{
|
||||||
w: w,
|
w: w,
|
||||||
ops: new(ui.Ops),
|
ops: new(ui.Ops),
|
||||||
q: w.Queue(),
|
q: w.Queue(),
|
||||||
}
|
}
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx Context) Reset(e app.DrawEvent) Context {
|
func (ctx *Context) Reset(e app.DrawEvent) {
|
||||||
ctx.c = &e.Config
|
ctx.c = &e.Config
|
||||||
ctx.ops.Reset()
|
ctx.ops.Reset()
|
||||||
ctx.cs = layout.RigidConstraints(e.Size)
|
ctx.cs = layout.RigidConstraints(e.Size)
|
||||||
ctx.Faces.Reset(ctx.c)
|
ctx.Faces.Reset(ctx.c)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx Context) Draw() {
|
func (ctx *Context) Draw() {
|
||||||
ctx.w.Draw(ctx.ops)
|
ctx.w.Draw(ctx.ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Layout func(Context) Context
|
type Layout func(*Context)
|
||||||
|
|
||||||
type Widget interface {
|
type Widget interface {
|
||||||
Layout(Context) Context
|
Layout(*Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type WidgetCombinator func(...Widget) Widget
|
type WidgetCombinator func(...Widget) Widget
|
||||||
|
@ -104,9 +102,8 @@ func NewLabel(t string, lops ...LabelOption) *Label {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) Layout(ctx Context) Context {
|
func (l *Label) Layout(ctx *Context) {
|
||||||
ctx.dims = l.l.Layout(ctx.ops, ctx.cs)
|
ctx.dims = l.l.Layout(ctx.ops, ctx.cs)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Editor struct {
|
type Editor struct {
|
||||||
|
@ -134,9 +131,8 @@ func NewEditor(t string, eops ...EditorOption) *Editor {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) Layout(ctx Context) Context {
|
func (e *Editor) Layout(ctx *Context) {
|
||||||
ctx.dims = e.e.Layout(ctx.c, ctx.q, ctx.ops, ctx.cs)
|
ctx.dims = e.e.Layout(ctx.c, ctx.q, ctx.ops, ctx.cs)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Editor) Text() string { return e.e.Text() }
|
func (e *Editor) Text() string { return e.e.Text() }
|
||||||
|
@ -151,8 +147,8 @@ func NewfWidget(l Layout) fWidget {
|
||||||
return fWidget{ l: l }
|
return fWidget{ l: l }
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fw fWidget) Layout(ctx Context) Context {
|
func (fw fWidget) Layout(ctx *Context) {
|
||||||
return fw.l(ctx)
|
fw.l(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Flex WidgetCombinator
|
type Flex WidgetCombinator
|
||||||
|
@ -160,9 +156,8 @@ type Flex WidgetCombinator
|
||||||
// This "Widget" does nothing except set the Flexible field of a FlexOpts
|
// This "Widget" does nothing except set the Flexible field of a FlexOpts
|
||||||
// struct within the Extra data structure
|
// struct within the Extra data structure
|
||||||
func Flexible(v float32) Widget {
|
func Flexible(v float32) Widget {
|
||||||
return NewfWidget(func(ctx Context) Context {
|
return NewfWidget(func(ctx *Context) {
|
||||||
extra.data[extra.cur].(*FlexOpts).flexible = v
|
extra.data[extra.cur].(*FlexOpts).flexible = v
|
||||||
return ctx
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,25 +192,26 @@ func NewFlex(fos ...FlexOption) Flex {
|
||||||
}
|
}
|
||||||
index := extra.New()
|
index := extra.New()
|
||||||
extra.data[index] = opts
|
extra.data[index] = opts
|
||||||
|
// do not call "make" inside the Layout function
|
||||||
|
fcs := make([]layout.FlexChild,0)
|
||||||
|
|
||||||
return func(ws ...Widget) Widget {
|
return func(ws ...Widget) Widget {
|
||||||
return NewfWidget(func(ctx Context) Context {
|
return NewfWidget(func(ctx *Context) {
|
||||||
// ensure child widgets write options to the right place
|
// ensure child widgets write options to the right place
|
||||||
extra.cur = index
|
extra.cur = index
|
||||||
opts := extra.data[index].(*FlexOpts)
|
opts := extra.data[index].(*FlexOpts)
|
||||||
f.Init(ctx.ops, ctx.cs)
|
f.Init(ctx.ops, ctx.cs)
|
||||||
fcs := make([]layout.FlexChild,len(ws))
|
for _, w := range ws {
|
||||||
for i, w := range ws {
|
if opts.flexible != 0 {
|
||||||
if v := opts.flexible; v != 0 {
|
ctx.cs = f.Flexible(opts.flexible)
|
||||||
ctx.cs = f.Flexible(v)
|
|
||||||
} else {
|
} else {
|
||||||
ctx.cs = f.Rigid()
|
ctx.cs = f.Rigid()
|
||||||
}
|
}
|
||||||
ctx = w.Layout(ctx)
|
w.Layout(ctx)
|
||||||
fcs[i] = f.End(ctx.dims)
|
fcs = append(fcs, f.End(ctx.dims))
|
||||||
}
|
}
|
||||||
ctx.dims = f.Layout(fcs...)
|
f.Layout(fcs...)
|
||||||
return ctx
|
fcs = fcs[0:0] // truncate
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,14 +249,12 @@ func NewInset(insos ...InsetOption) WidgetCombinator {
|
||||||
for _,o := range insos { o(opts) }
|
for _,o := range insos { o(opts) }
|
||||||
ins := layout.Inset{ Top: opts.top, Right: opts.right, Bottom: opts.bottom, Left: opts.left }
|
ins := layout.Inset{ Top: opts.top, Right: opts.right, Bottom: opts.bottom, Left: opts.left }
|
||||||
return func(ws ...Widget) Widget {
|
return func(ws ...Widget) Widget {
|
||||||
return NewfWidget(func(ctx Context) Context {
|
return NewfWidget(func(ctx *Context) {
|
||||||
ctx.cs = ins.Begin(ctx.c, ctx.ops, ctx.cs)
|
ctx.cs = ins.Begin(ctx.c, ctx.ops, ctx.cs)
|
||||||
var dims layout.Dimens
|
|
||||||
for _, w := range ws {
|
for _, w := range ws {
|
||||||
ctx = w.Layout(ctx)
|
w.Layout(ctx)
|
||||||
}
|
}
|
||||||
ctx.dims = ins.End(dims)
|
ctx.dims = ins.End(ctx.dims)
|
||||||
return ctx
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,18 +268,17 @@ type Background struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Enclosure interface {
|
type Enclosure interface {
|
||||||
Begin(Context) Context
|
Begin(*Context)
|
||||||
End(Context) Context
|
End(*Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Enclose(e Enclosure, ws ...Widget) Widget {
|
func Enclose(e Enclosure, ws ...Widget) Widget {
|
||||||
return NewfWidget(func(ctx Context) Context {
|
return NewfWidget(func(ctx *Context) {
|
||||||
ctx = e.Begin(ctx)
|
e.Begin(ctx)
|
||||||
for _,w := range ws {
|
for _,w := range ws {
|
||||||
ctx = w.Layout(ctx)
|
w.Layout(ctx)
|
||||||
}
|
}
|
||||||
ctx = e.End(ctx)
|
e.End(ctx)
|
||||||
return ctx
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,13 +308,12 @@ func NewBackground(bos ...BackgroundOption) WidgetCombinator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bg *Background) Begin(ctx Context) Context {
|
func (bg *Background) Begin(ctx *Context) {
|
||||||
bg.macro.Record(ctx.ops)
|
bg.macro.Record(ctx.ops)
|
||||||
ctx.cs = bg.Inset.Begin(ctx.c, ctx.ops, ctx.cs)
|
ctx.cs = bg.Inset.Begin(ctx.c, ctx.ops, ctx.cs)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bg *Background) End(ctx Context) Context {
|
func (bg *Background) End(ctx *Context) {
|
||||||
ctx.dims = bg.Inset.End(ctx.dims)
|
ctx.dims = bg.Inset.End(ctx.dims)
|
||||||
bg.macro.Stop()
|
bg.macro.Stop()
|
||||||
//var stack ui.StackOp
|
//var stack ui.StackOp
|
||||||
|
@ -333,16 +325,15 @@ func (bg *Background) End(ctx Context) Context {
|
||||||
if r > h / 2 {
|
if r > h / 2 {
|
||||||
r = h / 2
|
r = h / 2
|
||||||
}
|
}
|
||||||
rrect(ctx.ops, w, h, r, r, r, r)
|
Rrect(ctx.ops, w, h, r, r, r, r)
|
||||||
}
|
}
|
||||||
gdraw.ColorOp{Color: bg.Color}.Add(ctx.ops)
|
gdraw.ColorOp{Color: bg.Color}.Add(ctx.ops)
|
||||||
gdraw.DrawOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(ctx.ops)
|
gdraw.DrawOp{Rect: f32.Rectangle{Max: f32.Point{X: w, Y: h}}}.Add(ctx.ops)
|
||||||
bg.macro.Add(ctx.ops)
|
bg.macro.Add(ctx.ops)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://pomax.github.io/bezierinfo/#circles_cubic.
|
// https://pomax.github.io/bezierinfo/#circles_cubic.
|
||||||
func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) {
|
func Rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) {
|
||||||
w, h := float32(width), float32(height)
|
w, h := float32(width), float32(height)
|
||||||
const c = 0.55228475 // 4*(sqrt(2)-1)/3
|
const c = 0.55228475 // 4*(sqrt(2)-1)/3
|
||||||
var b gdraw.PathBuilder
|
var b gdraw.PathBuilder
|
||||||
|
@ -360,7 +351,7 @@ func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) {
|
||||||
|
|
||||||
type Clickable interface {
|
type Clickable interface {
|
||||||
Widget
|
Widget
|
||||||
Clicked(Context) bool
|
Clicked(*Context) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//cWidget is a clickable Widget that provides the Clicked() method.
|
//cWidget is a clickable Widget that provides the Clicked() method.
|
||||||
|
@ -369,14 +360,13 @@ type cWidget struct {
|
||||||
click *gesture.Click
|
click *gesture.Click
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w cWidget) Layout(ctx Context) Context {
|
func (w cWidget) Layout(ctx *Context) {
|
||||||
ctx = w.w.Layout(ctx)
|
w.w.Layout(ctx)
|
||||||
pointer.RectAreaOp{Size: ctx.dims.Size}.Add(ctx.ops)
|
pointer.RectAreaOp{Size: ctx.dims.Size}.Add(ctx.ops)
|
||||||
w.click.Add(ctx.ops)
|
w.click.Add(ctx.ops)
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w cWidget) Clicked(ctx Context) bool {
|
func (w cWidget) Clicked(ctx *Context) bool {
|
||||||
for _,e := range w.click.Events(ctx.q) {
|
for _,e := range w.click.Events(ctx.q) {
|
||||||
if e.Type == gesture.TypeClick {
|
if e.Type == gesture.TypeClick {
|
||||||
return true
|
return true
|
||||||
|
|
Loading…
Reference in New Issue
Block a user