~eliasnaur/scatter

6057a1e29f8d7c8fe98d6e29c610d323649cbe10 — Elias Naur 6 months ago de647f2
cmd/scatter: updates for gio package reorganization

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

M cmd/scatter/ui.go
M go.mod
M go.sum
M cmd/scatter/ui.go => cmd/scatter/ui.go +102 -101
@@ 18,14 18,15 @@ import (
	"gioui.org/app"
	"gioui.org/f32"
	"gioui.org/gesture"
	"gioui.org/key"
	"gioui.org/io/key"
	"gioui.org/io/pointer"
	"gioui.org/io/profile"
	"gioui.org/layout"
	"gioui.org/measure"
	"gioui.org/paint"
	"gioui.org/pointer"
	"gioui.org/system"
	"gioui.org/op"
	"gioui.org/op/paint"
	"gioui.org/text"
	"gioui.org/ui"
	"gioui.org/text/shape"
	"gioui.org/unit"
	"gioui.org/widget"
	"golang.org/x/exp/shiny/iconvg"



@@ 40,7 41,7 @@ import (

type Env struct {
	insets layout.Inset
	faces  measure.Faces
	faces  shape.Faces
	client *Client
	redraw func()
}


@@ 53,7 54,7 @@ type App struct {

	// Profiling.
	profiling   bool
	profile     system.ProfileEvent
	profile     profile.Event
	lastMallocs uint64
}



@@ 147,7 148,7 @@ type Contact struct {

type icon struct {
	src  []byte
	size ui.Value
	size unit.Value

	// Cached values.
	img     image.Image


@@ 189,10 190,10 @@ var fonts struct {
}

var theme struct {
	text     ui.MacroOp
	tertText ui.MacroOp
	brand    ui.MacroOp
	white    ui.MacroOp
	text     op.MacroOp
	tertText op.MacroOp
	brand    op.MacroOp
	white    op.MacroOp
}

func uiMain() {


@@ 200,15 201,15 @@ func uiMain() {
	fonts.bold = mustLoadFont(gobold.TTF)
	fonts.italic = mustLoadFont(goitalic.TTF)
	fonts.mono = mustLoadFont(gomono.TTF)
	var ops ui.Ops
	var ops op.Ops
	theme.text = colorMaterial(&ops, rgb(0x000000))
	theme.tertText = colorMaterial(&ops, rgb(0xbbbbbb))
	theme.brand = colorMaterial(&ops, rgb(0x3c98c6))
	theme.white = colorMaterial(&ops, rgb(0xffffff))
	go func() {
		w := app.NewWindow(
			app.WithWidth(ui.Dp(400)),
			app.WithHeight(ui.Dp(800)),
			app.WithWidth(unit.Dp(400)),
			app.WithHeight(unit.Dp(800)),
			app.WithTitle("Scatter"),
		)
		if err := newApp(w).run(); err != nil {


@@ 218,8 219,8 @@ func uiMain() {
	app.Main()
}

func colorMaterial(ops *ui.Ops, color color.RGBA) ui.MacroOp {
	var mat ui.MacroOp
func colorMaterial(ops *op.Ops, color color.RGBA) op.MacroOp {
	var mat op.MacroOp
	mat.Record(ops)
	paint.ColorOp{Color: color}.Add(ops)
	mat.Stop()


@@ 312,7 313,7 @@ func (t *Transition) Event(gtx *layout.Context) interface{} {
}

func (t *Transition) Layout(gtx *layout.Context) {
	var stack ui.StackOp
	var stack op.StackOp
	stack.Push(gtx.Ops)
	prev, page := t.prev, t.page
	if prev != nil {


@@ 338,10 339,10 @@ func (t *Transition) Layout(gtx *layout.Context) {
		}
		diameter := progress * max
		radius := diameter / 2
		ui.InvalidateOp{}.Add(gtx.Ops)
		op.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 := op.TransformOp{}.Offset(center.Sub(clipCenter))
		off.Add(gtx.Ops)
		rrect(gtx.Ops, diameter, diameter, radius, radius, radius, radius)
		off.Invert().Add(gtx.Ops)


@@ 439,22 440,22 @@ func (a *App) Layout(gtx *layout.Context) {

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

	system.ProfileOp{Key: a}.Add(gtx.Ops)
	profile.Op{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(gtx, func() {
		in := a.env.insets
		in.Top = ui.Max(gtx, ui.Dp(16), in.Top)
		in.Top = unit.Max(gtx, unit.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(gtx)
			text.Label{Material: theme.text, Face: a.env.faces.For(fonts.mono, unit.Sp(10)), Text: txt}.Layout(gtx)
		})
	})
}


@@ 466,7 467,7 @@ func newContactsPage(env *Env) *contactsPage {
			Axis: layout.Vertical,
		},
		searchEdit: &text.Editor{
			Face:         env.faces.For(fonts.regular, ui.Sp(20)),
			Face:         env.faces.For(fonts.regular, unit.Sp(20)),
			SingleLine:   true,
			Submit:       true,
			Hint:         "Email address",


@@ 562,28 563,28 @@ func (p *contactsPage) Layout(gtx *layout.Context) {

func (p *contactsPage) contact(gtx *layout.Context, index int) {
	in := layout.Inset{
		Top:    ui.Dp(16),
		Bottom: ui.Dp(16),
		Left:   ui.Max(gtx, ui.Dp(16), p.env.insets.Left),
		Right:  ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
		Top:    unit.Dp(16),
		Bottom: unit.Dp(16),
		Left:   unit.Max(gtx, unit.Dp(16), p.env.insets.Left),
		Right:  unit.Max(gtx, unit.Dp(16), p.env.insets.Right),
	}
	contact := p.contacts[index]
	click := &p.clicks[index]
	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.Inset{Right: unit.Dp(8)}
			in.Layout(gtx, func() {
				cc := clipCircle{}
				cc.Layout(gtx, func() {
					sz := image.Point{X: gtx.Px(ui.Dp(48)), Y: gtx.Px(ui.Dp(48))}
					sz := image.Point{X: gtx.Px(unit.Dp(48)), Y: gtx.Px(unit.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(gtx)
			text.Label{Material: theme.text, Face: p.env.faces.For(fonts.regular, unit.Sp(18)), Text: contact.Address}.Layout(gtx)
		})

		f.Layout(c1, c2)


@@ 604,10 605,10 @@ func (t *Topbar) Event(gtx *layout.Context) interface{} {
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(gtx, insets.Top, ui.Dp(16)),
		Bottom: ui.Dp(16),
		Left:   ui.Max(gtx, insets.Left, ui.Dp(16)),
		Right:  ui.Max(gtx, insets.Right, ui.Dp(16)),
		Top:    unit.Add(gtx, insets.Top, unit.Dp(16)),
		Bottom: unit.Dp(16),
		Left:   unit.Max(gtx, insets.Left, unit.Dp(16)),
		Right:  unit.Max(gtx, insets.Right, unit.Dp(16)),
	}
	stack.Init(gtx)
	stackContent := stack.Rigid(func() {


@@ 616,9 617,9 @@ func (t *Topbar) Layout(gtx *layout.Context, insets layout.Inset, w layout.Widge
			flex.Init(gtx)
			backChild := flex.Rigid(func() {
				if t.Back {
					ico := (&icon{src: icons.NavigationArrowBack, size: ui.Dp(24)}).image(gtx, rgb(0xffffff))
					ico := (&icon{src: icons.NavigationArrowBack, size: unit.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))
					gtx.Dimensions.Size.X += gtx.Px(unit.Dp(4))
					pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
					t.backClick.Add(gtx.Ops)
				}


@@ 658,7 659,7 @@ func newSignInPage(env *Env) *signInPage {
	for _, f := range p.fields {
		f.env = p.env
		f.edit = &text.Editor{
			Face:         env.faces.For(fonts.regular, ui.Sp(16)),
			Face:         env.faces.For(fonts.regular, unit.Sp(16)),
			SingleLine:   true,
			Hint:         f.Hint,
			Material:     theme.text,


@@ 689,7 690,7 @@ func (p *signInPage) Layout(gtx *layout.Context) {
	c1 := f.Rigid(func() {
		var t Topbar
		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)
			text.Label{Material: colorMaterial(gtx.Ops, rgb(0xffffff)), Face: p.env.faces.For(fonts.regular, unit.Sp(20)), Text: "Sign in"}.Layout(gtx)
		})
	})



@@ 702,22 703,22 @@ func (p *signInPage) Layout(gtx *layout.Context) {
func (p *signInPage) layoutSigninForm(gtx *layout.Context) {
	l := p.list
	inset := layout.Inset{
		Left:  ui.Max(gtx, ui.Dp(32), p.env.insets.Left),
		Right: ui.Max(gtx, ui.Dp(32), p.env.insets.Right),
		Left:  unit.Max(gtx, unit.Dp(32), p.env.insets.Left),
		Right: unit.Max(gtx, unit.Dp(32), p.env.insets.Right),
	}
	l.Layout(gtx, len(p.fields)+1, func(i int) {
		in := inset
		switch {
		case i < len(p.fields):
			in.Bottom = ui.Dp(12)
			in.Bottom = unit.Dp(12)
			if i == 0 {
				in.Top = ui.Dp(32)
				in.Top = unit.Dp(32)
			}
			in.Layout(gtx, func() {
				p.fields[i].Layout(gtx)
			})
		default:
			in.Bottom = ui.Max(gtx, ui.Dp(32), p.env.insets.Bottom)
			in.Bottom = unit.Max(gtx, unit.Dp(32), p.env.insets.Bottom)
			layout.Align(layout.E).Layout(gtx, func() {
				in.Layout(gtx, func() {
					p.submit.Layout(gtx, p.env)


@@ 731,11 732,11 @@ 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))}
	header := text.Label{Material: theme.text, Text: f.Header, Face: f.env.faces.For(fonts.bold, unit.Sp(12))}
	c1 := fl.Rigid(func() {
		gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
		header.Layout(gtx)
		gtx.Dimensions.Size.Y += gtx.Px(ui.Dp(4))
		gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
	})
	c2 := fl.Rigid(func() {
		f.edit.Layout(gtx)


@@ 764,11 765,11 @@ func (b *Button) Layout(gtx *layout.Context, env *Env) {
	b.buttonState.clear()
	bg := Background{
		Material: theme.brand,
		Radius:   ui.Dp(4),
		Inset:    layout.UniformInset(ui.Dp(8)),
		Radius:   unit.Dp(4),
		Inset:    layout.UniformInset(unit.Dp(8)),
	}
	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 := text.Label{Material: theme.white, Face: env.faces.For(fonts.regular, unit.Sp(16)), Text: b.Label, Alignment: text.Middle}
		lbl.Layout(gtx)
	})
	pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)


@@ 776,17 777,17 @@ func (b *Button) Layout(gtx *layout.Context, env *Env) {
}

type Background struct {
	Material ui.MacroOp
	Radius   ui.Value
	Material op.MacroOp
	Radius   unit.Value
	Inset    layout.Inset
}

func (b *Background) Layout(gtx *layout.Context, w layout.Widget) {
	var macro ui.MacroOp
	var macro op.MacroOp
	macro.Record(gtx.Ops)
	b.Inset.Layout(gtx, w)
	macro.Stop()
	var stack ui.StackOp
	var stack op.StackOp
	stack.Push(gtx.Ops)
	size := gtx.Dimensions.Size
	width, height := float32(size.X), float32(size.Y)


@@ 812,8 813,8 @@ func newThreadsPage(env *Env) *threadsPage {
			Axis: layout.Vertical,
		},
		fab: &IconButton{
			Icon:  &icon{src: icons.ContentCreate, size: ui.Dp(24)},
			Inset: layout.UniformInset(ui.Dp(16)),
			Icon:  &icon{src: icons.ContentCreate, size: unit.Dp(24)},
			Inset: layout.UniformInset(unit.Dp(16)),
		},
	}
}


@@ 877,7 878,7 @@ func (p *threadsPage) Layout(gtx *layout.Context) {
		c1 := f.Rigid(func() {
			var t Topbar
			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)
				text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, unit.Sp(20)), Text: p.account.User}.Layout(gtx)
			})
		})



@@ 889,8 890,8 @@ func (p *threadsPage) Layout(gtx *layout.Context) {
	c2 := st.Rigid(func() {
		layout.Align(layout.SE).Layout(gtx, func() {
			layout.Inset{
				Right:  ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
				Bottom: ui.Max(gtx, ui.Dp(16), p.env.insets.Bottom),
				Right:  unit.Max(gtx, unit.Dp(16), p.env.insets.Right),
				Bottom: unit.Max(gtx, unit.Dp(16), p.env.insets.Bottom),
			}.Layout(gtx, func() {
				p.fab.Layout(gtx)
			})


@@ 908,9 909,9 @@ func (p *threadsPage) layoutThreads(gtx *layout.Context) {
		in := layout.Inset{}
		switch i {
		case 0:
			in.Top = ui.Dp(4)
			in.Top = unit.Dp(4)
		case len(p.threads) - 1:
			in.Bottom = ui.Max(gtx, ui.Dp(4), p.env.insets.Bottom)
			in.Bottom = unit.Max(gtx, unit.Dp(4), p.env.insets.Bottom)
		}
		in.Layout(gtx, func() {
			p.thread(gtx, i)


@@ 937,19 938,19 @@ func (p *threadsPage) thread(gtx *layout.Context, index int) {
	}
	click := &p.clicks[index]
	in := layout.Inset{
		Left:  ui.Max(gtx, ui.Dp(16), p.env.insets.Left),
		Right: ui.Max(gtx, ui.Dp(16), p.env.insets.Right),
		Left:  unit.Max(gtx, unit.Dp(16), p.env.insets.Left),
		Right: unit.Max(gtx, unit.Dp(16), p.env.insets.Right),
	}
	in.Layout(gtx, func() {
		elem := layout.Flex{Axis: layout.Vertical}
		elem.Init(gtx)
		c1 := elem.Rigid(func() {
			in := layout.Inset{Top: ui.Dp(8), Bottom: ui.Dp(8)}
			in := layout.Inset{Top: unit.Dp(8), Bottom: unit.Dp(8)}
			in.Layout(gtx, func() {
				f := centerRowOpts()
				f.Init(gtx)
				c1 := f.Rigid(func() {
					in := layout.Inset{Right: ui.Dp(12)}
					in := layout.Inset{Right: unit.Dp(12)}
					cc := clipCircle{}
					in.Layout(gtx, func() {
						cc.Layout(gtx, func() {


@@ 958,7 959,7 @@ func (p *threadsPage) thread(gtx *layout.Context, index int) {

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


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


@@ 986,16 987,16 @@ func (p *threadsPage) thread(gtx *layout.Context, index int) {
						f := baseline()
						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(gtx)
							text.Label{Material: theme.text, Face: p.env.faces.For(font, unit.Sp(18)), Text: t.ID}.Layout(gtx)
						})
						c2 := f.Flexible(1, func() {
							gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
							in := layout.Inset{Left: ui.Dp(2)}
							in := layout.Inset{Left: unit.Dp(2)}
							in.Layout(gtx, func() {
								text.Label{
									Alignment: text.End,
									Material:  bgtexmat,
									Face:      p.env.faces.For(font, ui.Sp(12)),
									Face:      p.env.faces.For(font, unit.Sp(12)),
									Text:      formatTime(t.Updated),
								}.Layout(gtx)
							})


@@ 1003,9 1004,9 @@ func (p *threadsPage) thread(gtx *layout.Context, index int) {
						f.Layout(c1, c2)
					})
					c2 := f.Rigid(func() {
						in := layout.Inset{Top: ui.Dp(6)}
						in := layout.Inset{Top: unit.Dp(6)}
						in.Layout(gtx, func() {
							text.Label{Material: bgtexmat, Face: p.env.faces.For(font, ui.Sp(14)), MaxLines: 1, Text: t.Snippet}.Layout(gtx)
							text.Label{Material: bgtexmat, Face: p.env.faces.For(font, unit.Sp(14)), MaxLines: 1, Text: t.Snippet}.Layout(gtx)
						})
					})
					f.Layout(c1, c2)


@@ 1024,22 1025,22 @@ func newThreadPage(env *Env, threadID string) *threadPage {
	return &threadPage{
		env:       env,
		thread:    thread,
		checkmark: &icon{src: icons.ActionDone, size: ui.Dp(12)},
		checkmark: &icon{src: icons.ActionDone, size: unit.Dp(12)},
		list: &layout.List{
			Axis:        layout.Vertical,
			ScrollToEnd: true,
		},
		result: make(chan []*Message, 1),
		msgEdit: &text.Editor{
			Face:         env.faces.For(fonts.regular, ui.Sp(14)),
			Face:         env.faces.For(fonts.regular, unit.Sp(14)),
			Submit:       true,
			Hint:         "Send a message",
			Material:     theme.text,
			HintMaterial: theme.tertText,
		},
		send: &IconButton{
			Icon:  &icon{src: icons.ContentSend, size: ui.Dp(24)},
			Inset: layout.UniformInset(ui.Dp(6)),
			Icon:  &icon{src: icons.ContentSend, size: unit.Dp(24)},
			Inset: layout.UniformInset(unit.Dp(6)),
		},
		invite: &Button{
			Label: "Send invitation",


@@ 1112,16 1113,16 @@ func (p *threadPage) Layout(gtx *layout.Context) {
	f.Init(gtx)
	c1 := f.Rigid(func() {
		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)
			text.Label{Material: theme.white, Face: p.env.faces.For(fonts.regular, unit.Sp(20)), Text: p.thread.ID}.Layout(gtx)
		})
	})

	c3 := f.Rigid(func() {
		in := layout.Inset{
			Top:    ui.Dp(16),
			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),
			Top:    unit.Dp(16),
			Left:   unit.Max(gtx, unit.Dp(16), p.env.insets.Left),
			Right:  unit.Max(gtx, unit.Dp(16), p.env.insets.Right),
			Bottom: unit.Max(gtx, unit.Dp(16), p.env.insets.Bottom),
		}
		in.Layout(gtx, func() {
			switch {


@@ 1145,14 1146,14 @@ func (p *threadPage) Layout(gtx *layout.Context) {
}

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

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


@@ 1166,8 1167,8 @@ func (p *threadPage) layoutMessageBox(gtx *layout.Context) {
		}
		bg := Background{
			Material: colorMaterial(gtx.Ops, rgb(0xeeeeee)),
			Inset:    layout.UniformInset(ui.Dp(8)),
			Radius:   ui.Dp(10),
			Inset:    layout.UniformInset(unit.Dp(8)),
			Radius:   unit.Dp(10),
		}
		bg.Layout(gtx, func() {
			layout.Align(layout.W).Layout(gtx, func() {


@@ 1181,7 1182,7 @@ func (p *threadPage) layoutMessageBox(gtx *layout.Context) {

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)}
	in := layout.Inset{Top: unit.Dp(16), Left: unit.Dp(16), Right: unit.Dp(40)}
	align := layout.Align(layout.W)
	msgMat := colorMaterial(gtx.Ops, rgb(0xffffff))
	bgcol := theme.brand


@@ 1193,14 1194,14 @@ func (p *threadPage) message(gtx *layout.Context, index int) {
		msgMat = theme.text
		timecol = rgb(0x888888)
	}
	in.Left = ui.Max(gtx, in.Left, p.env.insets.Left)
	in.Right = ui.Max(gtx, in.Right, p.env.insets.Right)
	in.Left = unit.Max(gtx, in.Left, p.env.insets.Left)
	in.Right = unit.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),
				Inset:    layout.Inset{Top: unit.Dp(8), Bottom: unit.Dp(8), Left: unit.Dp(12), Right: unit.Dp(12)},
				Radius:   unit.Dp(10),
			}
			bg.Layout(gtx, func() {
				f := layout.Flex{Axis: layout.Vertical}


@@ 1208,9 1209,9 @@ func (p *threadPage) message(gtx *layout.Context, index int) {

				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 := text.Label{Material: msgMat, Face: p.env.faces.For(fonts.regular, unit.Sp(14)), Text: msg.Message}
					label.Layout(gtx)
					gtx.Dimensions.Size.Y += gtx.Px(ui.Dp(4))
					gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
					msgWidth = gtx.Dimensions.Size.X
				})



@@ 1222,14 1223,14 @@ func (p *threadPage) message(gtx *layout.Context, index int) {
					var children []layout.FlexChild
					child := f.Rigid(func() {
						time := formatTime(msg.Time)
						tlbl := text.Label{Material: colorMaterial(gtx.Ops, timecol), Face: p.env.faces.For(fonts.regular, ui.Sp(10)), Text: time}
						tlbl := text.Label{Material: colorMaterial(gtx.Ops, timecol), Face: p.env.faces.For(fonts.regular, unit.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.Inset{Left: unit.Dp(12)}
							in.Layout(gtx, func() {
								checkmark := p.checkmark.image(gtx, timecol)
								r := checkmark.Bounds()


@@ 1285,7 1286,7 @@ func (b *IconButton) Layout(gtx *layout.Context) {
	ico := b.Icon.image(gtx, rgb(0xffffff))
	bg := Background{
		Material: theme.brand,
		Radius:   ui.Px(1e6),
		Radius:   unit.Px(1e6),
		Inset:    b.Inset,
	}
	bg.Layout(gtx, func() {


@@ 1317,7 1318,7 @@ func (a *App) update(gtx *layout.Context) {
}

type fill struct {
	material ui.MacroOp
	material op.MacroOp
}

func (f fill) Layout(gtx *layout.Context) {


@@ 1347,7 1348,7 @@ type clipCircle struct {
}

func (cc *clipCircle) Layout(gtx *layout.Context, w layout.Widget) {
	var macro ui.MacroOp
	var macro op.MacroOp
	macro.Record(gtx.Ops)
	w()
	dims := gtx.Dimensions


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


@@ 1372,7 1373,7 @@ func toRectF(r image.Rectangle) f32.Rectangle {
	}
}

func (ic *icon) image(c ui.Config, col color.RGBA) image.Image {
func (ic *icon) image(c unit.Converter, col color.RGBA) image.Image {
	sz := c.Px(ic.size)
	if sz == ic.imgSize {
		return ic.img


@@ 1392,7 1393,7 @@ func (ic *icon) image(c ui.Config, col color.RGBA) image.Image {
}

// https://pomax.github.io/bezierinfo/#circles_cubic.
func rrect(ops *ui.Ops, width, height, se, sw, nw, ne float32) {
func rrect(ops *op.Ops, width, height, se, sw, nw, ne float32) {
	w, h := float32(width), float32(height)
	const c = 0.55228475 // 4*(sqrt(2)-1)/3
	var b paint.PathBuilder

M go.mod => go.mod +1 -1
@@ 3,7 3,7 @@ module scatter.im
go 1.13

require (
	gioui.org v0.0.0-20190930110410-3c65aa61f64e
	gioui.org v0.0.0-20190930150022-6bc5ff247938
	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 +2 -2
@@ 1,5 1,5 @@
gioui.org v0.0.0-20190930110410-3c65aa61f64e h1:QD7zkFNJz4TfiOv38QqBHZcR/RCtrQxJ4toj4DQYSV0=
gioui.org v0.0.0-20190930110410-3c65aa61f64e/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
gioui.org v0.0.0-20190930150022-6bc5ff247938 h1:V13kL8G+RP2/Bj8VF/asHQuIjBBbyqDDGxzbBIisM6g=
gioui.org v0.0.0-20190930150022-6bc5ff247938/go.mod h1:+CEjc9B//HrBfWsQOVxjCyih7HGIj3Pww1xFHVDZyyk=
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=