e956728fec02bf38d2cfc992e57ebaf9c4aeceb2 — Elias Naur a month ago 23bb2f4 master
cmd/scatter: update gio version

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

M cmd/scatter/ui.go
M go.mod
M go.sum
M cmd/scatter/ui.go => cmd/scatter/ui.go +264 -279
@@ 510,23 510,23 @@ func (p *contactsPage) Layout(gtx *layout.Context) {
 	if l.Dragging() {
 		key.HideInputOp{}.Add(gtx.Ops)
 	}
-	f := layout.Flex{Axis: layout.Vertical}
-	c1 := f.Rigid(gtx, func() {
-		p.topbar.Layout(gtx, p.env.insets, func() {
-			e := theme.Editor("Email address")
-			e.Font.Size = unit.Sp(20)
-			e.Color = rgb(0xffffff)
-			e.HintColor = rgb(0xbbbbbb)
-			e.Layout(gtx, p.searchEdit)
-		})
-	})
-	c2 := f.Flex(gtx, 1, func() {
-		gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
-		l.Layout(gtx, len(p.contacts), func(i int) {
-			p.contact(gtx, i)
-		})
-	})
-	f.Layout(gtx, c1, c2)
+	layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+		layout.Rigid(func() {
+			p.topbar.Layout(gtx, p.env.insets, func() {
+				e := theme.Editor("Email address")
+				e.Font.Size = unit.Sp(20)
+				e.Color = rgb(0xffffff)
+				e.HintColor = rgb(0xbbbbbb)
+				e.Layout(gtx, p.searchEdit)
+			})
+		}),
+		layout.Flexed(1, func() {
+			gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
+			l.Layout(gtx, len(p.contacts), func(i int) {
+				p.contact(gtx, i)
+			})
+		}),
+	)
 }
 
 func (p *contactsPage) contact(gtx *layout.Context, index int) {


@@ 539,23 539,22 @@ func (p *contactsPage) contact(gtx *layout.Context, index int) {
 	contact := p.contacts[index]
 	click := &p.clicks[index]
 	in.Layout(gtx, func() {
-		f := layout.Flex{Alignment: layout.Middle}
-		c1 := f.Rigid(gtx, func() {
-			in := layout.Inset{Right: unit.Dp(8)}
-			in.Layout(gtx, func() {
-				cc := clipCircle{}
-				cc.Layout(gtx, func() {
-					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.Color.Primary}.Layout(gtx)
+		layout.Flex{Alignment: layout.Middle}.Layout(gtx,
+			layout.Rigid(func() {
+				in := layout.Inset{Right: unit.Dp(8)}
+				in.Layout(gtx, func() {
+					cc := clipCircle{}
+					cc.Layout(gtx, func() {
+						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.Color.Primary}.Layout(gtx)
+					})
 				})
-			})
-		})
-		c2 := f.Flex(gtx, 1, func() {
-			theme.H6(contact.Address).Layout(gtx)
-		})
-
-		f.Layout(gtx, c1, c2)
+			}),
+			layout.Flexed(1, func() {
+				theme.H6(contact.Address).Layout(gtx)
+			}),
+		)
 	})
 	pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
 	click.Add(gtx.Ops)


@@ 571,35 570,35 @@ 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:    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)),
 	}
