966f1abb7672a3e13a6f0abc60205054f588718b — Elias Naur 4 months ago 91079f1
cmd/scatter: handle safe area insets correctly

Signed-off-by: Elias Naur <mail@eliasnaur.com>
3 files changed, 69 insertions(+), 82 deletions(-)

M cmd/scatter/ui.go
M go.mod
M go.sum
M cmd/scatter/ui.go => cmd/scatter/ui.go +62 -75
@@ 40,7 40,7 @@ import (
 )
 
 type Env struct {
-	insets app.Insets
+	insets layout.Inset
 	faces  measure.Faces
 	client *Client
 	redraw func()


@@ 91,13 91,12 @@ type Button struct {
 type Topbar struct {
 	Back bool
 
+	ops       *ui.Ops
 	backClick gesture.Click
 	stack     layout.Stack
 	insets    layout.Inset
-	insets2   layout.Inset
 	flex      layout.Flex
 	backChild layout.FlexChild
-	bg        layout.StackChild
 }
 
 type formField struct {


@@ 282,7 281,12 @@ func (a *App) run() error {
 				cfg = &e.Config
 				q := a.w.Queue()
 				a.env.faces.Reset(cfg)
-				a.env.insets = e.Insets
+				a.env.insets = layout.Inset{
+					Top:    e.Insets.Top,
+					Left:   e.Insets.Left,
+					Right:  e.Insets.Right,
+					Bottom: e.Insets.Bottom,
+				}
 				cs := layout.RigidConstraints(e.Size)
 				a.Layout(cfg, q, ops, cs)
 				if a.profiling {


@@ 451,13 455,11 @@ func (a *App) layoutTimings(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.C
 	a.lastMallocs = mstats.Mallocs
 	al := layout.Align{Alignment: layout.NE}
 	cs = al.Begin(ops, cs)
-	in := layout.Inset{Top: ui.Dp(16)}
-	in2 := layout.Inset{Top: a.env.insets.Top}
+	in := a.env.insets
+	in.Top = ui.Max(c, ui.Dp(16), in.Top)
 	cs = in.Begin(c, ops, cs)
-	cs = in2.Begin(c, ops, cs)
 	txt := fmt.Sprintf("m: %d %s", mallocs, a.profile.Timings)
 	dims := text.Label{Material: theme.text, Face: a.env.faces.For(fonts.mono, ui.Sp(10)), Text: txt}.Layout(ops, cs)
-	dims = in2.End(dims)
 	dims = in.End(dims)
 	return al.End(dims)
 }


@@ 560,24 562,23 @@ func (p *contactsPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout
 	c1 := f.End(dims)
 	cs = f.Flexible(1)
 	cs.Height.Min = cs.Height.Max
-	sysInset := layout.Inset{
-		Left:  p.env.insets.Left,
-		Right: p.env.insets.Right,
-	}
-	cs = sysInset.Begin(c, ops, cs)
 	for l.Init(c, q, ops, cs, len(p.contacts)); l.More(); l.Next() {
 		l.End(p.contact(c, ops, l.Constraints(), l.Index()))
 	}
 	dims = l.Layout()
-	dims = sysInset.End(dims)
 	c2 := f.End(dims)
 	return f.Layout(c1, c2)
 }
 
 func (p *contactsPage) contact(c ui.Config, ops *ui.Ops, cs layout.Constraints, index int) layout.Dimensions {
+	in := layout.Inset{
+		Top:    ui.Dp(16),
+		Bottom: ui.Dp(16),
+		Left:   ui.Max(c, ui.Dp(16), p.env.insets.Left),
+		Right:  ui.Max(c, ui.Dp(16), p.env.insets.Right),
+	}
 	contact := p.contacts[index]
 	click := &p.clicks[index]
-	in := layout.UniformInset(ui.Dp(8))
 	cs = in.Begin(c, ops, cs)
 	f := (&layout.Flex{Alignment: layout.Middle}).Init(ops, cs)
 	var dims layout.Dimensions


@@ 614,50 615,42 @@ func (t *Topbar) Event(c ui.Config, q input.Queue) interface{} {
 	return nil
 }
 
-func (t *Topbar) Begin(c ui.Config, insets app.Insets, ops *ui.Ops, cs layout.Constraints) layout.Constraints {
-	t.stack = layout.Stack{Alignment: layout.W}
-	topInset := insets.Top
-	t.insets2 = layout.Inset{
-		Top:   topInset,
-		Left:  insets.Left,
-		Right: insets.Right,
+func (t *Topbar) Begin(c ui.Config, insets layout.Inset, ops *ui.Ops, cs layout.Constraints) layout.Constraints {
+	t.ops = ops
+	t.stack = layout.Stack{Alignment: layout.SW}
+	t.insets = layout.Inset{
+		Top:    ui.Add(c, insets.Top, ui.Dp(16)),
+		Bottom: ui.Dp(16),
+		Left:   ui.Max(c, insets.Left, ui.Dp(16)),
+		Right:  ui.Max(c, insets.Right, ui.Dp(16)),
 	}
 	t.stack.Init(ops, cs)
 	cs = t.stack.Rigid()
-	if h := c.Px(topInset) + c.Px(ui.Dp(56)); h < cs.Height.Max {
-		cs.Height.Max = h
-	}
-	dims := fill{theme.brand}.Layout(ops, cs)
-	t.bg = t.stack.End(dims)
-	cs = t.stack.Rigid()
-	cs = t.insets2.Begin(c, ops, cs)
+	cs = t.insets.Begin(c, ops, cs)
 	t.flex = layout.Flex{Alignment: layout.Middle}
 	t.flex.Init(ops, cs)
 	cs = t.flex.Rigid()
-	dims = layout.Dimensions{}
-	t.insets = layout.UniformInset(ui.Dp(12))
+	dims := layout.Dimensions{}
 	if t.Back {
-		t.insets.Left = ui.Px(0)
 		ico := (&icon{src: icons.NavigationArrowBack, size: ui.Dp(24)}).image(c, rgb(0xffffff))
-		in := layout.UniformInset(ui.Dp(8))
-		cs = in.Begin(c, ops, cs)
 		dims = widget.Image{Src: ico, Rect: ico.Bounds(), Scale: 1}.Layout(c, ops, cs)
-		dims = in.End(dims)
+		dims.Size.X += c.Px(ui.Dp(4))
 		pointer.RectAreaOp{Rect: image.Rectangle{Max: dims.Size}}.Add(ops)
 		t.backClick.Add(ops)
 	}
 	t.backChild = t.flex.End(dims)
-	cs = t.flex.Flexible(1)
-	return t.insets.Begin(c, ops, cs)
+	return t.flex.Flexible(1)
 }
 
 func (t *Topbar) End(dims layout.Dimensions) layout.Dimensions {
-	dims = t.insets.End(dims)
 	content := t.flex.End(dims)
 	dims = t.flex.Layout(t.backChild, content)
-	dims = t.insets2.End(dims)
+	dims = t.insets.End(dims)
 	stackContent := t.stack.End(dims)
-	return t.stack.Layout(t.bg, stackContent)
+	cs := t.stack.Expand()
+	dims = fill{theme.brand}.Layout(t.ops, cs)
+	bg := t.stack.End(dims)
+	return t.stack.Layout(bg, stackContent)
 }
 
 func newSignInPage(env *Env) *signInPage {


@@ 726,22 719,19 @@ func (p *signInPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.C
 	c1 := f.End(dims)
 
 	cs = f.Flexible(1)
-	sysInset := layout.Inset{
-		Left:   p.env.insets.Left,
-		Right:  p.env.insets.Right,
-		Bottom: p.env.insets.Bottom,
-	}
-	cs = sysInset.Begin(c, ops, cs)
 	dims = p.layoutSigninForm(c, q, ops, cs)
-	dims = sysInset.End(dims)
 	c2 := f.End(dims)
 	return f.Layout(c1, c2)
 }
 
 func (p *signInPage) layoutSigninForm(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.Constraints) layout.Dimensions {
 	l := p.list
+	inset := layout.Inset{
+		Left:  ui.Max(c, ui.Dp(32), p.env.insets.Left),
+		Right: ui.Max(c, ui.Dp(32), p.env.insets.Right),
+	}
 	for l.Init(c, q, ops, cs, len(p.fields)+1); l.More(); l.Next() {
-		in := layout.Inset{Left: ui.Dp(32), Right: ui.Dp(32)}
+		in := inset
 		var dims layout.Dimensions
 		switch {
 		case l.Index() < len(p.fields):


@@ 753,7 743,7 @@ func (p *signInPage) layoutSigninForm(c ui.Config, q input.Queue, ops *ui.Ops, c
 			dims = p.fields[l.Index()].Layout(c, q, ops, cs)
 			dims = in.End(dims)
 		default:
-			in.Bottom = ui.Dp(32)
+			in.Bottom = ui.Max(c, ui.Dp(32), p.env.insets.Bottom)
 			align := layout.Align{Alignment: layout.E}
 			cs = in.Begin(c, ops, align.Begin(ops, cs))
 			dims = p.submit.Layout(c, p.env, ops, cs)


@@ 904,10 894,6 @@ func (p *threadsPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.
 	st := layout.Stack{Alignment: layout.Center}
 	st.Init(ops, cs)
 
-	sysInset := layout.Inset{
-		Left:  p.env.insets.Left,
-		Right: p.env.insets.Right,
-	}
 	var dims layout.Dimensions
 	cs = st.Rigid()
 	{


@@ 924,22 910,20 @@ func (p *threadsPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.
 		c3 := f.End(dims)
 
 		cs = f.Flexible(1)
-		cs = sysInset.Begin(c, ops, cs)
 		dims = p.layoutThreads(c, q, ops, cs)
-		dims = sysInset.End(dims)
 		c4 := f.End(dims)
 		dims = f.Layout(c3, c4)
 	}
 	c1 := st.End(dims)
 	cs = st.Rigid()
-	sysInset.Bottom = p.env.insets.Bottom
-	cs = sysInset.Begin(c, ops, cs)
 	al := layout.Align{Alignment: layout.SE}
-	in := layout.UniformInset(ui.Dp(16))
+	in := layout.Inset{
+		Right:  ui.Max(c, ui.Dp(16), p.env.insets.Right),
+		Bottom: ui.Max(c, ui.Dp(16), p.env.insets.Bottom),
+	}
 	cs = in.Begin(c, ops, al.Begin(ops, cs))
 	dims = p.fab.Layout(c, p.env, ops, cs)
 	dims = al.End(in.End(dims))
-	dims = sysInset.End(dims)
 	c2 := st.End(dims)
 	dims = st.Layout(c1, c2)
 	return dims


@@ 956,7 940,7 @@ func (p *threadsPage) layoutThreads(c ui.Config, q input.Queue, ops *ui.Ops, cs 
 		case 0:
 			in.Top = ui.Dp(4)
 		case len(p.threads) - 1:
-			in.Bottom = ui.Dp(4)
+			in.Bottom = ui.Max(c, ui.Dp(4), p.env.insets.Bottom)
 		}
 		cs := in.Begin(c, ops, l.Constraints())
 		dims := p.thread(c, ops, cs, l.Index())


@@ 985,18 969,23 @@ func (p *threadsPage) thread(c ui.Config, ops *ui.Ops, cs layout.Constraints, in
 		font = fonts.bold
 	}
 	click := &p.clicks[index]
+	in := layout.Inset{
+		Left:  ui.Max(c, ui.Dp(16), p.env.insets.Left),
+		Right: ui.Max(c, ui.Dp(16), p.env.insets.Right),
+	}
+	cs = in.Begin(c, ops, cs)
 	elem := layout.Flex{Axis: layout.Vertical}
 	elem.Init(ops, cs)
 	cs = elem.Rigid()
 	var dims layout.Dimensions
 	{
-		in := layout.UniformInset(ui.Dp(8))
+		in := layout.Inset{Top: ui.Dp(8), Bottom: ui.Dp(8)}
 		cs = in.Begin(c, ops, cs)
 		f := centerRowOpts()
 		f.Init(ops, cs)
 		cs = f.Rigid()
 		{
-			in := layout.Inset{Right: ui.Dp(12), Left: ui.Dp(4)}
+			in := layout.Inset{Right: ui.Dp(12)}
 			cc := clipCircle{}
 			cs = cc.Begin(ops, in.Begin(c, ops, cs))
 


@@ 1069,7 1058,8 @@ func (p *threadsPage) thread(c ui.Config, ops *ui.Ops, cs layout.Constraints, in
 		click.Add(ops)
 	}
 	c1 := elem.End(dims)
-	return elem.Layout(c1)
+	dims = elem.Layout(c1)
+	return in.End(dims)
 }
 
 func newThreadPage(env *Env, threadID string) *threadPage {


@@ 1180,16 1170,14 @@ func (p *threadPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.C
 	}
 	c1 := f.End(dims)
 
-	sysInset := layout.Inset{
-		Left:  p.env.insets.Left,
-		Right: p.env.insets.Right,
-	}
 	{
 		cs = f.Rigid()
-		sysInset := sysInset
-		sysInset.Bottom = p.env.insets.Bottom
-		cs = sysInset.Begin(c, ops, cs)
-		in := layout.Inset{Top: ui.Dp(8), Bottom: ui.Dp(8), Left: ui.Dp(12), Right: ui.Dp(12)}
+		in := layout.Inset{
+			Top:    ui.Dp(16),
+			Left:   ui.Max(c, ui.Dp(16), p.env.insets.Left),
+			Right:  ui.Max(c, ui.Dp(16), p.env.insets.Right),
+			Bottom: ui.Max(c, ui.Dp(16), p.env.insets.Bottom),
+		}
 		cs = in.Begin(c, ops, cs)
 		switch {
 		case p.thread.PendingInvitation:


@@ 1200,19 1188,16 @@ func (p *threadPage) Layout(c ui.Config, q input.Queue, ops *ui.Ops, cs layout.C
 			dims = p.invite.Layout(c, p.env, ops, cs)
 		}
 		dims = in.End(dims)
-		dims = sysInset.End(dims)
 	}
 	c3 := f.End(dims)
 
 	{
 		cs = f.Flexible(1)
 		cs.Height.Min = cs.Height.Max
-		cs = sysInset.Begin(c, ops, cs)
 		for l.Init(c, q, ops, cs, len(p.messages)); l.More(); l.Next() {
 			l.End(p.message(c, ops, l.Constraints(), l.Index()))
 		}
 		dims = l.Layout()
-		dims = sysInset.End(dims)
 	}
 	c2 := f.End(dims)
 	return f.Layout(c1, c2, c3)


@@ 1262,7 1247,7 @@ func (p *threadPage) layoutMessageBox(c ui.Config, q input.Queue, ops *ui.Ops, c
 func (p *threadPage) message(c ui.Config, ops *ui.Ops, cs layout.Constraints, index int) layout.Dimensions {
 	msg := p.messages[index]
 	var dims layout.Dimensions
-	in := layout.Inset{Top: ui.Dp(8), Left: ui.Dp(8), Right: ui.Dp(40)}
+	in := layout.Inset{Top: ui.Dp(16), Left: ui.Dp(16), Right: ui.Dp(40)}
 	align := layout.Align{Alignment: layout.W}
 	msgMat := colorMaterial(ops, rgb(0xffffff))
 	bgcol := theme.brand


@@ 1274,6 1259,8 @@ func (p *threadPage) message(c ui.Config, ops *ui.Ops, cs layout.Constraints, in
 		msgMat = theme.text
 		timecol = rgb(0x888888)
 	}
+	in.Left = ui.Max(c, in.Left, p.env.insets.Left)
+	in.Right = ui.Max(c, in.Right, p.env.insets.Right)
 	cs = in.Begin(c, ops, cs)
 	{
 		cs = align.Begin(ops, cs)

M go.mod => go.mod +2 -2
@@ 3,8 3,8 @@ module scatter.im
 go 1.13
 
 require (
-	gioui.org/cmd v0.0.0-20190905191537-6ad154419b2d // indirect
-	gioui.org/ui v0.0.0-20190905191537-6ad154419b2d
+	gioui.org/cmd v0.0.0-20190909155013-86b231ca287a // indirect
+	gioui.org/ui v0.0.0-20190909155013-86b231ca287a
 	github.com/eliasnaur/libsignal-protocol-go v0.0.0-20190626062856-3295f72b181e
 	github.com/emersion/go-imap v1.0.0-rc.1
 	github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e

M go.sum => go.sum +5 -5
@@ 1,8 1,8 @@
-gioui.org v0.0.0-20190905191537-6ad154419b2d h1:lSY1y1oKdLzz8VLzYoyxeKj0B5uSf8/AW4KdLq/RUtQ=
-gioui.org/cmd v0.0.0-20190905191537-6ad154419b2d h1:TSoXJ1a0DFWmDhP6VbwdFbry2N6tp3o0T4+tStesPNU=
-gioui.org/cmd v0.0.0-20190905191537-6ad154419b2d/go.mod h1:rn6q3wa1gugvIB50m0JxJ4krKQAfc0sQHJKvsuZW8+c=
-gioui.org/ui v0.0.0-20190905191537-6ad154419b2d h1:7MAtTBqpH81f7iHtVi1etPXQEpaQWuSO6LM0TTW8Who=
-gioui.org/ui v0.0.0-20190905191537-6ad154419b2d/go.mod h1:dmqi7kyK1sZ03VAkEqzKKpKWfRPCuqrd1eDtSqyhjzY=
+gioui.org v0.0.0-20190909155013-86b231ca287a h1:Fn4zEWPu9exc2GE1SFTEpJNRXnFXqueLoAm8/ZCWubA=
+gioui.org/cmd v0.0.0-20190909155013-86b231ca287a h1:7H7HPKR+9wPQTgYzRuR9v8nDYwzHmM/oetPfPwuOo8w=
+gioui.org/cmd v0.0.0-20190909155013-86b231ca287a/go.mod h1:kMRzpXdArk4aGFuBDFqLSax2l/DhbnFWtEZJOPmrAX0=
+gioui.org/ui v0.0.0-20190909155013-86b231ca287a h1:hMJBZPoLd5pxH+mtuqAgE6WB23U9h0YVChHWDcm+KYc=
+gioui.org/ui v0.0.0-20190909155013-86b231ca287a/go.mod h1:PssKPKlqVIeyaed+0w492Xc2NgX5M3n6oZKOAj5rxoE=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/RadicalApp/complete v0.0.0-20170329192659-17e6c0ee499b h1:cAULFohNVfNzco0flF4okSPg3s7/tCj+hMIldtYZo4c=
 github.com/RadicalApp/complete v0.0.0-20170329192659-17e6c0ee499b/go.mod h1:zZ3+l0EkpT2ZPnoamPBG50PBUtQrXwwyJ6elQZMmqgk=