-
Notifications
You must be signed in to change notification settings - Fork 0
/
flex.go
181 lines (154 loc) · 3.77 KB
/
flex.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
180
181
package etk
import (
"image"
"github.com/hajimehoshi/ebiten/v2"
)
// Flex is a flexible stack-based layout which may be oriented horizontally or
// vertically. Children are positioned with equal spacing by default. A minimum
// size may instead be specified via SetChildSize, causing children to be
// positioned similar to a flexbox, where each child either has the minimum
// size or the child stretches to fill the remaining row or column.
type Flex struct {
*Box
vertical bool
childWidth, childHeight int
columnGap, rowGap int
modified bool
}
// NewFlex returns a new Flex widget.
func NewFlex() *Flex {
return &Flex{
Box: NewBox(),
columnGap: 5,
rowGap: 5,
}
}
// SetRect sets the position and size of the widget.
func (f *Flex) SetRect(r image.Rectangle) {
f.Lock()
defer f.Unlock()
f.Box.rect = r
f.modified = true
}
// SetGaps sets the gaps between each child in the Flex.
func (f *Flex) SetGaps(columnGap int, rowGap int) {
f.Lock()
defer f.Unlock()
if f.columnGap == columnGap && f.rowGap == rowGap {
return
}
f.columnGap, f.rowGap = columnGap, rowGap
f.modified = true
}
// SetChildSize sets the minimum size of each child in the Flex.
func (f *Flex) SetChildSize(width int, height int) {
f.Lock()
defer f.Unlock()
if f.childWidth == width && f.childHeight == height {
return
}
f.childWidth, f.childHeight = width, height
f.modified = true
}
// SetVertical sets the orientation of the child widget stacking.
func (f *Flex) SetVertical(v bool) {
f.Lock()
defer f.Unlock()
if f.vertical == v {
return
}
f.vertical = v
f.modified = true
}
// AddChild adds a child to the widget.
func (f *Flex) AddChild(w ...Widget) {
f.Lock()
defer f.Unlock()
f.children = append(f.children, w...)
f.modified = true
}
// HandleKeyboard is called when a keyboard event occurs.
func (f *Flex) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
return false, nil
}
// HandleMouse is called when a mouse event occurs.
func (f *Flex) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
return false, nil
}
// Draw draws the widget on the screen.
func (f *Flex) Draw(screen *ebiten.Image) error {
f.Lock()
defer f.Unlock()
if f.modified {
f.reposition()
f.modified = false
}
for _, child := range f.children {
err := child.Draw(screen)
if err != nil {
return err
}
}
return nil
}
func (f *Flex) reposition() {
r := f.rect
childWidth := f.childWidth
if childWidth == 0 {
if f.vertical {
childWidth = r.Dx()
} else if len(f.children) > 0 {
var gapSpace int
if len(f.children) > 1 {
gapSpace = f.columnGap * (len(f.children) - 1)
}
childWidth = (r.Dx() - gapSpace) / len(f.children)
}
}
childHeight := f.childHeight
if childHeight == 0 {
if f.vertical && len(f.children) > 0 {
var gapSpace int
if len(f.children) > 1 {
gapSpace = f.rowGap * (len(f.children) - 1)
}
childHeight = (r.Dy() - gapSpace) / len(f.children)
} else {
childHeight = r.Dy()
}
}
rects := make([]image.Rectangle, len(f.children))
x1, y1 := r.Min.X, r.Min.Y
if f.vertical {
for i := range f.children {
x2, y2 := x1+childWidth, y1+childHeight
if y2 > r.Max.Y {
return
}
rects[i] = image.Rect(x1, y1, x2, y2)
y1 += childHeight + f.rowGap
if y1 > r.Max.Y-childHeight {
rects[i].Max.Y = r.Max.Y
x1 += childWidth + f.columnGap
y1 = r.Min.Y
}
}
} else {
for i := range f.children {
x2, y2 := x1+childWidth, y1+childHeight
if x2 > r.Max.X {
return
}
rects[i] = image.Rect(x1, y1, x2, y2)
x1 += childWidth + f.columnGap
if x1 > r.Max.X-childWidth {
rects[i].Max.X = r.Max.X
y1 += childHeight + f.rowGap
x1 = r.Min.X
}
}
}
for i, child := range f.children {
child.SetRect(rects[i])
}
}