-	stackContent := stack.Rigid(gtx, func() {
-		insets.Layout(gtx, func() {
-			flex := layout.Flex{Alignment: layout.Middle}
-			backChild := flex.Rigid(gtx, func() {
-				if t.Back {
-					ico := (&icon{src: icons.NavigationArrowBack, size: unit.Dp(24)}).image(gtx, rgb(0xffffff))
-					ico.Add(gtx.Ops)
-					paint.PaintOp{Rect: f32.Rectangle{Max: toPointF(ico.Size())}}.Add(gtx.Ops)
-					gtx.Dimensions.Size = ico.Size()
-					gtx.Dimensions.Size.X += gtx.Px(unit.Dp(4))
-					pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
-					t.backClick.Add(gtx.Ops)
-				}
+	layout.Stack{Alignment: layout.SW}.Layout(gtx,
+		layout.Expanded(func() {
+			fill{theme.Color.Primary}.Layout(gtx)
+		}),
+		layout.Stacked(func() {
+			insets.Layout(gtx, func() {
+				layout.Flex{Alignment: layout.Middle}.Layout(gtx,
+					layout.Rigid(func() {
+						if t.Back {
+							ico := (&icon{src: icons.NavigationArrowBack, size: unit.Dp(24)}).image(gtx, rgb(0xffffff))
+							ico.Add(gtx.Ops)
+							paint.PaintOp{Rect: f32.Rectangle{Max: toPointF(ico.Size())}}.Add(gtx.Ops)
+							gtx.Dimensions.Size = ico.Size()
+							gtx.Dimensions.Size.X += gtx.Px(unit.Dp(4))
+							pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
+							t.backClick.Add(gtx.Ops)
+						}
+					}),
+					layout.Flexed(1, w),
+				)
 			})
-			content := flex.Flex(gtx, 1, w)
-			flex.Layout(gtx, backChild, content)
-		})
-	})
-	bg := stack.Expand(gtx, func() {
-		fill{theme.Color.Primary}.Layout(gtx)
-	})
-	stack.Layout(gtx, bg, stackContent)
+		}),
+	)
 }
 
 func newSignInPage(env *Env) *signInPage {


@@ 646,21 645,19 @@ func (p *signInPage) Event(gtx *layout.Context) interface{} {
 }
 
 func (p *signInPage) Layout(gtx *layout.Context) {
-	f := layout.Flex{Axis: layout.Vertical}
-
-	c1 := f.Rigid(gtx, func() {
-		var t Topbar
-		t.Layout(gtx, p.env.insets, func() {
-			lbl := theme.H6("Sign in")
-			lbl.Color = rgb(0xffffff)
-			lbl.Layout(gtx)
-		})
-	})
-
-	c2 := f.Flex(gtx, 1, func() {
-		p.layoutSigninForm(gtx)
-	})
-	f.Layout(gtx, c1, c2)
+	layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+		layout.Rigid(func() {
+			var t Topbar
+			t.Layout(gtx, p.env.insets, func() {
+				lbl := theme.H6("Sign in")
+				lbl.Color = rgb(0xffffff)
+				lbl.Layout(gtx)
+			})
+		}),
+		layout.Flexed(1, func() {
+			p.layoutSigninForm(gtx)
+		}),
+	)
 }
 
 func (p *signInPage) layoutSigninForm(gtx *layout.Context) {


@@ 692,19 689,18 @@ func (p *signInPage) layoutSigninForm(gtx *layout.Context) {
 }
 
 func (f *formField) Layout(gtx *layout.Context) {
-	fl := layout.Flex{Axis: layout.Vertical}
-
-	c1 := fl.Rigid(gtx, func() {
-		gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
-		header := theme.Caption(f.Header)
-		header.Font.Weight = text.Bold
-		header.Layout(gtx)
-		gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
-	})
-	c2 := fl.Rigid(gtx, func() {
-		theme.Editor(f.Hint).Layout(gtx, f.edit)
-	})
-	fl.Layout(gtx, c1, c2)
+	layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+		layout.Rigid(func() {
+			gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
+			header := theme.Caption(f.Header)
+			header.Font.Weight = text.Bold
+			header.Layout(gtx)
+			gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
+		}),
+		layout.Rigid(func() {
+			theme.Editor(f.Hint).Layout(gtx, f.edit)
+		}),
+	)
 }
 
 type Background struct {


@@ 737,7 733,7 @@ func (b *Background) Layout(gtx *layout.Context, w layout.Widget) {
 	}
 	paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
 	paint.PaintOp{Rect: f32.Rectangle{Max: f32.Point{X: width, Y: height}}}.Add(gtx.Ops)
-	macro.Add(gtx.Ops)
+	macro.Add()
 	stack.Pop()
 }
 


@@ 800,36 796,34 @@ func (p *threadsPage) fetchThreads() {
 }
 
 func (p *threadsPage) Layout(gtx *layout.Context) {
-	st := layout.Stack{Alignment: layout.SE}
-
-	c1 := st.Rigid(gtx, func() {
-		f := layout.Flex{Axis: layout.Vertical}
-
-		c1 := f.Rigid(gtx, func() {
-			var t Topbar
-			t.Layout(gtx, p.env.insets, func() {
-				lbl := theme.H6(p.account.User)
-				lbl.Color = rgb(0xffffff)
-				lbl.Layout(gtx)
-			})
-		})
-
-		c2 := f.Flex(gtx, 1, func() {
-			p.layoutThreads(gtx)
-		})
-		f.Layout(gtx, c1, c2)
-	})
-	c2 := st.Rigid(gtx, func() {
-		layout.Align(layout.SE).Layout(gtx, func() {
-			layout.Inset{
-				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() {
-				theme.IconButton(iconLib.create).Layout(gtx, p.fab)
+	layout.Stack{Alignment: layout.SE}.Layout(gtx,
+		layout.Stacked(func() {
+			layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+				layout.Rigid(func() {
+					var t Topbar
+					t.Layout(gtx, p.env.insets, func() {
+						lbl := theme.H6(p.account.User)
+						lbl.Color = rgb(0xffffff)
+						lbl.Layout(gtx)
+					})
+				}),
+
+				layout.Flexed(1, func() {
+					p.layoutThreads(gtx)
+				}),
+			)
+		}),
+		layout.Stacked(func() {
+			layout.Align(layout.SE).Layout(gtx, func() {
+				layout.Inset{
+					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() {
+					theme.IconButton(iconLib.create).Layout(gtx, p.fab)
+				})
 			})
-		})
-	})
-	st.Layout(gtx, c1, c2)
+		}),
+	)
 }
 
 func (p *threadsPage) layoutThreads(gtx *layout.Context) {


@@ 874,81 868,75 @@ func (p *threadsPage) thread(gtx *layout.Context, index int) {
 		Right: unit.Max(gtx, unit.Dp(16), p.env.insets.Right),
 	}
 	in.Layout(gtx, func() {
-		elem := layout.Flex{Axis: layout.Vertical}
-		c1 := elem.Rigid(gtx, func() {
-			in := layout.Inset{Top: unit.Dp(8), Bottom: unit.Dp(8)}
-			in.Layout(gtx, func() {
-				f := centerRowOpts()
-				c1 := f.Rigid(gtx, func() {
+		in := layout.Inset{Top: unit.Dp(8), Bottom: unit.Dp(8)}
+		in.Layout(gtx, func() {
+			centerRowOpts().Layout(gtx,
+				layout.Rigid(func() {
 					in := layout.Inset{Right: unit.Dp(12)}
 					cc := clipCircle{}
 					in.Layout(gtx, func() {
 						cc.Layout(gtx, func() {
-							st := layout.Stack{Alignment: layout.Center}
-
-							// Background color
-							c1 := st.Rigid(gtx, func() {
-								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)]
-								fill{color}.Layout(gtx)
-							})
-
-							// Contact initial.
-							c2 := st.Rigid(gtx, func() {
-								initial := ""
-								for _, c := range t.ID {
-									initial = string(unicode.ToUpper(c))
-									break
-								}
-								lbl := theme.H5(initial)
-								lbl.Color = rgb(0xffffff)
-								lbl.Layout(gtx)
-							})
-							st.Layout(gtx, c1, c2)
+							layout.Stack{Alignment: layout.Center}.Layout(gtx,
+								// Background color
+								layout.Stacked(func() {
+									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)]
+									fill{color}.Layout(gtx)
+								}),
+								// Contact initial.
+								layout.Stacked(func() {
+									initial := ""
+									for _, c := range t.ID {
+										initial = string(unicode.ToUpper(c))
+										break
+									}
+									lbl := theme.H5(initial)
+									lbl.Color = rgb(0xffffff)
+									lbl.Layout(gtx)
+								}),
+							)
 						})
 					})
-				})
-				c2 := f.Rigid(gtx, func() {
-					f := column()
-					c1 := f.Rigid(gtx, func() {
-						f := baseline()
-						c1 := f.Rigid(gtx, func() {
-							lbl := theme.H6(t.ID)
-							lbl.Font.Weight = fontWeight
-							lbl.Layout(gtx)
-						})
-						c2 := f.Flex(gtx, 1, func() {
-							gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
-							in := layout.Inset{Left: unit.Dp(2)}
+				}),
+				layout.Rigid(func() {
+					column().Layout(gtx,
+						layout.Rigid(func() {
+							baseline().Layout(gtx,
+								layout.Rigid(func() {
+									lbl := theme.H6(t.ID)
+									lbl.Font.Weight = fontWeight
+									lbl.Layout(gtx)
+								}),
+								layout.Flexed(1, func() {
+									gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
+									in := layout.Inset{Left: unit.Dp(2)}
+									in.Layout(gtx, func() {
+										lbl := theme.Caption(formatTime(t.Updated))
+										lbl.Color = bgtexcol
+										lbl.Alignment = text.End
+										lbl.Font.Weight = fontWeight
+										lbl.Layout(gtx)
+									})
+								}),
+							)
+						}),
+						layout.Rigid(func() {
+							in := layout.Inset{Top: unit.Dp(6)}
 							in.Layout(gtx, func() {
-								lbl := theme.Caption(formatTime(t.Updated))
+								lbl := theme.Body2(t.Snippet)
 								lbl.Color = bgtexcol
-								lbl.Alignment = text.End
 								lbl.Font.Weight = fontWeight
+								lbl.MaxLines = 1
 								lbl.Layout(gtx)
 							})
-						})
-						f.Layout(gtx, c1, c2)
-					})
-					c2 := f.Rigid(gtx, func() {
-						in := layout.Inset{Top: unit.Dp(6)}
-						in.Layout(gtx, func() {
-							lbl := theme.Body2(t.Snippet)
-							lbl.Color = bgtexcol
-							lbl.Font.Weight = fontWeight
-							lbl.MaxLines = 1
-							lbl.Layout(gtx)
-						})
-					})
-					f.Layout(gtx, c1, c2)
-				})
-				f.Layout(gtx, c1, c2)
-			})
-			pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
-			click.Add(gtx.Ops)
+						}),
+					)
+				}),
+			)
 		})
-		elem.Layout(gtx, c1)
+		pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops)
+		click.Add(gtx.Ops)
 	})
 }
 


