/
tabs.go
179 lines (165 loc) · 4.13 KB
/
tabs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: Unlicense OR MIT
package main
import (
"fmt"
"image"
"image/color"
"log"
"math"
"os"
"gioui.org/app"
"gioui.org/f32"
"gioui.org/io/system"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"github.com/hauntedness/giom/play/common"
"gioui.org/font/gofont"
)
func main() {
go func() {
defer os.Exit(0)
w := app.NewWindow()
if err := loop(w); err != nil {
log.Fatal(err)
}
}()
app.Main()
}
func loop(w *app.Window) error {
th := material.NewTheme(gofont.Collection())
var ops op.Ops
for {
e := <-w.Events()
switch e := e.(type) {
case system.DestroyEvent:
return e.Err
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
drawTabs(gtx, th)
e.Frame(gtx.Ops)
}
}
}
var (
tabs Tabs
slider Slider
)
type Tabs struct {
list layout.List
tabs []Tab
selected int
}
type Tab struct {
btn widget.Clickable
Title string
}
func init() {
for i := 1; i <= 3; i++ {
tabs.tabs = append(tabs.tabs,
Tab{Title: fmt.Sprintf("Tab %d", i)},
)
}
}
func drawTabs(gtx layout.Context, th *material.Theme) layout.Dimensions {
flex := layout.Flex{
Axis: layout.Vertical,
Alignment: layout.Middle,
}
tabsDims := flex.Layout(gtx,
// tabs
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
// center the tabs
return widget.Border{
Color: common.ColorGreen,
CornerRadius: 0,
Width: 0,
}.Layout(gtx,
func(gtx layout.Context) layout.Dimensions {
return layout.Center.Layout(gtx,
func(gtx layout.Context) layout.Dimensions {
// cros axis also center
tabs.list.Alignment = layout.Middle
dims := tabs.list.Layout(gtx, len(tabs.tabs),
func(gtx layout.Context, tabIdx int) layout.Dimensions {
t := &tabs.tabs[tabIdx]
if t.btn.Clicked() {
if tabs.selected < tabIdx {
slider.PushLeft()
} else if tabs.selected > tabIdx {
slider.PushRight()
}
tabs.selected = tabIdx
}
var tabWidth int
return layout.Stack{Alignment: layout.S}.Layout(gtx,
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
dims := material.Clickable(gtx, &t.btn, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(12)).Layout(gtx,
material.H6(th, t.Title).Layout,
)
})
tabWidth = dims.Size.X
return dims
}),
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
if tabs.selected != tabIdx {
return layout.Dimensions{}
}
tabHeight := gtx.Dp(unit.Dp(4))
tabRect := image.Rect(0, 0, tabWidth, tabHeight)
paint.FillShape(gtx.Ops, th.Palette.ContrastBg, clip.Rect(tabRect).Op())
return layout.Dimensions{
Size: image.Point{X: tabWidth, Y: tabHeight},
}
}),
)
})
return dims
})
})
}),
// slider
layout.Flexed(1,
func(gtx layout.Context) layout.Dimensions {
return slider.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
fill(gtx, dynamicColor(tabs.selected), dynamicColor(tabs.selected+1))
return layout.Center.Layout(gtx,
material.H1(th, fmt.Sprintf("Tab content #%d", tabs.selected+1)).Layout,
)
})
}),
)
return tabsDims
}
func fill(gtx layout.Context, col1, col2 color.NRGBA) {
dr := image.Rectangle{Max: gtx.Constraints.Min}
paint.FillShape(gtx.Ops,
color.NRGBA{R: 0, G: 0, B: 0, A: 0xFF},
clip.Rect(dr).Op(),
)
col2.R = byte(float32(col2.R))
col2.G = byte(float32(col2.G))
col2.B = byte(float32(col2.B))
paint.LinearGradientOp{
Stop1: f32.Pt(float32(dr.Min.X), 0),
Stop2: f32.Pt(float32(dr.Max.X), 0),
Color1: col1,
Color2: col2,
}.Add(gtx.Ops)
defer clip.Rect(dr).Push(gtx.Ops).Pop()
paint.PaintOp{}.Add(gtx.Ops)
}
func dynamicColor(i int) color.NRGBA {
sn, cs := math.Sincos(float64(i) * math.Phi)
return color.NRGBA{
R: 0xA0 + byte(0x30*sn),
G: 0xA0 + byte(0x30*cs),
B: 0xD0,
A: 0xFF,
}
}