~eliasnaur/scatter

923af691545357b4c6487e06177e79e9a3ae47e6 — Elias Naur 6 months ago 376080b
cmd/scatter: change *layout.Context variables from `c` to `gtx`

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

M cmd/scatter/ui.go
M cmd/scatter/ui.go => cmd/scatter/ui.go +233 -233
@@ 70,8 70,8 @@ type pageStack struct {

type Page interface {
	Start(stop <-chan struct{})
	Event(c *layout.Context) interface{}
	Layout(c *layout.Context)
	Event(gtx *layout.Context) interface{}
	Layout(gtx *layout.Context)
}

type signInPage struct {


@@ 223,7 223,7 @@ func colorMaterial(ops *ui.Ops, color color.RGBA) ui.MacroOp {

func (a *App) run() error {
	var updates <-chan struct{}
	c := &layout.Context{
	gtx := &layout.Context{
		Queue: a.w.Queue(),
	}
	for {


@@ 272,19 272,19 @@ func (a *App) run() error {
					}
				}
			case app.UpdateEvent:
				c.Reset(&e.Config, layout.RigidConstraints(e.Size))
				a.env.faces.Reset(c)
				gtx.Reset(&e.Config, layout.RigidConstraints(e.Size))
				a.env.faces.Reset(gtx)
				a.env.insets = layout.Inset{
					Top:    e.Insets.Top,
					Left:   e.Insets.Left,
					Right:  e.Insets.Right,
					Bottom: e.Insets.Bottom,
				}
				a.Layout(c)
				a.Layout(gtx)
				if a.profiling {
					a.layoutTimings(c)
					a.layoutTimings(gtx)
				}
				a.w.Update(c.Ops)
				a.w.Update(gtx.Ops)
			}
		}
	}


@@ 302,24 302,24 @@ func (t *Transition) Start(stop <-chan struct{}) {
	t.page.Start(stop)
}

func (t *Transition) Event(c *layout.Context) interface{} {
	return t.page.Event(c)
func (t *Transition) Event(gtx *layout.Context) interface{} {
	return t.page.Event(gtx)
}

func (t *Transition) Layout(c *layout.Context) {
func (t *Transition) Layout(gtx *layout.Context) {
	var stack ui.StackOp
	stack.Push(c.Ops)
	stack.Push(gtx.Ops)
	prev, page := t.prev, t.page
	if prev != nil {
		if t.reverse {
			prev, page = page, prev
		}
		now := c.Now()
		now := gtx.Now()
		if t.time.IsZero() {
			t.time = now
		}
		prev.Layout(c)
		cs := c.Constraints
		prev.Layout(gtx)
		cs := gtx.Constraints
		size := f32.Point{X: float32(cs.Width.Max), Y: float32(cs.Height.Max)}
		max := float32(math.Sqrt(float64(size.X*size.X + size.Y*size.Y)))
		progress := float32(now.Sub(t.time).Seconds()) * 3


@@ 333,16 333,16 @@ func (t *Transition) Layout(c *layout.Context) {
		}
		diameter := progress * max
		radius := diameter / 2
		ui.InvalidateOp{}.Add(c.Ops)
		ui.InvalidateOp{}.Add(gtx.Ops)
		center := size.Mul(.5)
		clipCenter := f32.Point{X: diameter / 2, Y: diameter / 2}
		off := ui.TransformOp{}.Offset(center.Sub(clipCenter))
		off.Add(c.Ops)
		rrect(c.Ops, diameter, diameter, radius, radius, radius, radius)
		off.Invert().Add(c.Ops)
		fill{theme.white}.Layout(c)
		off.Add(gtx.Ops)
		rrect(gtx.Ops, diameter, diameter, radius, radius, radius, radius)
		off.Invert().Add(gtx.Ops)
		fill{theme.white}.Layout(gtx)
	}
	page.Layout(c)
	page.Layout(gtx)
	stack.Pop()
}



@@ 427,29 427,29 @@ func argb(c uint32) color.RGBA {
	return color.RGBA{A: uint8(c >> 24), R: uint8(c >> 16), G: uint8(c >> 8), B: uint8(c)}
}

func (a *App) Layout(c *layout.Context) {
	a.update(c)
	a.stack.Current().Layout(c)
func (a *App) Layout(gtx *layout.Context) {
	a.update(gtx)
	a.stack.Current().Layout(gtx)
}

func (a *App) layoutTimings(c *layout.Context) {
	for e, ok := c.Next(a); ok; e, ok = c.Next(a) {
func (a *App) layoutTimings(gtx *layout.Context) {
	for e, ok := gtx.Next(a); ok; e, ok = gtx.Next(a) {
		if e, ok := e.(system.ProfileEvent); ok {
			a.profile = e
		}
	}

	system.ProfileOp{Key: a}.Add(c.Ops)
	system.ProfileOp{Key: a}.Add(gtx.Ops)
	var mstats runtime.MemStats
	runtime.ReadMemStats(&mstats)
	mallocs := mstats.Mallocs - a.lastMallocs
	a.lastMallocs = mstats.Mallocs
	layout.Align(layout.NE).Layout(c, func() {
	layout.Align(layout.NE).Layout(gtx, func() {
		in := a.env.insets
		in.Top = ui.Max(c, ui.Dp(16), in.Top)
		in.Layout(c, func() {
		in.Top = ui.Max(gtx, ui.Dp(16), in.Top)
		in.Layout(gtx, func() {
			txt := fmt.Sprintf("m: %d %s", mallocs, a.profile.Timings)
			text.Label{Material: theme.text, Face: a.env.faces.For(fonts.mono, ui.Sp(10)), Text: txt}.Layout(c)
			text.Label{Material: theme.text, Face: a.env.faces.For(fonts.mono, ui.Sp(10)), Text: txt}.Layout(gtx)
		})
	})
}


@@ 478,9 478,9 @@ func newContactsPage(env *Env) *contactsPage {

func (p *contactsPage) Start(stop <-chan struct{}) {}

func (p *contactsPage) Event(c *layout.Context) interface{} {
func (p *contactsPage) Event(gtx *layout.Context) interface{} {
	for {
		e, ok := p.searchEdit.Next(c)
		e, ok := p.searchEdit.Next(gtx)
		if !ok {
			break
		}


@@ 499,13 499,13 @@ func (p *contactsPage) Event(c *layout.Context) interface{} {
	default:
	}
	for i := range p.clicks {
		for e, ok := p.clicks[i].Next(c); ok; e, ok = p.clicks[i].Next(c) {
		for e, ok := p.clicks[i].Next(gtx); ok; e, ok = p.clicks[i].Next(gtx) {
			if e.Type == gesture.TypeClick {
				return NewThreadEvent{p.contacts[i].Address}
			}
		}
	}
	return p.topbar.Event(c)
	return p.topbar.Event(gtx)
}

func isEmailAddress(e string) bool {


@@ 533,63 533,63 @@ func (p *contactsPage) queryContacts(q string) {
	}()
}

func (p *contactsPage) Layout(c *layout.Context) {
	for e := p.Event(c); e != nil; e = p.Event(c) {
func (p *contactsPage) Layout(gtx *layout.Context) {
	for e := p.Event(gtx); e != nil; e = p.Event(gtx) {
	}
	l := p.list
	if l.Dragging() {
		key.HideInputOp{}.Add(c.Ops)
		key.HideInputOp{}.Add(gtx.Ops)
	}
	f := layout.Flex{Axis: layout.Vertical}
	f.Init(c)
	f.Init(gtx)
	c1 := f.Rigid(func() {
		p.topbar.Layout(c, p.env.insets, func() {
			p.searchEdit.Layout(c)
		p.topbar.Layout(gtx, p.env.insets, func() {
			p.searchEdit.Layout(gtx)
		})
	})
	c2 := f.Flexible(1, func() {
		c.Constraints.Height.Min = c.Constraints.Height.Max
		l.Layout(c, len(p.contacts), func(i int) {
			p.contact(c, i)
		gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
		l.Layout(gtx, len(p.contacts), func(i int) {
			p.contact(gtx, i)
		})
	})
	f.Layout(c1, c2)
}

func (p *contactsPage) contact(c *layout.Context, index int) {
func (p *contactsPage) contact(gtx *layout.Context, index int) {
	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),
		Left:   ui.Max(gtx, ui.Dp(16), p.env.insets.Left),
		Right:  ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
	}
	contact := p.contacts[index]
	click := &p.clicks[index]
	in.Layout(c, func() {
		f := (&layout.Flex{Alignment: layout.Middle}).Init(c)
	in.Layout(gtx, func() {
		f := (&layout.Flex{Alignment: layout.Middle}).Init(gtx)
		c1 := f.Rigid(func() {
			in := layout.Inset{Right: ui.Dp(8)}
			in.Layout(c, func() {
			in.Layout(gtx, func() {
				cc := clipCircle{}
				cc.Layout(c, func() {
					sz := image.Point{X: c.Px(ui.Dp(48)), Y: c.Px(ui.Dp(48))}
					c.Constraints = layout.RigidConstraints(c.Constraints.Constrain(sz))
					fill{theme.brand}.Layout(c)
				cc.Layout(gtx, func() {
					sz := image.Point{X: gtx.Px(ui.Dp(48)), Y: gtx.Px(ui.Dp(48))}
					gtx.Constraints = layout.RigidConstraints(gtx.Constraints.Constrain(sz))
					fill{theme.brand}.Layout(gtx)
				})
			})
		})
		c2 := f.Flexible(1, func() {
			text.Label{Material: theme.text, Face: p.env.faces.For(fonts.regular, ui.Sp(18)), Text: contact.Address}.Layout(c)
			text.Label{Material: theme.text, Face: p.env.faces.For(fonts.regular, ui.Sp(18)), Text: contact.Address}.Layout(gtx)
		})

		f.Layout(c1, c2)
	})
	pointer.RectAreaOp{Rect: image.Rectangle{Max: c.Dimensions.Size}}.Add(c.Ops)
	click.Add(c.Ops)
	pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
	click.Add(gtx.Ops)
}

func (t *Topbar) Event(c *layout.Context) interface{} {
	for e, ok := t.backClick.Next(c); ok; e, ok = t.backClick.Next(c) {
func (t *Topbar) Event(gtx *layout.Context) interface{} {
	for e, ok := t.backClick.Next(gtx); ok; e, ok = t.backClick.Next(gtx) {
		if e.Type == gesture.TypeClick {
			return BackEvent{}
		}


@@ 597,26 597,26 @@ func (t *Topbar) Event(c *layout.Context) interface{} {
	return nil
}

func (t *Topbar) Layout(c *layout.Context, insets layout.Inset, w layout.Widget) {
func (t *Topbar) Layout(gtx *layout.Context, insets layout.Inset, w layout.Widget) {
	stack := layout.Stack{Alignment: layout.SW}
	insets = layout.Inset{
		Top:    ui.Add(c, insets.Top, ui.Dp(16)),
		Top:    ui.Add(gtx, 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)),
		Left:   ui.Max(gtx, insets.Left, ui.Dp(16)),
		Right:  ui.Max(gtx, insets.Right, ui.Dp(16)),
	}
	stack.Init(c)
	stack.Init(gtx)
	stackContent := stack.Rigid(func() {
		insets.Layout(c, func() {
		insets.Layout(gtx, func() {
			flex := layout.Flex{Alignment: layout.Middle}
			flex.Init(c)
			flex.Init(gtx)
			backChild := flex.Rigid(func() {
				if t.Back {
					ico := (&icon{src: icons.NavigationArrowBack, size: ui.Dp(24)}).image(c, rgb(0xffffff))
					widget.Image{Src: ico, Rect: ico.Bounds(), Scale: 1}.Layout(c)
					c.Dimensions.Size.X += c.Px(ui.Dp(4))
					pointer.RectAreaOp{Rect: image.Rectangle{Max: c.Dimensions.Size}}.Add(c.Ops)
					t.backClick.Add(c.Ops)
					ico := (&icon{src: icons.NavigationArrowBack, size: ui.Dp(24)}).image(gtx, rgb(0xffffff))
					widget.Image{Src: ico, Rect: ico.Bounds(), Scale: 1}.Layout(gtx)
					gtx.Dimensions.Size.X += gtx.Px(ui.Dp(4))
					pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
					t.backClick.Add(gtx.Ops)
				}
			})
			content := flex.Flexible(1, w)


@@ 624,7 624,7 @@ func (t *Topbar) Layout(c *layout.Context, insets layout.Inset, w layout.Widget)
		})
	})
	bg := stack.Expand(func() {
		fill{theme.brand}.Layout(c)
		fill{theme.brand}.Layout(gtx)
	})
	stack.Layout(bg, stackContent)
}


@@ 668,8 668,8 @@ func newSignInPage(env *Env) *signInPage {
func (p *signInPage) Start(stop <-chan struct{}) {
}

func (p *signInPage) Event(c *layout.Context) interface{} {
	for e, ok := p.submit.Next(c); ok; e, ok = p.submit.Next(c) {
func (p *signInPage) Event(gtx *layout.Context) interface{} {
	for e, ok := p.submit.Next(gtx); ok; e, ok = p.submit.Next(gtx) {
		if e.Type == gesture.TypeClick {
			for _, f := range p.fields {
				*f.Value = f.edit.Text()


@@ 680,30 680,30 @@ func (p *signInPage) Event(c *layout.Context) interface{} {
	return nil
}

func (p *signInPage) Layout(c *layout.Context) {
func (p *signInPage) Layout(gtx *layout.Context) {
	f := layout.Flex{Axis: layout.Vertical}
	f.Init(c)
	f.Init(gtx)

	c1 := f.Rigid(func() {
		var t Topbar
		t.Layout(c, p.env.insets, func() {
			text.Label{Material: colorMaterial(c.Ops, rgb(0xffffff)), Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: "Sign in"}.Layout(c)
		t.Layout(gtx, p.env.insets, func() {
			text.Label{Material: colorMaterial(gtx.Ops, rgb(0xffffff)), Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: "Sign in"}.Layout(gtx)
		})
	})

	c2 := f.Flexible(1, func() {
		p.layoutSigninForm(c)
		p.layoutSigninForm(gtx)
	})
	f.Layout(c1, c2)
}

func (p *signInPage) layoutSigninForm(c *layout.Context) {
func (p *signInPage) layoutSigninForm(gtx *layout.Context) {
	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),
		Left:  ui.Max(gtx, ui.Dp(32), p.env.insets.Left),
		Right: ui.Max(gtx, ui.Dp(32), p.env.insets.Right),
	}
	l.Layout(c, len(p.fields)+1, func(i int) {
	l.Layout(gtx, len(p.fields)+1, func(i int) {
		in := inset
		switch {
		case i < len(p.fields):


@@ 711,32 711,32 @@ func (p *signInPage) layoutSigninForm(c *layout.Context) {
			if i == 0 {
				in.Top = ui.Dp(32)
			}
			in.Layout(c, func() {
				p.fields[i].Layout(c)
			in.Layout(gtx, func() {
				p.fields[i].Layout(gtx)
			})
		default:
			in.Bottom = ui.Max(c, ui.Dp(32), p.env.insets.Bottom)
			layout.Align(layout.E).Layout(c, func() {
				in.Layout(c, func() {
					p.submit.Layout(c, p.env)
			in.Bottom = ui.Max(gtx, ui.Dp(32), p.env.insets.Bottom)
			layout.Align(layout.E).Layout(gtx, func() {
				in.Layout(gtx, func() {
					p.submit.Layout(gtx, p.env)
				})
			})
		}
	})
}

func (f *formField) Layout(c *layout.Context) {
	theme.text.Add(c.Ops)
	fl := (&layout.Flex{Axis: layout.Vertical}).Init(c)
func (f *formField) Layout(gtx *layout.Context) {
	theme.text.Add(gtx.Ops)
	fl := (&layout.Flex{Axis: layout.Vertical}).Init(gtx)

	header := text.Label{Material: theme.text, Text: f.Header, Face: f.env.faces.For(fonts.bold, ui.Sp(12))}
	c1 := fl.Rigid(func() {
		c.Constraints.Width.Min = c.Constraints.Width.Max
		header.Layout(c)
		c.Dimensions.Size.Y += c.Px(ui.Dp(4))
		gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
		header.Layout(gtx)
		gtx.Dimensions.Size.Y += gtx.Px(ui.Dp(4))
	})
	c2 := fl.Rigid(func() {
		f.edit.Layout(c)
		f.edit.Layout(gtx)
	})
	fl.Layout(c1, c2)
}


@@ 745,18 745,18 @@ func (b *Button) Next(q ui.Queue) (gesture.ClickEvent, bool) {
	return b.click.Next(q)
}

func (b *Button) Layout(c *layout.Context, env *Env) {
func (b *Button) Layout(gtx *layout.Context, env *Env) {
	bg := Background{
		Material: theme.brand,
		Radius:   ui.Dp(4),
		Inset:    layout.UniformInset(ui.Dp(8)),
	}
	bg.Layout(c, func() {
	bg.Layout(gtx, func() {
		lbl := text.Label{Material: theme.white, Face: env.faces.For(fonts.regular, ui.Sp(16)), Text: b.Label, Alignment: text.Middle}
		lbl.Layout(c)
		lbl.Layout(gtx)
	})
	pointer.RectAreaOp{Rect: image.Rectangle{Max: c.Dimensions.Size}}.Add(c.Ops)
	b.click.Add(c.Ops)
	pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
	b.click.Add(gtx.Ops)
}

type Background struct {


@@ 765,27 765,27 @@ type Background struct {
	Inset    layout.Inset
}

func (b *Background) Layout(c *layout.Context, w layout.Widget) {
func (b *Background) Layout(gtx *layout.Context, w layout.Widget) {
	var macro ui.MacroOp
	macro.Record(c.Ops)
	b.Inset.Layout(c, w)
	macro.Record(gtx.Ops)
	b.Inset.Layout(gtx, w)
	macro.Stop()
	var stack ui.StackOp
	stack.Push(c.Ops)
	size := c.Dimensions.Size
	stack.Push(gtx.Ops)
	size := gtx.Dimensions.Size
	width, height := float32(size.X), float32(size.Y)
	if r := float32(c.Px(b.Radius)); r > 0 {
	if r := float32(gtx.Px(b.Radius)); r > 0 {
		if r > width/2 {
			r = width / 2
		}
		if r > height/2 {
			r = height / 2
		}
		rrect(c.Ops, width, height, r, r, r, r)
		rrect(gtx.Ops, width, height, r, r, r, r)
	}
	b.Material.Add(c.Ops)
	paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: width, Y: height}}}.Add(c.Ops)
	macro.Add(c.Ops)
	b.Material.Add(gtx.Ops)
	paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: width, Y: height}}}.Add(gtx.Ops)
	macro.Add(gtx.Ops)
	stack.Pop()
}



@@ 812,7 812,7 @@ func (p *threadsPage) Start(stop <-chan struct{}) {
	}()
}

func (p *threadsPage) Event(c *layout.Context) interface{} {
func (p *threadsPage) Event(gtx *layout.Context) interface{} {
	select {
	case <-p.updates:
		p.fetchThreads()


@@ 822,14 822,14 @@ func (p *threadsPage) Event(c *layout.Context) interface{} {
		p.env.redraw()
	default:
	}
	for e, ok := p.fab.Next(c); ok; e, ok = p.fab.Next(c) {
	for e, ok := p.fab.Next(gtx); ok; e, ok = p.fab.Next(gtx) {
		if e.Type == gesture.TypeClick {
			return ShowContactsEvent{}
		}
	}
	for i := range p.clicks {
		click := &p.clicks[i]
		for e, ok := click.Next(c); ok; e, ok = click.Next(c) {
		for e, ok := click.Next(gtx); ok; e, ok = click.Next(gtx) {
			if e.Type == gesture.TypeClick {
				t := p.threads[i]
				return ShowThreadEvent{Thread: t.ID}


@@ 852,54 852,54 @@ func (p *threadsPage) fetchThreads() {
	}()
}

func (p *threadsPage) Layout(c *layout.Context) {
func (p *threadsPage) Layout(gtx *layout.Context) {
	st := layout.Stack{Alignment: layout.Center}
	st.Init(c)
	st.Init(gtx)

	c1 := st.Rigid(func() {
		f := layout.Flex{Axis: layout.Vertical}
		f.Init(c)
		f.Init(gtx)

		c1 := f.Rigid(func() {
			var t Topbar
			t.Layout(c, p.env.insets, func() {
				text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: p.account.User}.Layout(c)
			t.Layout(gtx, p.env.insets, func() {
				text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: p.account.User}.Layout(gtx)
			})
		})

		c2 := f.Flexible(1, func() {
			p.layoutThreads(c)
			p.layoutThreads(gtx)
		})
		f.Layout(c1, c2)
	})
	c2 := st.Rigid(func() {
		layout.Align(layout.SE).Layout(c, func() {
		layout.Align(layout.SE).Layout(gtx, func() {
			layout.Inset{
				Right:  ui.Max(c, ui.Dp(16), p.env.insets.Right),
				Bottom: ui.Max(c, ui.Dp(16), p.env.insets.Bottom),
			}.Layout(c, func() {
				p.fab.Layout(c)
				Right:  ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
				Bottom: ui.Max(gtx, ui.Dp(16), p.env.insets.Bottom),
			}.Layout(gtx, func() {
				p.fab.Layout(gtx)
			})
		})
	})
	st.Layout(c1, c2)
}

func (p *threadsPage) layoutThreads(c *layout.Context) {
func (p *threadsPage) layoutThreads(gtx *layout.Context) {
	l := p.list
	if l.Dragging() {
		key.HideInputOp{}.Add(c.Ops)
		key.HideInputOp{}.Add(gtx.Ops)
	}
	l.Layout(c, len(p.threads), func(i int) {
	l.Layout(gtx, len(p.threads), func(i int) {
		in := layout.Inset{}
		switch i {
		case 0:
			in.Top = ui.Dp(4)
		case len(p.threads) - 1:
			in.Bottom = ui.Max(c, ui.Dp(4), p.env.insets.Bottom)
			in.Bottom = ui.Max(gtx, ui.Dp(4), p.env.insets.Bottom)
		}
		in.Layout(c, func() {
			p.thread(c, i)
		in.Layout(gtx, func() {
			p.thread(gtx, i)
		})
	})
}


@@ 913,7 913,7 @@ var contactColors = []color.RGBA{
	{A: 0xff, R: 0x00, G: 0x89, B: 0x7b},
}

func (p *threadsPage) thread(c *layout.Context, index int) {
func (p *threadsPage) thread(gtx *layout.Context, index int) {
	t := p.threads[index]
	bgtexmat := theme.tertText
	font := fonts.regular


@@ 923,32 923,32 @@ func (p *threadsPage) thread(c *layout.Context, index int) {
	}
	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),
		Left:  ui.Max(gtx, ui.Dp(16), p.env.insets.Left),
		Right: ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
	}
	in.Layout(c, func() {
	in.Layout(gtx, func() {
		elem := layout.Flex{Axis: layout.Vertical}
		elem.Init(c)
		elem.Init(gtx)
		c1 := elem.Rigid(func() {
			in := layout.Inset{Top: ui.Dp(8), Bottom: ui.Dp(8)}
			in.Layout(c, func() {
			in.Layout(gtx, func() {
				f := centerRowOpts()
				f.Init(c)
				f.Init(gtx)
				c1 := f.Rigid(func() {
					in := layout.Inset{Right: ui.Dp(12)}
					cc := clipCircle{}
					in.Layout(c, func() {
						cc.Layout(c, func() {
					in.Layout(gtx, func() {
						cc.Layout(gtx, func() {
							st := layout.Stack{Alignment: layout.Center}
							st.Init(c)
							st.Init(gtx)

							// Background color
							c1 := st.Rigid(func() {
								sz := image.Point{X: c.Px(ui.Dp(48)), Y: c.Px(ui.Dp(48))}
								c.Constraints = layout.RigidConstraints(c.Constraints.Constrain(sz))
								sz := image.Point{X: gtx.Px(ui.Dp(48)), Y: gtx.Px(ui.Dp(48))}
								gtx.Constraints = layout.RigidConstraints(gtx.Constraints.Constrain(sz))
								color := contactColors[index%len(contactColors)]
								mat := colorMaterial(c.Ops, color)
								fill{mat}.Layout(c)
								mat := colorMaterial(gtx.Ops, color)
								fill{mat}.Layout(gtx)
							})

							// Contact initial.


@@ 959,7 959,7 @@ func (p *threadsPage) thread(c *layout.Context, index int) {
									break
								}
								face := p.env.faces.For(fonts.regular, ui.Sp(24))
								text.Label{Material: theme.white, Face: face, Text: initial}.Layout(c)
								text.Label{Material: theme.white, Face: face, Text: initial}.Layout(gtx)
							})
							st.Layout(c1, c2)
						})


@@ 967,39 967,39 @@ func (p *threadsPage) thread(c *layout.Context, index int) {
				})
				c2 := f.Rigid(func() {
					f := column()
					f.Init(c)
					f.Init(gtx)
					c1 := f.Rigid(func() {
						f := baseline()
						f.Init(c)
						f.Init(gtx)
						c1 := f.Rigid(func() {
							text.Label{Material: theme.text, Face: p.env.faces.For(font, ui.Sp(18)), Text: t.ID}.Layout(c)
							text.Label{Material: theme.text, Face: p.env.faces.For(font, ui.Sp(18)), Text: t.ID}.Layout(gtx)
						})
						c2 := f.Flexible(1, func() {
							c.Constraints.Width.Min = c.Constraints.Width.Max
							gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
							in := layout.Inset{Left: ui.Dp(2)}
							in.Layout(c, func() {
							in.Layout(gtx, func() {
								text.Label{
									Alignment: text.End,
									Material:  bgtexmat,
									Face:      p.env.faces.For(font, ui.Sp(12)),
									Text:      formatTime(t.Updated),
								}.Layout(c)
								}.Layout(gtx)
							})
						})
						f.Layout(c1, c2)
					})
					c2 := f.Rigid(func() {
						in := layout.Inset{Top: ui.Dp(6)}
						in.Layout(c, func() {
							text.Label{Material: bgtexmat, Face: p.env.faces.For(font, ui.Sp(14)), MaxLines: 1, Text: t.Snippet}.Layout(c)
						in.Layout(gtx, func() {
							text.Label{Material: bgtexmat, Face: p.env.faces.For(font, ui.Sp(14)), MaxLines: 1, Text: t.Snippet}.Layout(gtx)
						})
					})
					f.Layout(c1, c2)
				})
				f.Layout(c1, c2)
			})
			pointer.RectAreaOp{Rect: image.Rectangle{Max: c.Dimensions.Size}}.Add(c.Ops)
			click.Add(c.Ops)
			pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
			click.Add(gtx.Ops)
		})
		elem.Layout(c1)
	})


@@ 1049,23 1049,23 @@ func (p *threadPage) Start(stop <-chan struct{}) {
	}()
}

func (p *threadPage) Event(c *layout.Context) interface{} {
func (p *threadPage) Event(gtx *layout.Context) interface{} {
	select {
	case <-p.updates:
		p.fetchMessages()
	default:
	}
	for e, ok := p.msgEdit.Next(c); ok; e, ok = p.msgEdit.Next(c) {
	for e, ok := p.msgEdit.Next(gtx); ok; e, ok = p.msgEdit.Next(gtx) {
		if _, ok := e.(text.SubmitEvent); ok {
			p.sendMessage()
		}
	}
	for e, ok := p.send.Next(c); ok; e, ok = p.send.Next(c) {
	for e, ok := p.send.Next(gtx); ok; e, ok = p.send.Next(gtx) {
		if e.Type == gesture.TypeClick {
			p.sendMessage()
		}
	}
	for e, ok := p.invite.Next(c); ok; e, ok = p.invite.Next(c) {
	for e, ok := p.invite.Next(gtx); ok; e, ok = p.invite.Next(gtx) {
		if e.Type == gesture.TypeClick {
			if err := p.env.client.Send(p.thread.ID, "Invitation sent"); err != nil {
				log.Printf("failed to send invitation: %v", err)


@@ 1073,7 1073,7 @@ func (p *threadPage) Event(c *layout.Context) interface{} {
			break
		}
	}
	for e, ok := p.accept.Next(c); ok; e, ok = p.accept.Next(c) {
	for e, ok := p.accept.Next(gtx); ok; e, ok = p.accept.Next(gtx) {
		if e.Type == gesture.TypeClick {
			if err := p.env.client.Send(p.thread.ID, "Invitation accepted"); err != nil {
				log.Printf("failed to send invitation accept: %v", err)


@@ 1081,7 1081,7 @@ func (p *threadPage) Event(c *layout.Context) interface{} {
			break
		}
	}
	return p.topbar.Event(c)
	return p.topbar.Event(gtx)
}

func (p *threadPage) sendMessage() {


@@ 1093,145 1093,145 @@ func (p *threadPage) sendMessage() {
	}
}

func (p *threadPage) Layout(c *layout.Context) {
func (p *threadPage) Layout(gtx *layout.Context) {
	l := p.list
	if l.Dragging() {
		key.HideInputOp{}.Add(c.Ops)
		key.HideInputOp{}.Add(gtx.Ops)
	}
	select {
	case p.messages = <-p.result:
	default:
	}
	f := layout.Flex{Axis: layout.Vertical}
	f.Init(c)
	f.Init(gtx)
	c1 := f.Rigid(func() {
		p.topbar.Layout(c, p.env.insets, func() {
			text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: p.thread.ID}.Layout(c)
		p.topbar.Layout(gtx, p.env.insets, func() {
			text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, ui.Sp(20)), Text: p.thread.ID}.Layout(gtx)
		})
	})

	c3 := f.Rigid(func() {
		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),
			Left:   ui.Max(gtx, ui.Dp(16), p.env.insets.Left),
			Right:  ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
			Bottom: ui.Max(gtx, ui.Dp(16), p.env.insets.Bottom),
		}
		in.Layout(c, func() {
		in.Layout(gtx, func() {
			switch {
			case p.thread.PendingInvitation:
				p.accept.Layout(c, p.env)
				p.accept.Layout(gtx, p.env)
			case p.env.client.ContainsSession(p.thread.ID):
				p.layoutMessageBox(c)
				p.layoutMessageBox(gtx)
			default:
				p.invite.Layout(c, p.env)
				p.invite.Layout(gtx, p.env)
			}
		})
	})

	c2 := f.Flexible(1, func() {
		c.Constraints.Height.Min = c.Constraints.Height.Max
		l.Layout(c, len(p.messages), func(i int) {
			p.message(c, i)
		gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
		l.Layout(gtx, len(p.messages), func(i int) {
			p.message(gtx, i)
		})
	})
	f.Layout(c1, c2, c3)
}

func (p *threadPage) layoutMessageBox(c *layout.Context) {
	if mh := c.Px(ui.Dp(100)); c.Constraints.Height.Max > mh {
		c.Constraints.Height.Max = mh
func (p *threadPage) layoutMessageBox(gtx *layout.Context) {
	if mh := gtx.Px(ui.Dp(100)); gtx.Constraints.Height.Max > mh {
		gtx.Constraints.Height.Max = mh
	}
	f := (&layout.Flex{Alignment: layout.End}).Init(c)
	f := (&layout.Flex{Alignment: layout.End}).Init(gtx)

	var sendHeight int
	c2 := f.Rigid(func() {
		in := layout.Inset{Left: ui.Dp(8)}
		in.Layout(c, func() {
			p.send.Layout(c)
			sendHeight = c.Dimensions.Size.Y
		in.Layout(gtx, func() {
			p.send.Layout(gtx)
			sendHeight = gtx.Dimensions.Size.Y
		})
	})

	c1 := f.Flexible(1, func() {
		c.Constraints.Width.Min = c.Constraints.Width.Max
		if c.Constraints.Height.Min < sendHeight {
			c.Constraints.Height.Min = sendHeight
		gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
		if gtx.Constraints.Height.Min < sendHeight {
			gtx.Constraints.Height.Min = sendHeight
		}
		bg := Background{
			Material: colorMaterial(c.Ops, rgb(0xeeeeee)),
			Material: colorMaterial(gtx.Ops, rgb(0xeeeeee)),
			Inset:    layout.UniformInset(ui.Dp(8)),
			Radius:   ui.Dp(10),
		}
		bg.Layout(c, func() {
			layout.Align(layout.W).Layout(c, func() {
				c.Constraints.Width.Min = c.Constraints.Width.Max
				p.msgEdit.Layout(c)
		bg.Layout(gtx, func() {
			layout.Align(layout.W).Layout(gtx, func() {
				gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
				p.msgEdit.Layout(gtx)
			})
		})
	})
	f.Layout(c1, c2)
}

func (p *threadPage) message(c *layout.Context, index int) {
func (p *threadPage) message(gtx *layout.Context, index int) {
	msg := p.messages[index]
	in := layout.Inset{Top: ui.Dp(16), Left: ui.Dp(16), Right: ui.Dp(40)}
	align := layout.Align(layout.W)
	msgMat := colorMaterial(c.Ops, rgb(0xffffff))
	msgMat := colorMaterial(gtx.Ops, rgb(0xffffff))
	bgcol := theme.brand
	timecol := argb(0xaaaaaaaa)
	if msg.Own {
		in.Left, in.Right = in.Right, in.Left
		align = layout.Align(layout.E)
		bgcol = colorMaterial(c.Ops, rgb(0xeeeeee))
		bgcol = colorMaterial(gtx.Ops, rgb(0xeeeeee))
		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)
	in.Layout(c, func() {
		align.Layout(c, func() {
	in.Left = ui.Max(gtx, in.Left, p.env.insets.Left)
	in.Right = ui.Max(gtx, in.Right, p.env.insets.Right)
	in.Layout(gtx, func() {
		align.Layout(gtx, func() {
			bg := Background{
				Material: bgcol,
				Inset:    layout.Inset{Top: ui.Dp(8), Bottom: ui.Dp(8), Left: ui.Dp(12), Right: ui.Dp(12)},
				Radius:   ui.Dp(10),
			}
			bg.Layout(c, func() {
			bg.Layout(gtx, func() {
				f := layout.Flex{Axis: layout.Vertical}
				f.Init(c)
				f.Init(gtx)

				var msgWidth int
				c1 := f.Rigid(func() {
					label := text.Label{Material: msgMat, Face: p.env.faces.For(fonts.regular, ui.Sp(14)), Text: msg.Message}
					label.Layout(c)
					c.Dimensions.Size.Y += c.Px(ui.Dp(4))
					msgWidth = c.Dimensions.Size.X
					label.Layout(gtx)
					gtx.Dimensions.Size.Y += gtx.Px(ui.Dp(4))
					msgWidth = gtx.Dimensions.Size.X
				})

				c2 := f.Rigid(func() {
					c.Constraints.Width.Min = msgWidth
					gtx.Constraints.Width.Min = msgWidth
					f := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween, Alignment: layout.Middle}
					f.Init(c)
					f.Init(gtx)

					var children []layout.FlexChild
					child := f.Rigid(func() {
						time := formatTime(msg.Time)
						tlbl := text.Label{Material: colorMaterial(c.Ops, timecol), Face: p.env.faces.For(fonts.regular, ui.Sp(10)), Text: time}
						tlbl.Layout(c)
						tlbl := text.Label{Material: colorMaterial(gtx.Ops, timecol), Face: p.env.faces.For(fonts.regular, ui.Sp(10)), Text: time}
						tlbl.Layout(gtx)
					})
					children = append(children, child)

					if msg.Own {
						child := f.Rigid(func() {
							in := layout.Inset{Left: ui.Dp(12)}
							in.Layout(c, func() {
								checkmark := p.checkmark.image(c, timecol)
							in.Layout(gtx, func() {
								checkmark := p.checkmark.image(gtx, timecol)
								r := checkmark.Bounds()
								if msg.Sent {
									paint.ImageOp{Src: checkmark, Rect: r}.Add(c.Ops)
									paint.PaintOp{Rect: toRectF(r)}.Add(c.Ops)
									paint.ImageOp{Src: checkmark, Rect: r}.Add(gtx.Ops)
									paint.PaintOp{Rect: toRectF(r)}.Add(gtx.Ops)
								}
								c.Dimensions = layout.Dimensions{Size: r.Size()}
								gtx.Dimensions = layout.Dimensions{Size: r.Size()}
							})
						})
						children = append(children, child)


@@ 1278,25 1278,25 @@ func (b *IconButton) Next(q ui.Queue) (gesture.ClickEvent, bool) {
	return b.click.Next(q)
}

func (b *IconButton) Layout(c *layout.Context) {
	ico := b.Icon.image(c, rgb(0xffffff))
func (b *IconButton) Layout(gtx *layout.Context) {
	ico := b.Icon.image(gtx, rgb(0xffffff))
	bg := Background{
		Material: theme.brand,
		Radius:   ui.Px(1e6),
		Inset:    b.Inset,
	}
	bg.Layout(c, func() {
	bg.Layout(gtx, func() {
		sz := image.Point{X: ico.Bounds().Dx(), Y: ico.Bounds().Dy()}
		c.Constraints = layout.RigidConstraints(c.Constraints.Constrain(sz))
		widget.Image{Src: ico, Rect: ico.Bounds(), Scale: 1}.Layout(c)
		gtx.Constraints = layout.RigidConstraints(gtx.Constraints.Constrain(sz))
		widget.Image{Src: ico, Rect: ico.Bounds(), Scale: 1}.Layout(gtx)
	})
	pointer.EllipseAreaOp{Rect: image.Rectangle{Max: c.Dimensions.Size}}.Add(c.Ops)
	b.click.Add(c.Ops)
	pointer.EllipseAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
	b.click.Add(gtx.Ops)
}

func (a *App) update(c *layout.Context) {
func (a *App) update(gtx *layout.Context) {
	page := a.stack.Current()
	if e := page.Event(c); e != nil {
	if e := page.Event(gtx); e != nil {
		switch e := e.(type) {
		case BackEvent:
			a.stack.Pop()


@@ 1317,15 1317,15 @@ type fill struct {
	material ui.MacroOp
}

func (f fill) Layout(c *layout.Context) {
	cs := c.Constraints
func (f fill) Layout(gtx *layout.Context) {
	cs := gtx.Constraints
	d := image.Point{X: cs.Width.Max, Y: cs.Height.Max}
	dr := f32.Rectangle{
		Max: f32.Point{X: float32(d.X), Y: float32(d.Y)},
	}
	f.material.Add(c.Ops)
	paint.PaintOp{Rect: dr}.Add(c.Ops)
	c.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y}
	f.material.Add(gtx.Ops)
	paint.PaintOp{Rect: dr}.Add(gtx.Ops)
	gtx.Dimensions = layout.Dimensions{Size: d, Baseline: d.Y}
}

func column() layout.Flex {


@@ 1343,11 1343,11 @@ func baseline() layout.Flex {
type clipCircle struct {
}

func (cc *clipCircle) Layout(c *layout.Context, w layout.Widget) {
func (cc *clipCircle) Layout(gtx *layout.Context, w layout.Widget) {
	var macro ui.MacroOp
	macro.Record(c.Ops)
	macro.Record(gtx.Ops)
	w()
	dims := c.Dimensions
	dims := gtx.Dimensions
	macro.Stop()
	max := dims.Size.X
	if dy := dims.Size.Y; dy > max {


@@ 1356,9 1356,9 @@ func (cc *clipCircle) Layout(c *layout.Context, w layout.Widget) {
	szf := float32(max)
	rr := szf * .5
	var stack ui.StackOp
	stack.Push(c.Ops)
	rrect(c.Ops, szf, szf, rr, rr, rr, rr)
	macro.Add(c.Ops)
	stack.Push(gtx.Ops)
	rrect(gtx.Ops, szf, szf, rr, rr, rr, rr)
	macro.Add(gtx.Ops)
	stack.Pop()
}