@@ 1030,81 1018,80 @@ func (p *threadPage) Layout(gtx *layout.Context) {
 	case p.messages = <-p.result:
 	default:
 	}
-	f := layout.Flex{Axis: layout.Vertical}
-	c1 := f.Rigid(gtx, func() {
-		p.topbar.Layout(gtx, p.env.insets, func() {
-			lbl := theme.H6(p.thread.ID)
-			lbl.Color = rgb(0xffffff)
-			lbl.Layout(gtx)
-		})
-	})
+	layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+		layout.Rigid(func() {
+			p.topbar.Layout(gtx, p.env.insets, func() {
+				lbl := theme.H6(p.thread.ID)
+				lbl.Color = rgb(0xffffff)
+				lbl.Layout(gtx)
+			})
+		}),
 
-	c3 := f.Rigid(gtx, func() {
-		in := layout.Inset{
-			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 {
-			case p.thread.PendingInvitation:
-				theme.Button("Accept invitation").Layout(gtx, p.accept)
-			case p.env.client.ContainsSession(p.thread.ID):
-				p.layoutMessageBox(gtx)
-			default:
-				theme.Button("Send invitation").Layout(gtx, p.invite)
-			}
-		})
-	})
+		layout.Flexed(1, func() {
+			gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
+			l.Layout(gtx, len(p.messages), func(i int) {
+				p.message(gtx, i)
+			})
+		}),
 
