...
1 package etk
2
3 import (
4 "image"
5
6 "github.com/hajimehoshi/ebiten/v2"
7 )
8
9
10
11 type Grid struct {
12 *Box
13
14 columnSizes []int
15 rowSizes []int
16
17 columnPadding int
18 rowPadding int
19
20 cellPositions [][2]int
21 cellSpans [][2]int
22
23 updated bool
24 }
25
26
27 func NewGrid() *Grid {
28 return &Grid{
29 Box: NewBox(),
30 }
31 }
32
33
34 func (g *Grid) SetRect(r image.Rectangle) {
35 g.Lock()
36 defer g.Unlock()
37
38 g.Box.rect = r
39 g.updated = true
40 }
41
42
43
44 func (g *Grid) SetColumnSizes(size ...int) {
45 g.Lock()
46 defer g.Unlock()
47
48 g.columnSizes = size
49 g.updated = true
50 }
51
52
53 func (g *Grid) SetColumnPadding(padding int) {
54 g.Lock()
55 defer g.Unlock()
56
57 g.columnPadding = padding
58 g.updated = true
59 }
60
61
62
63 func (g *Grid) SetRowSizes(size ...int) {
64 g.Lock()
65 defer g.Unlock()
66
67 g.rowSizes = size
68 g.updated = true
69 }
70
71
72 func (g *Grid) SetRowPadding(padding int) {
73 g.Lock()
74 defer g.Unlock()
75
76 g.rowPadding = padding
77 g.updated = true
78 }
79
80
81
82 func (g *Grid) AddChild(wgt ...Widget) {
83 g.Box.AddChild(wgt...)
84
85 for i := 0; i < len(wgt); i++ {
86 g.cellPositions = append(g.cellPositions, [2]int{0, 0})
87 g.cellSpans = append(g.cellSpans, [2]int{1, 1})
88 }
89
90 g.updated = true
91 }
92
93
94
95 func (g *Grid) AddChildAt(wgt Widget, x int, y int, columns int, rows int) {
96 g.Box.AddChild(wgt)
97
98 g.cellPositions = append(g.cellPositions, [2]int{x, y})
99 g.cellSpans = append(g.cellSpans, [2]int{columns, rows})
100
101 g.updated = true
102 }
103
104
105 func (g *Grid) Clear() {
106 g.Lock()
107 defer g.Unlock()
108
109 g.children = g.children[:0]
110 g.cellPositions = g.cellPositions[:0]
111 g.cellSpans = g.cellSpans[:0]
112 g.updated = true
113 }
114
115
116 func (g *Grid) HandleKeyboard(ebiten.Key, rune) (handled bool, err error) {
117 if g.updated {
118 g.reposition()
119 g.updated = false
120 }
121
122 return false, nil
123 }
124
125
126 func (g *Grid) HandleMouse(cursor image.Point, pressed bool, clicked bool) (handled bool, err error) {
127 if g.updated {
128 g.reposition()
129 g.updated = false
130 }
131
132 return false, nil
133 }
134
135
136 func (g *Grid) Draw(screen *ebiten.Image) error {
137 g.Lock()
138 defer g.Unlock()
139
140 if g.updated {
141 g.reposition()
142 g.updated = false
143 }
144
145 return nil
146 }
147
148 func (g *Grid) reposition() {
149 if g.rect.Min.X == 0 && g.rect.Min.Y == 0 && g.rect.Max.X == 0 && g.rect.Max.Y == 0 {
150 return
151 }
152
153 gridX, gridY := g.rect.Min.X, g.rect.Min.Y
154 gridW, gridH := g.rect.Dx(), g.rect.Dy()
155
156
157 var (
158 numColumns int
159 numRows int
160
161 maxColumnProportion = 1
162 maxRowProportion = 1
163
164 numColumnProportions = make(map[int]int)
165 numRowProportions = make(map[int]int)
166 )
167 for i := range g.children {
168 position := g.cellPositions[i]
169 x, y := position[0], position[1]
170
171 span := g.cellSpans[i]
172 w, h := span[0], span[1]
173
174 if x+w > numColumns {
175 numColumns = x + w
176 }
177 if y+h > numRows {
178 numRows = y + h
179 }
180
181 if -w > maxColumnProportion {
182 maxColumnProportion = -w
183 }
184 if -h > maxRowProportion {
185 maxRowProportion = -h
186 }
187 }
188
189
190 numColumnSizes := len(g.columnSizes)
191 numRowSizes := len(g.rowSizes)
192
193 columnWidths := make([]int, numColumns)
194 var usedWidth int
195 for i := 0; i < numColumns; i++ {
196 if i >= numColumnSizes {
197 columnWidths[i] = -1
198 } else {
199 columnWidths[i] = g.columnSizes[i]
200
201 if g.columnSizes[i] > 0 {
202 usedWidth += g.columnSizes[i]
203 }
204 }
205
206 if columnWidths[i] < 0 {
207 numColumnProportions[-columnWidths[i]]++
208 }
209 }
210 remainingWidth := gridW - usedWidth - (g.columnPadding * (numColumns + 1))
211 columnProportions := make([]int, maxColumnProportion)
212 for i := 0; i < maxColumnProportion; i++ {
213 columnProportions[i] = remainingWidth / (i + 1)
214 }
215 for i := 0; i < numColumns; i++ {
216 if columnWidths[i] < 0 {
217 columnWidths[i] = columnProportions[-columnWidths[i]-1] / numColumnProportions[-columnWidths[i]]
218 }
219 }
220
221 rowHeights := make([]int, numRows)
222 var usedHeight int
223 for i := 0; i < numRows; i++ {
224 if i >= numRowSizes {
225 rowHeights[i] = -1
226 } else {
227 rowHeights[i] = g.rowSizes[i]
228
229 if g.rowSizes[i] > 0 {
230 usedHeight += g.rowSizes[i]
231 }
232 }
233
234 if rowHeights[i] < 0 {
235 numRowProportions[-rowHeights[i]]++
236 }
237 }
238 remainingHeight := gridH - usedHeight - (g.rowPadding * (numRows + 1))
239 rowProportions := make([]int, maxRowProportion)
240 for i := 0; i < maxRowProportion; i++ {
241 rowProportions[i] = remainingHeight / (i + 1)
242 }
243 for i := 0; i < numRows; i++ {
244 if rowHeights[i] < 0 {
245 rowHeights[i] = rowProportions[-rowHeights[i]-1] / numRowProportions[-rowHeights[i]]
246 }
247 }
248
249 columnPositions := make([]int, numColumns)
250 {
251 x := g.columnPadding
252 for i := 0; i < numColumns; i++ {
253 columnPositions[i] = x
254 x += columnWidths[i] + g.columnPadding
255 }
256 }
257
258 rowPositions := make([]int, numRows)
259 {
260 y := g.rowPadding
261 for i := 0; i < numRows; i++ {
262 rowPositions[i] = y
263 y += rowHeights[i] + g.rowPadding
264 }
265 }
266
267
268 for i, child := range g.children {
269 position := g.cellPositions[i]
270 span := g.cellSpans[i]
271
272 x := columnPositions[position[0]]
273 y := rowPositions[position[1]]
274
275 var w, h int
276 for j := 0; j < span[0]; j++ {
277 if j > 0 {
278 w += g.columnPadding
279 }
280 w += columnWidths[position[0]+j]
281 }
282 for j := 0; j < span[1]; j++ {
283 if j > 0 {
284 h += g.rowPadding
285 }
286 h += rowHeights[position[1]+j]
287 }
288
289 child.SetRect(image.Rect(gridX+x, gridY+y, gridX+x+w, gridY+y+h))
290 }
291 }
292
View as plain text