blob: 8f35a3b3ca469c2d2bf551c6fd7e4b9cfac5b453 [file] [log] [blame]
giolekva313ee2b2021-12-15 15:17:29 +04001package main
2
3import (
giolekva313ee2b2021-12-15 15:17:29 +04004 "image"
5 "image/color"
giolekva313ee2b2021-12-15 15:17:29 +04006
7 "gioui.org/layout"
8 "gioui.org/op/clip"
9 "gioui.org/op/paint"
10 "gioui.org/unit"
11 "gioui.org/widget"
giolekva96202c52021-12-18 12:45:34 +040012 "gioui.org/widget/material"
giolekva313ee2b2021-12-15 15:17:29 +040013)
14
15type (
16 C = layout.Context
17 D = layout.Dimensions
18)
19
giolekva3f0dcda2021-12-22 23:32:49 +040020type DeviceCapabilities struct {
21 HasCamera bool
22}
23
giolekva313ee2b2021-12-15 15:17:29 +040024type UI struct {
giolekva3f0dcda2021-12-22 23:32:49 +040025 th *material.Theme
26 cap DeviceCapabilities
27
giolekva313ee2b2021-12-15 15:17:29 +040028 invite struct {
29 open widget.Clickable
30 show bool
31 qr image.Image
32 }
33
giolekva3f0dcda2021-12-22 23:32:49 +040034 approve struct {
35 open widget.Clickable
36 show bool
37 }
38
giolekva313ee2b2021-12-15 15:17:29 +040039 join struct {
giolekva3f0dcda2021-12-22 23:32:49 +040040 open widget.Clickable
41 show bool
42 qr image.Image
giolekva313ee2b2021-12-15 15:17:29 +040043 }
44}
45
giolekva3f0dcda2021-12-22 23:32:49 +040046func NewUI(th *material.Theme, cap DeviceCapabilities) *UI {
47 return &UI{th: th, cap: cap}
giolekvaf58a7692021-12-15 18:05:39 +040048}
49
50func (ui *UI) InviteQRGenerated(img image.Image) {
51 ui.invite.qr = img
giolekva313ee2b2021-12-15 15:17:29 +040052}
53
giolekva3f0dcda2021-12-22 23:32:49 +040054func (ui *UI) JoinQRGenerated(img image.Image) {
55 ui.join.qr = img
56}
57
giolekva313ee2b2021-12-15 15:17:29 +040058func (ui *UI) OnBack() bool {
59 if ui.invite.show {
60 ui.invite.show = false
61 return true
giolekva3f0dcda2021-12-22 23:32:49 +040062 } else if ui.approve.show {
63 ui.approve.show = false
64 return true
giolekva313ee2b2021-12-15 15:17:29 +040065 } else if ui.join.show {
66 ui.join.show = false
67 return true
68 }
69 return false
70}
71
72func (ui *UI) Layout(gtx C) []UIEvent {
73 var events []UIEvent
74 if ui.invite.open.Clicked() {
giolekva313ee2b2021-12-15 15:17:29 +040075 ui.invite.show = true
giolekva3f0dcda2021-12-22 23:32:49 +040076 ui.approve.show = false
77 ui.join.show = false
giolekva313ee2b2021-12-15 15:17:29 +040078 ui.invite.qr = nil
giolekvaf58a7692021-12-15 18:05:39 +040079 events = append(events, EventGetInviteQRCode{})
giolekva3f0dcda2021-12-22 23:32:49 +040080 } else if ui.approve.open.Clicked() {
81 events = append(events, EventApproveOther{})
giolekva313ee2b2021-12-15 15:17:29 +040082 } else if ui.join.open.Clicked() {
giolekva3f0dcda2021-12-22 23:32:49 +040083 if ui.cap.HasCamera {
84 events = append(events, EventScanBarcode{})
85 } else {
86 ui.invite.show = false
87 ui.approve.show = false
88 ui.join.show = true
89 events = append(events, EventGetJoinQRCode{})
90 }
giolekva313ee2b2021-12-15 15:17:29 +040091 }
92 if ui.invite.show {
93 ui.layout(gtx, ui.layoutInvite)
94 } else if ui.join.show {
95 ui.layout(gtx, ui.layoutJoin)
96 } else {
97 ui.layout(gtx, ui.layoutMain)
98 }
99 return events
100}
101
102func (ui *UI) layout(gtx C, mainPanel layout.Widget) D {
103 return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
104 layout.Flexed(10, func(gtx C) D {
105 return mainPanel(gtx)
106 }),
107 layout.Flexed(1, func(gtx C) D {
108 return ui.layoutActions(gtx)
109 }),
110 )
111}
112
113func ColorBox(gtx layout.Context, size image.Point, color color.NRGBA) layout.Dimensions {
114 defer clip.Rect{Max: size}.Push(gtx.Ops).Pop()
115 paint.ColorOp{Color: color}.Add(gtx.Ops)
116 paint.PaintOp{}.Add(gtx.Ops)
117 return layout.Dimensions{Size: size}
118}
119
120func (ui *UI) layoutMain(gtx C) D {
121 return ColorBox(gtx, gtx.Constraints.Min, color.NRGBA{B: 255, A: 255})
122}
123
124func (ui *UI) layoutActions(gtx C) D {
giolekva3f0dcda2021-12-22 23:32:49 +0400125 return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceAround, WeightSum: 3.2}.Layout(gtx,
giolekva313ee2b2021-12-15 15:17:29 +0400126 layout.Flexed(1, func(gtx C) D {
giolekva8d6a0ca2021-12-19 17:42:25 +0400127 b := material.Button(ui.th, &ui.invite.open, "Invite")
128 b.CornerRadius = unit.Px(20)
129 return b.Layout(gtx)
giolekva313ee2b2021-12-15 15:17:29 +0400130 }),
131 layout.Flexed(1, func(gtx C) D {
giolekva3f0dcda2021-12-22 23:32:49 +0400132 b := material.Button(ui.th, &ui.approve.open, "Approve")
133 b.CornerRadius = unit.Px(20)
134 return b.Layout(gtx)
135 }),
136 layout.Flexed(1, func(gtx C) D {
giolekva8d6a0ca2021-12-19 17:42:25 +0400137 b := material.Button(ui.th, &ui.join.open, "Join")
138 b.CornerRadius = unit.Px(20)
139 return b.Layout(gtx)
giolekva313ee2b2021-12-15 15:17:29 +0400140 }),
141 )
142}
143
giolekva3f0dcda2021-12-22 23:32:49 +0400144func layoutQR(gtx C, qr image.Image) D {
145 if qr == nil {
giolekvaf58a7692021-12-15 18:05:39 +0400146 return ColorBox(gtx, gtx.Constraints.Max, color.NRGBA{})
giolekva313ee2b2021-12-15 15:17:29 +0400147 }
giolekva3f0dcda2021-12-22 23:32:49 +0400148 d := qr.Bounds().Max.Sub(qr.Bounds().Min)
giolekva313ee2b2021-12-15 15:17:29 +0400149 return layout.Inset{
150 Left: unit.Px(0.5 * float32(gtx.Constraints.Max.X-d.X)),
151 Top: unit.Px(0.5 * float32(gtx.Constraints.Max.Y-d.Y)),
152 }.Layout(gtx, func(gtx C) D {
giolekva3f0dcda2021-12-22 23:32:49 +0400153 paint.NewImageOp(qr).Add(gtx.Ops)
giolekva313ee2b2021-12-15 15:17:29 +0400154 paint.PaintOp{}.Add(gtx.Ops)
155 return D{Size: gtx.Constraints.Max}
156 })
giolekva3f0dcda2021-12-22 23:32:49 +0400157
158}
159
160func (ui *UI) layoutInvite(gtx C) D {
161 return layoutQR(gtx, ui.invite.qr)
giolekva313ee2b2021-12-15 15:17:29 +0400162}
163
164func (ui *UI) layoutJoin(gtx C) D {
giolekva3f0dcda2021-12-22 23:32:49 +0400165 return layoutQR(gtx, ui.join.qr)
giolekva313ee2b2021-12-15 15:17:29 +0400166}