-	c2 := f.Flex(gtx, 1, func() {
-		gtx.Constraints.Height.Min = gtx.Constraints.Height.Max
-		l.Layout(gtx, len(p.messages), func(i int) {
-			p.message(gtx, i)
-		})
-	})
-	f.Layout(gtx, c1, c2, c3)
+		layout.Rigid(func() {
+			in := layout.Inset{
+				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 {
+				case p.thread.PendingInvitation:
+					theme.Button("Accept invitation").Layout(gtx, p.accept)
+				case p.env.client.ContainsSession(p.thread.ID):
+					p.layoutMessageBox(gtx)
+				default:
+					theme.Button("Send invitation").Layout(gtx, p.invite)
+				}
+			})
+		}),
+	)
 }
 
 func (p *threadPage) layoutMessageBox(gtx *layout.Context) {
 	if mh := gtx.Px(unit.Dp(100)); gtx.Constraints.Height.Max > mh {
 		gtx.Constraints.Height.Max = mh
 	}
-	f := layout.Flex{Alignment: layout.End}
 
 	var sendHeight int
-	c2 := f.Rigid(gtx, func() {
-		in := layout.Inset{Left: unit.Dp(8)}
-		in.Layout(gtx, func() {
-			btn := theme.IconButton(iconLib.send)
-			btn.Size = unit.Dp(48)
-			btn.Padding = unit.Dp(12)
-			btn.Layout(gtx, p.send)
-			sendHeight = gtx.Dimensions.Size.Y
-		})
-	})
-
-	c1 := f.Flex(gtx, 1, func() {
-		gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
-		if gtx.Constraints.Height.Min < sendHeight {
-			gtx.Constraints.Height.Min = sendHeight
-		}
-		bg := Background{
-			Color:  rgb(0xeeeeee),
-			Inset:  layout.Inset{Left: unit.Dp(8), Right: unit.Dp(8)},
-			Radius: unit.Dp(10),
-		}
-		bg.Layout(gtx, func() {
-			layout.Align(layout.W).Layout(gtx, func() {
-				gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
-				ed := theme.Editor("Send a message")
-				ed.Font.Size = unit.Sp(14)
-				ed.Layout(gtx, p.msgEdit)
+	layout.Flex{Alignment: layout.End}.Layout(gtx,
+		layout.Flexed(1, func() {
+			gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
+			if gtx.Constraints.Height.Min < sendHeight {
+				gtx.Constraints.Height.Min = sendHeight
+			}
+			bg := Background{
+				Color:  rgb(0xeeeeee),
+				Inset:  layout.Inset{Left: unit.Dp(8), Right: unit.Dp(8)},
+				Radius: unit.Dp(10),
+			}
+			bg.Layout(gtx, func() {
+				layout.Align(layout.W).Layout(gtx, func() {
+					gtx.Constraints.Width.Min = gtx.Constraints.Width.Max
+					ed := theme.Editor("Send a message")
+					ed.Font.Size = unit.Sp(14)
+					ed.Layout(gtx, p.msgEdit)
+				})
 			})
-		})
-	})
-	f.Layout(gtx, c1, c2)
+		}),
+		layout.Rigid(func() {
+			in := layout.Inset{Left: unit.Dp(8)}
+			in.Layout(gtx, func() {
+				btn := theme.IconButton(iconLib.send)
+				btn.Size = unit.Dp(48)
+				btn.Padding = unit.Dp(12)
+				btn.Layout(gtx, p.send)
+				sendHeight = gtx.Dimensions.Size.Y
+			})
+		}),
+	)
 }
 
 func (p *threadPage) message(gtx *layout.Context, index int) {


@@ 1131,49 1118,47 @@ func (p *threadPage) message(gtx *layout.Context, index int) {
 				Radius: unit.Dp(10),
 			}
 			bg.Layout(gtx, func() {
-				f := layout.Flex{Axis: layout.Vertical}
-
 				var msgWidth int
-				c1 := f.Rigid(gtx, func() {
-					lbl := theme.Body2(msg.Message)
-					lbl.Color = msgCol
-					lbl.Layout(gtx)
-					gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
-					msgWidth = gtx.Dimensions.Size.X
-				})
-
-				c2 := f.Rigid(gtx, func() {
-					gtx.Constraints.Width.Min = msgWidth
-					f := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween, Alignment: layout.Middle}
-
-					var children []layout.FlexChild
-					child := f.Rigid(gtx, func() {
-						time := formatTime(msg.Time)
-						lbl := theme.Caption(time)
-						lbl.Color = timecol
+				layout.Flex{Axis: layout.Vertical}.Layout(gtx,
+					layout.Rigid(func() {
+						lbl := theme.Body2(msg.Message)
+						lbl.Color = msgCol
 						lbl.Layout(gtx)
-					})
-					children = append(children, child)
-
-					if msg.Own {
-						child := f.Rigid(gtx, func() {
-							in := layout.Inset{Left: unit.Dp(12)}
-							in.Layout(gtx, func() {
-								checkmark := p.checkmark.image(gtx, timecol)
-								sz := checkmark.Size()
-								if msg.Sent {
-									checkmark.Add(gtx.Ops)
-									paint.PaintOp{Rect: f32.Rectangle{Max: toPointF(sz)}}.Add(gtx.Ops)
-								}
-								gtx.Dimensions = layout.Dimensions{Size: sz}
-							})
+						gtx.Dimensions.Size.Y += gtx.Px(unit.Dp(4))
+						msgWidth = gtx.Dimensions.Size.X
+					}),
+
+					layout.Rigid(func() {
+						gtx.Constraints.Width.Min = msgWidth
+						f := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceBetween, Alignment: layout.Middle}
+
+						var children []layout.FlexChild
+						child := layout.Rigid(func() {
+							time := formatTime(msg.Time)
+							lbl := theme.Caption(time)
+							lbl.Color = timecol
+							lbl.Layout(gtx)
 						})
 						children = append(children, child)
-					}
-					f.Layout(gtx, children...)
-				})
 
-				f.Layout(gtx, c1, c2)
+						if msg.Own {
+							child := layout.Rigid(func() {
+								in := layout.Inset{Left: unit.Dp(12)}
+								in.Layout(gtx, func() {
+									checkmark := p.checkmark.image(gtx, timecol)
+									sz := checkmark.Size()
+									if msg.Sent {
+										checkmark.Add(gtx.Ops)
+										paint.PaintOp{Rect: f32.Rectangle{Max: toPointF(sz)}}.Add(gtx.Ops)
+									}
+									gtx.Dimensions = layout.Dimensions{Size: sz}
+								})
+							})
+							children = append(children, child)
+						}
+						f.Layout(gtx, children...)
+					}),
+				)
 			})
 		})
 	})


