apps/gophers: convert to new explicit layout API

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-05-16 11:44:30 +02:00
parent e436dce0e7
commit 3e5e9b2df2
2 changed files with 160 additions and 163 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ module gioui.org/apps
go 1.12 go 1.12
require ( require (
gioui.org/ui v0.0.0-20190512182307-c342054dbc08 gioui.org/ui v0.0.0-20190516094417-e436dce0e761
github.com/google/go-github/v24 v24.0.1 github.com/google/go-github/v24 v24.0.1
golang.org/x/exp v0.0.0-20190321205749-f0864edee7f3 golang.org/x/exp v0.0.0-20190321205749-f0864edee7f3
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f
+159 -162
View File
@@ -217,13 +217,13 @@ func (a *App) run() error {
cs := layout.ExactConstraints(a.w.Size()) cs := layout.ExactConstraints(a.w.Size())
a.Layout(ops, cs) a.Layout(ops, cs)
if a.w.Profiling { if a.w.Profiling {
layout.Align( layout.Align{
layout.NE, Alignment: layout.NE,
layout.Margin(a.cfg, W: layout.Insets{
layout.Margins{Top: ui.Dp(16)}, Top: a.cfg.Pixels(ui.Dp(16)),
text.Label{Src: textColor, Face: a.face(fonts.mono, 8), Text: a.w.Timings()}, W: text.Label{Src: textColor, Face: a.face(fonts.mono, 8), Text: a.w.Timings()}.Layout,
), }.Layout,
).Layout(ops, cs) }.Layout(ops, cs)
} }
a.w.Draw(ops) a.w.Draw(ops)
a.w.SetTextInput(a.kqueue.Frame(ops)) a.w.SetTextInput(a.kqueue.Frame(ops))
@@ -413,29 +413,32 @@ func (up *userPage) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
default: default:
} }
for i, ok := l.Init(ops, cs, len(up.commits)); ok; i, ok = l.Index() { for i, ok := l.Init(ops, cs, len(up.commits)); ok; i, ok = l.Index() {
l.Elem(up.commit(i)) l.Elem(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
return up.commit(ops, cs, i)
})
} }
dims := l.Layout() dims := l.Layout()
return dims return dims
} }
func (up *userPage) commit(index int) layout.Widget { func (up *userPage) commit(ops *ui.Ops, cs layout.Constraints, index int) layout.Dimens {
sz := ui.Dp(48)
u := up.user u := up.user
c := up.cfg c := up.cfg
avatar := clipCircle(layout.Sized(c, sz, sz, widget.Image{Src: u.avatar, Rect: u.avatar.Bounds()}))
msg := up.commits[index].GetMessage() msg := up.commits[index].GetMessage()
label := text.Label{Src: textColor, Face: up.faces.For(fonts.regular, ui.Sp(12)), Text: msg} label := text.Label{Src: textColor, Face: up.faces.For(fonts.regular, ui.Sp(12)), Text: msg}
return layout.Margin(c, return layout.Insets{
layout.Margins{Top: ui.Dp(16), Right: ui.Dp(8), Left: ui.Dp(8)}, Top: c.Pixels(ui.Dp(16)), Right: c.Pixels(ui.Dp(8)), Left: c.Pixels(ui.Dp(8)),
layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { W: func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
return (&layout.Flex{Axis: layout.Horizontal, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}). f := layout.Flex{Constraints: cs, Axis: layout.Horizontal, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}
Init(ops, cs). return f.Layout(ops,
Rigid(avatar). f.Rigid(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
Flexible(-1, 1, layout.Fit, layout.Margin(c, layout.Margins{Left: ui.Dp(8)}, label)). sz := c.Pixels(ui.Dp(48))
Layout() return clipCircle(ops, cs, layout.Sized{Width: sz, Height: sz, W: widget.Image{Src: u.avatar, Rect: u.avatar.Bounds()}.Layout}.Layout)
}), }),
) f.Flexible(ops, 1, layout.Fit, layout.Insets{Left: c.Pixels(ui.Dp(8)), W: label.Layout}.Layout),
)
},
}.Layout(ops, cs)
} }
func (up *userPage) fetchCommits(ctx context.Context) { func (up *userPage) fetchCommits(ctx context.Context) {
@@ -462,39 +465,44 @@ func (up *userPage) fetchCommits(ctx context.Context) {
func (a *App) layoutUsers(ops *ui.Ops, cs layout.Constraints) layout.Dimens { func (a *App) layoutUsers(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
c := a.cfg c := a.cfg
a.fab.Update(c, a.pqueue) a.fab.Update(c, a.pqueue)
st := (&layout.Stack{Alignment: layout.Center}).Init(ops, cs). st := layout.Stack{Constraints: cs, Alignment: layout.Center}
Rigid(layout.Align( c2 := st.Rigid(ops, layout.Align{
layout.SE, Alignment: layout.SE,
layout.Margin(c, W: layout.EqualInsets(
layout.EqualMargins(ui.Dp(16)), c.Pixels(ui.Dp(16)),
a.fab, a.fab.Layout,
), ).Layout,
)) }.Layout)
a.edit.Update(c, a.pqueue, a.kqueue) a.edit.Update(c, a.pqueue, a.kqueue)
a.edit2.Update(c, a.pqueue, a.kqueue) a.edit2.Update(c, a.pqueue, a.kqueue)
return st.Expand(0, layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { return st.Layout(ops,
return (&layout.Flex{Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Stretch}).Init(ops, cs). st.Expand(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
Rigid(layout.Margin(c, f := layout.Flex{Constraints: cs, Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Stretch}
layout.EqualMargins(ui.Dp(16)), return f.Layout(ops,
layout.Sized(c, ui.Dp(0), ui.Dp(200), a.edit), f.Rigid(ops, layout.EqualInsets(
)). c.Pixels(ui.Dp(16)),
Rigid(layout.Margin(c, layout.Sized{Width: 0, Height: c.Pixels(ui.Dp(200)), W: a.edit.Layout}.Layout,
layout.Margins{Bottom: ui.Dp(16), Left: ui.Dp(16), Right: ui.Dp(16)}, ).Layout),
a.edit2, f.Rigid(ops, layout.Insets{
)). Bottom: c.Pixels(ui.Dp(16)), Left: c.Pixels(ui.Dp(16)), Right: c.Pixels(ui.Dp(16)),
Rigid(layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { W: a.edit2.Layout,
return (&layout.Stack{Alignment: layout.Center}).Init(ops, cs). }.Layout),
Rigid(layout.Margin(c, f.Rigid(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
layout.Margins{Top: ui.Dp(16), Right: ui.Dp(8), Bottom: ui.Dp(8), Left: ui.Dp(8)}, s := layout.Stack{Constraints: cs, Alignment: layout.Center}
text.Label{Src: rgb(0x888888), Face: a.face(fonts.regular, 9), Text: "GOPHERS"}, c := s.Rigid(ops, layout.Insets{
)). Top: c.Pixels(ui.Dp(16)), Right: c.Pixels(ui.Dp(8)), Bottom: c.Pixels(ui.Dp(8)), Left: c.Pixels(ui.Dp(8)),
Expand(0, fill(rgb(0xf2f2f2))). W: text.Label{Src: rgb(0x888888), Face: a.face(fonts.regular, 9), Text: "GOPHERS"}.Layout,
Layout() }.Layout)
})). return s.Layout(ops,
Flexible(-1, 1, layout.Fit, a.layoutContributors()). s.Expand(ops, fill(rgb(0xf2f2f2)).Layout),
Layout() c,
})). )
Layout() }),
f.Flexible(ops, 1, layout.Fit, a.layoutContributors),
)
}),
c2,
)
} }
func (a *ActionButton) Update(c *ui.Config, q pointer.Events) { func (a *ActionButton) Update(c *ui.Config, q pointer.Events) {
@@ -505,145 +513,134 @@ func (a *ActionButton) Update(c *ui.Config, q pointer.Events) {
func (a *ActionButton) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens { func (a *ActionButton) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
c := a.cfg c := a.cfg
fl := (&layout.Flex{Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.End, MainAxisSize: layout.Min}).Init(ops, cs) f := layout.Flex{Constraints: cs, Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.End, MainAxisSize: layout.Min}
fabCol := brandColor fabCol := brandColor
fl.Rigid(layout.Margin(c, return f.Layout(ops,
layout.Margins{Top: ui.Dp(4)}, f.Rigid(ops, layout.Insets{
layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { Top: c.Pixels(ui.Dp(4)),
dims := fab(c, a.sendIco.image(c), fabCol, ui.Dp(56)).Layout(ops, cs) W: func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
a.btnClicker.Op(ops, &gesture.Ellipse{dims.Size}) dims := fab(ops, cs, a.sendIco.image(c), fabCol, c.Pixels(ui.Dp(56)))
return dims a.btnClicker.Op(ops, &gesture.Ellipse{dims.Size})
}), return dims
)) },
return fl.Layout() }.Layout),
)
} }
func (a *App) layoutContributors() layout.Widget { func (a *App) layoutContributors(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
return layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { c := a.cfg
c := a.cfg l := a.usersList
l := a.usersList l.Scroll(c, a.pqueue)
l.Scroll(c, a.pqueue) if l.Dragging() {
if l.Dragging() { key.OpHideInput{}.Add(ops)
key.OpHideInput{}.Add(ops) }
} for i, ok := l.Init(ops, cs, len(a.users)); ok; i, ok = l.Index() {
for i, ok := l.Init(ops, cs, len(a.users)); ok; i, ok = l.Index() { l.Elem(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
l.Elem(a.user(c, i)) return a.user(ops, cs, c, i)
} })
dims := l.Layout() }
return dims dims := l.Layout()
}) return dims
} }
func (a *App) user(c *ui.Config, index int) layout.Widget { func (a *App) user(ops *ui.Ops, cs layout.Constraints, c *ui.Config, index int) layout.Dimens {
u := a.users[index] u := a.users[index]
click := &a.userClicks[index] click := &a.userClicks[index]
sz := ui.Dp(48)
for _, r := range click.Update(a.pqueue) { for _, r := range click.Update(a.pqueue) {
if r.Type == gesture.TypeClick { if r.Type == gesture.TypeClick {
a.selectedUser = newUserPage(a.ctx, u, a.w.Redraw, a.faces) a.selectedUser = newUserPage(a.ctx, u, a.w.Redraw, a.faces)
} }
} }
avatar := clipCircle(layout.Sized(a.cfg, sz, sz, widget.Image{Src: u.avatar, Rect: u.avatar.Bounds()})) elem := layout.Flex{Constraints: cs, Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}
return layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { return elem.Layout(ops,
elem := (&layout.Flex{Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}).Init(ops, cs) elem.Rigid(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
elem.Rigid(layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { dims := layout.EqualInsets(
dims := layout.Margin(c, c.Pixels(ui.Dp(8)),
layout.EqualMargins(ui.Dp(8)), func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { f := centerRowOpts(cs)
return centerRowOpts().Init(ops, cs). return f.Layout(ops,
Rigid(layout.Margin(c, layout.Margins{Right: ui.Dp(8)}, avatar)). f.Rigid(ops, layout.Insets{Right: c.Pixels(ui.Dp(8)), W: func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
Rigid(column( sz := c.Pixels(ui.Dp(48))
baseline( return clipCircle(ops, cs, layout.Sized{Width: sz, Height: sz, W: widget.Image{Src: u.avatar, Rect: u.avatar.Bounds()}.Layout}.Layout)
text.Label{Src: textColor, Face: a.face(fonts.regular, 11), Text: u.name}, }}.Layout),
layout.Align(layout.E, layout.Margin(c, f.Rigid(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
layout.Margins{Left: ui.Dp(2)}, f := column(cs)
text.Label{Src: textColor, Face: a.face(fonts.regular, 8), Text: "3 hours ago"}, return f.Layout(ops,
)), f.Rigid(ops, func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
), f := baseline(cs)
layout.Margin(c, return f.Layout(ops,
layout.Margins{Top: ui.Dp(4)}, f.Rigid(ops, text.Label{Src: textColor, Face: a.face(fonts.regular, 11), Text: u.name}.Layout),
text.Label{Src: tertTextColor, Face: a.face(fonts.regular, 10), Text: u.company}, f.Rigid(ops,
), layout.Align{
)). Alignment: layout.E,
Layout() W: layout.Insets{
}), Left: c.Pixels(ui.Dp(2)),
W: text.Label{Src: textColor, Face: a.face(fonts.regular, 8), Text: "3 hours ago"}.Layout,
}.Layout,
}.Layout,
),
)
}),
f.Rigid(ops, layout.Insets{
Top: c.Pixels(ui.Dp(4)),
W: text.Label{Src: tertTextColor, Face: a.face(fonts.regular, 10), Text: u.company}.Layout,
}.Layout),
)
}),
)
},
).Layout(ops, cs) ).Layout(ops, cs)
click.Op(ops, &gesture.Rect{dims.Size}) click.Op(ops, &gesture.Rect{dims.Size})
return dims return dims
})) }))
return elem.Layout()
})
} }
func fill(img image.Image) layout.Widget { func fill(img image.Image) widget.Image {
return widget.Image{Src: img, Rect: image.Rectangle{Max: image.Point{X: 1, Y: 1}}} return widget.Image{Src: img, Rect: image.Rectangle{Max: image.Point{X: 1, Y: 1}}}
} }
func column(widgets ...layout.Widget) layout.Widget { func column(cs layout.Constraints) layout.Flex {
return flex(&layout.Flex{Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}, widgets...) return layout.Flex{Constraints: cs, Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Start}
} }
func centerColumn(widgets ...layout.Widget) layout.Widget { func centerRowOpts(cs layout.Constraints) layout.Flex {
return flex(&layout.Flex{Axis: layout.Vertical, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Center, MainAxisSize: layout.Min}, widgets...) return layout.Flex{Constraints: cs, Axis: layout.Horizontal, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Center, MainAxisSize: layout.Min}
} }
func centerRowOpts(widgets ...layout.Widget) *layout.Flex { func baseline(cs layout.Constraints) layout.Flex {
return &layout.Flex{Axis: layout.Horizontal, MainAxisAlignment: layout.Start, CrossAxisAlignment: layout.Center, MainAxisSize: layout.Min} return layout.Flex{Constraints: cs, Axis: layout.Horizontal, CrossAxisAlignment: layout.Baseline, MainAxisSize: layout.Min}
} }
func centerRow(widgets ...layout.Widget) layout.Widget { func clipCircle(ops *ui.Ops, cs layout.Constraints, w layout.Widget) layout.Dimens {
return flex(centerRowOpts(), widgets...) ops.Begin()
dims := w(ops, cs)
block := ops.End()
max := dims.Size.X
if dy := dims.Size.Y; dy > max {
max = dy
}
szf := float32(max)
rr := szf * .5
ops.Begin()
gdraw.OpClip{Path: rrect(szf, szf, rr, rr, rr, rr)}.Add(ops)
block.Add(ops)
ops.End().Add(ops)
return dims
} }
func baseline(widgets ...layout.Widget) layout.Widget { func fab(ops *ui.Ops, cs layout.Constraints, ico, col image.Image, size float32) layout.Dimens {
return flex(&layout.Flex{Axis: layout.Horizontal, CrossAxisAlignment: layout.Baseline, MainAxisSize: layout.Min}, widgets...) sz := int(size + .5)
} rr := size * .5
dp := image.Point{X: (sz - ico.Bounds().Dx()) / 2, Y: (sz - ico.Bounds().Dy()) / 2}
func flex(f *layout.Flex, widgets ...layout.Widget) layout.Widget { dims := image.Point{X: sz, Y: sz}
return layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens { gdraw.OpClip{Path: rrect(size, size, rr, rr, rr, rr)}.Add(ops)
f.Init(ops, cs) gdraw.OpImage{Rect: f32.Rectangle{Max: f32.Point{X: float32(sz), Y: float32(sz)}}, Src: col, SrcRect: col.Bounds()}.Add(ops)
for _, w := range widgets { gdraw.OpImage{
f.Rigid(w) Rect: toRectF(ico.Bounds().Add(dp)),
} Src: ico,
return f.Layout() SrcRect: ico.Bounds(),
}) }.Add(ops)
} return layout.Dimens{Size: dims}
func clipCircle(w layout.Widget) layout.Widget {
return layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
ops.Begin()
dims := w.Layout(ops, cs)
block := ops.End()
max := dims.Size.X
if dy := dims.Size.Y; dy > max {
max = dy
}
szf := float32(max)
rr := szf * .5
ops.Begin()
gdraw.OpClip{Path: rrect(szf, szf, rr, rr, rr, rr)}.Add(ops)
block.Add(ops)
ops.End().Add(ops)
return dims
})
}
func fab(c *ui.Config, ico, col image.Image, size ui.Value) layout.Widget {
return layout.F(func(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
szf := c.Pixels(size)
sz := int(szf + .5)
rr := szf * .5
dp := image.Point{X: (sz - ico.Bounds().Dx()) / 2, Y: (sz - ico.Bounds().Dy()) / 2}
dims := image.Point{X: sz, Y: sz}
gdraw.OpClip{Path: rrect(szf, szf, rr, rr, rr, rr)}.Add(ops)
gdraw.OpImage{Rect: f32.Rectangle{Max: f32.Point{X: float32(sz), Y: float32(sz)}}, Src: col, SrcRect: col.Bounds()}.Add(ops)
gdraw.OpImage{
Rect: toRectF(ico.Bounds().Add(dp)),
Src: ico,
SrcRect: ico.Bounds(),
}.Add(ops)
return layout.Dimens{Size: dims}
})
} }
func toRectF(r image.Rectangle) f32.Rectangle { func toRectF(r image.Rectangle) f32.Rectangle {