@@ 1275,7 1260,7 @@ func (cc *clipCircle) Layout(gtx *layout.Context, w layout.Widget) {
 		Rect: f32.Rectangle{Max: f32.Point{X: szf, Y: szf}},
 		NE:   rr, NW: rr, SE: rr, SW: rr,
 	}.Op(gtx.Ops).Add(gtx.Ops)
-	macro.Add(gtx.Ops)
+	macro.Add()
 	stack.Pop()
 }
 

M go.mod => go.mod +1 -1
@@ 3,7 3,7 @@ module scatter.im
 go 1.13
 
 require (
-	gioui.org v0.0.0-20191202122135-c0beeb5e77dc
+	gioui.org v0.0.0-20191211234831-0bfcac97344d
 	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,6 1,6 @@
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-gioui.org v0.0.0-20191202122135-c0beeb5e77dc h1:2eKdkpHtriwdbRvEXKDzukEE/w5cbQDaTJSHsXB+tT0=
-gioui.org v0.0.0-20191202122135-c0beeb5e77dc/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY=
+gioui.org v0.0.0-20191211234831-0bfcac97344d h1:yxzaD2asbV2q/oGukWJ1JrIBp0zm45RIA8ngnY9L6Ic=
+gioui.org v0.0.0-20191211234831-0bfcac97344d/go.mod h1:KqFFi2Dq5gYA3FJ0sDOt8OBXoMsuxMtE8v2f0JExXAY=
 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=