Flexbox
Flexbox is a high level positioning plugin that allows for dynamic positioning of elements based apon container sizing. To learn more about flexbox refer to this MDN article, in this document we will go into the impleamentation of flexbox rather than the usage of it.
"display": "flex", "justify-content": "*", "align-content": "*", "align-items": "*", "flex-wrap": "*", "flex-direction": "*",
# Flex Properties
justify-content | align-content | align-items | flex-wrap | flex-direction |
---|---|---|---|---|
normal | normal | normal | nowrap | row |
center | center | center | wrap | column |
flex-start | flex-start | flex-start | row-reverse | |
flex-end | flex-end | flex-end | column-reverse | |
space-between | space-between | stretch | ||
space-around | space-around | baseline | ||
space-evenly | stretch |
# order?(go)
asd
1package flex
2
3import (
4 "gui/cstyle"
5 "gui/cstyle/plugins/inline"
6 "gui/element"
7 "gui/utils"
8 "sort"
9 "strings"
10)
11
12func Init() cstyle.Plugin {
13 return cstyle.Plugin{
14 Selector: func(n *element.Node) bool {
15 styles := map[string]string{
16 "display": "flex",
17 }
18 matches := true
19 for name, value := range styles {
20 if (n.Style[name] != value || n.Style[name] == "") && !(value == "*") {
21 matches = false
22 }
23 }
24 return matches
25 },
26 Level: 3,
27 Handler: func(n *element.Node, state *map[string]element.State) {
28 s := *state
29 self := s[n.Properties.Id]
30
31 verbs := strings.Split(n.Style["flex-direction"], "-")
32 flexDirection := verbs[0]
33 if flexDirection == "" {
34 flexDirection = "row"
35 }
36 flexReversed := false
37 if len(verbs) > 1 {
38 flexReversed = true
39 }
40
41 var flexWrapped bool
42 if n.Style["flex-wrap"] == "wrap" {
43 flexWrapped = true
44 } else {
45 flexWrapped = false
46 }
47
48 alignContent := n.Style["align-content"]
49 if alignContent == "" {
50 alignContent = "normal"
51 }
52 alignItems := n.Style["align-items"]
53 if alignItems == "" {
54 alignItems = "normal"
55 }
56 justifyItems := n.Style["justify-items"]
57 if justifyItems == "" {
58 justifyItems = "normal"
59 }
60
61 justifyContent := n.Style["justify-content"]
62 if justifyContent == "" {
63 justifyContent = "normal"
64 }
65 // fmt.Println(flexDirection, flexReversed, flexWrapped, hAlign, vAlign, justifyItems, justifyContent)
66 rows := [][]int{}
67 maxH := float32(0)
68 // maxW := float32(0)
69
70 // Get inital sizing
71 textTotal := 0
72 textCounts := []int{}
73 widths := []float32{}
74 // heights := []float32{}
75 innerSizes := [][]float32{}
76 minWidths := []float32{}
77 minHeights := []float32{}
78 maxWidths := []float32{}
79 // maxHeights := []float32{}
80 for _, v := range n.Children {
81 count := countText(v)
82 textTotal += count
83 textCounts = append(textCounts, count)
84
85 minw := getMinWidth(&v, state)
86 minWidths = append(minWidths, minw)
87
88 maxw := getMaxWidth(&v, state)
89 maxWidths = append(maxWidths, maxw)
90
91 w, h := getInnerSize(&v, state)
92
93 minh := getMinHeight(&v, state)
94 minHeights = append(minHeights, minh)
95
96 // maxh := getMaxHeight(&v, state)
97 // maxHeights = append(maxHeights, maxh)
98 innerSizes = append(innerSizes, []float32{w, h})
99 }
100 selfWidth := (self.Width - self.Padding.Left) - self.Padding.Right
101 selfHeight := (self.Height - self.Padding.Top) - self.Padding.Bottom
102
103 if flexDirection == "row" {
104 // if the elements are less than the size of the parent, don't change widths. Just set mins
105 if !flexWrapped {
106 if add2d(innerSizes, 0) < selfWidth {
107 for i := range innerSizes {
108 // for i, _ := range n.Children {
109 // vState := s[v.Properties.Id]
110
111 w := innerSizes[i][0]
112 // w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
113 widths = append(widths, w)
114 }
115 } else {
116 // Modifiy the widths so they aren't under the mins
117 for i, v := range n.Children {
118 vState := s[v.Properties.Id]
119
120 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
121 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
122
123 if w < minWidths[i] {
124 selfWidth -= minWidths[i] + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
125 textTotal -= textCounts[i]
126 textCounts[i] = 0
127 }
128
129 }
130 for i, v := range n.Children {
131 vState := s[v.Properties.Id]
132
133 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
134 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
135 // (w!=w) is of NaN
136 if w < minWidths[i] || (w != w) {
137 w = minWidths[i]
138 }
139 widths = append(widths, w)
140 }
141 }
142 // Apply the new widths
143 fState := s[n.Children[0].Properties.Id]
144 for i, v := range n.Children {
145 vState := s[v.Properties.Id]
146
147 vState.Width = widths[i]
148 xStore := vState.X
149 if i > 0 {
150 sState := s[n.Children[i-1].Properties.Id]
151 vState.X = sState.X + sState.Width + sState.Margin.Right + vState.Margin.Left + sState.Border.Width + vState.Border.Width
152 propagateOffsets(&v, xStore, vState.Y, vState.X, fState.Y+vState.Margin.Top, state)
153 }
154
155 vState.Y = fState.Y + vState.Margin.Top
156
157 (*state)[v.Properties.Id] = vState
158 deInline(&v, state)
159 applyInline(&v, state)
160 applyBlock(&v, state)
161 _, h := getInnerSize(&v, state)
162 h = utils.Max(h, vState.Height)
163 maxH = utils.Max(maxH, h)
164 }
165 // When not wrapping everything will be on the same row
166 rows = append(rows, []int{0, len(n.Children), int(maxH)})
167 } else {
168 // Flex Wrapped
169 sum := innerSizes[0][0]
170 for i := 0; i < len(n.Children); i++ {
171 v := n.Children[i]
172 vState := s[v.Properties.Id]
173
174 // if the next plus current will break then
175 w := innerSizes[i][0]
176 if i > 0 {
177 sib := s[n.Children[i-1].Properties.Id]
178 if maxWidths[i] > selfWidth {
179 w = selfWidth - vState.Margin.Left - vState.Margin.Right - (vState.Border.Width * 2)
180 }
181 if w+sum > selfWidth {
182 sum = w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
183 } else {
184 propagateOffsets(&v, vState.X, vState.Y, vState.X, sib.Y, state)
185 vState.Y = sib.Y
186 (*state)[v.Properties.Id] = vState
187 sum += w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
188 }
189 }
190
191 widths = append(widths, w)
192 }
193
194 // Move the elements into the correct position
195 start := 0
196 var prevOffset float32
197 for i := 0; i < len(n.Children); i++ {
198 v := n.Children[i]
199 vState := s[v.Properties.Id]
200
201 vState.Width = widths[i]
202 xStore := vState.X
203 yStore := vState.Y
204
205 if i > 0 {
206 sib := s[n.Children[i-1].Properties.Id]
207 if vState.Y+prevOffset == sib.Y {
208 yStore += prevOffset
209
210 if vState.Height < sib.Height {
211 vState.Height = minHeight(v, state, sib.Height)
212 }
213 // Shift right if on a row with sibling
214 xStore = sib.X + sib.Width + sib.Margin.Right + sib.Border.Width + vState.Margin.Left + vState.Border.Width
215 } else {
216 // Shift under sibling
217 yStore = sib.Y + sib.Height + sib.Margin.Top + sib.Margin.Bottom + sib.Border.Width*2
218 prevOffset = yStore - vState.Y
219 rows = append(rows, []int{start, i, int(maxH)})
220 start = i
221 maxH = 0
222 }
223 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
224 }
225 vState.X = xStore
226 vState.Y = yStore
227
228 (*state)[v.Properties.Id] = vState
229 deInline(&v, state)
230 applyInline(&v, state)
231 applyBlock(&v, state)
232 _, h := getInnerSize(&v, state)
233 h = utils.Max(h, vState.Height)
234 maxH = utils.Max(maxH, h)
235 vState.Height = minHeight(v, state, h)
236 (*state)[v.Properties.Id] = vState
237 }
238 if start < len(n.Children) {
239 rows = append(rows, []int{start, len(n.Children), int(maxH)})
240 }
241 }
242
243 var totalHeight float32
244
245 for _, v := range rows {
246 totalHeight += float32(v[2])
247 for i := v[0]; i < v[1]; i++ {
248 vState := s[n.Children[i].Properties.Id]
249 if vState.Height == float32(v[2]) {
250 totalHeight += vState.Margin.Top + vState.Margin.Bottom
251 v[2] += int(vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))
252 }
253 }
254 }
255 var yOffset float32
256 for _, v := range rows {
257 for i := v[0]; i < v[1]; i++ {
258 vState := s[n.Children[i].Properties.Id]
259 // height := float32(v[2])
260 if n.Children[i].Style["height"] == "" && n.Children[i].Style["min-height"] == "" && !flexWrapped {
261 height := self.Height - self.Padding.Top - self.Padding.Bottom - vState.Margin.Top - vState.Margin.Bottom - (vState.Border.Width * 2)
262 vState.Height = minHeight(n.Children[i], state, height)
263 } else if flexWrapped && (n.Style["height"] != "" || n.Style["min-height"] != "") {
264 height := ((selfHeight / totalHeight) * float32(v[2])) - (vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))
265 vState.Height = minHeight(n.Children[i], state, height)
266 yStore := vState.Y
267 vState.Y = self.Y + self.Padding.Top + yOffset + vState.Margin.Top + vState.Border.Width
268 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
269 } else if flexWrapped {
270 if vState.Height+vState.Margin.Top+vState.Margin.Bottom+(vState.Border.Width*2) != float32(v[2]) {
271 height := vState.Height - (vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))
272 vState.Height = minHeight(n.Children[i], state, height)
273 }
274 yStore := vState.Y
275 vState.Y = self.Y + self.Padding.Top + yOffset + vState.Margin.Top + vState.Border.Width
276 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
277 }
278 (*state)[n.Children[i].Properties.Id] = vState
279 }
280 if flexWrapped && (n.Style["height"] != "" || n.Style["min-height"] != "") {
281 yOffset += ((selfHeight / totalHeight) * float32(v[2]))
282 } else if flexWrapped {
283 yOffset += float32(v[2])
284 }
285 }
286 // Reverse elements
287 if flexReversed {
288 rowReverse(rows, n, state)
289 }
290
291 if justifyContent != "" && justifyContent != "normal" {
292 justifyRow(rows, n, state, justifyContent, flexReversed)
293 }
294
295 if alignContent != "normal" && flexWrapped {
296 alignRow(rows, n, state, alignItems, alignContent)
297 }
298
299 }
300
301 if flexDirection == "column" {
302 if !flexWrapped {
303 // if the container has a size restriction
304 var totalHeight, maxH float32
305 var fixedHeightElements int
306 for i, v := range n.Children {
307 vState := s[v.Properties.Id]
308 if v.Style["min-height"] != "" {
309 selfHeight -= vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
310 fixedHeightElements++
311 maxH = utils.Max(maxH, vState.Height)
312 } else {
313 // accoutn for element min height
314 totalHeight += minHeights[i] + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
315 maxH = utils.Max(maxH, minHeights[i])
316 }
317 }
318
319 heightDelta := selfHeight - totalHeight
320 if heightDelta < 0 {
321 heightDelta = -heightDelta
322 }
323 heightAdj := heightDelta / float32(len(n.Children)-fixedHeightElements)
324 if heightAdj < 0 {
325 heightAdj = -heightAdj
326 }
327 // We are calculating the amount a element needs to shrink because of its siblings
328 for i, v := range n.Children {
329 vState := s[v.Properties.Id]
330 yStore := vState.Y
331 vState.Height = setHeight(&v, state, minHeights[i]-heightAdj)
332 if i > 0 {
333 sib := s[n.Children[i-1].Properties.Id]
334
335 vState.Y = sib.Y + sib.Height + sib.Margin.Bottom + sib.Border.Width + vState.Margin.Top + vState.Border.Width
336 }
337 propagateOffsets(&v, vState.X, yStore, vState.X, vState.Y, state)
338
339 (*state)[v.Properties.Id] = vState
340 }
341
342 rows = append(rows, []int{0, len(n.Children) - 1, int(maxH)})
343
344 } else {
345 // need to redo this, with col wrap make each row the width of the longest element unless set otherwise (width, max-width) get width with min-width
346 var colHeight float32
347 var colIndex int
348 cols := [][][]float32{}
349
350 // Map elements to columns
351 for i, v := range n.Children {
352 vState := s[v.Properties.Id]
353 height := vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
354 if colHeight+height > selfHeight {
355 colHeight = height
356 colIndex++
357 width := innerSizes[i][0]
358 // width := vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2) + w
359 if colIndex >= len(cols) {
360 cols = append(cols, [][]float32{})
361 }
362 cols[colIndex] = append(cols[colIndex], []float32{float32(i), height, width})
363 } else {
364 colHeight += height
365 width := innerSizes[i][0]
366 // width := vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2) + w
367 if colIndex >= len(cols) {
368 cols = append(cols, [][]float32{})
369 }
370 cols[colIndex] = append(cols[colIndex], []float32{float32(i), height, width})
371 }
372 }
373
374 // Find the max total width of all columns
375 var totalMaxWidth float32
376 maxWidths := []float32{}
377 for _, col := range cols {
378 var maxWidth, maxHeight float32
379 for _, element := range col {
380 maxHeight = utils.Max(utils.Max(element[1], minHeights[int(element[0])]), maxHeight)
381 maxWidth = utils.Max(element[2], maxWidth)
382 }
383 rows = append(rows, []int{int(col[0][0]), int(col[len(col)-1][0]), int(maxHeight)})
384 totalMaxWidth += maxWidth
385 maxWidths = append(maxWidths, maxWidth)
386 }
387 // Move the elements into the correct position
388 var xOffset float32
389 var index int
390 for i, col := range cols {
391 // Move the elements into the correct position
392 yOffset := self.Y + self.Border.Width + self.Padding.Top
393 var marginOffset float32
394 for _, element := range col {
395 v := n.Children[int(element[0])]
396 vState := s[v.Properties.Id]
397 xStore := vState.X
398 yStore := vState.Y
399 vState.X = self.X + self.Padding.Left + self.Border.Width + xOffset + vState.Margin.Left + vState.Border.Width
400 vState.Y = yOffset + vState.Margin.Top + vState.Border.Width
401 propagateOffsets(&v, xStore, yStore, vState.X, vState.Y, state)
402 if innerSizes[index][0] == maxWidths[i] {
403 marginOffset = vState.Margin.Right + vState.Margin.Left + (vState.Border.Width * 2)
404 }
405 vState.Width = setWidth(&v, state, maxWidths[i])
406 yOffset += vState.Margin.Top + vState.Border.Width + vState.Height + vState.Margin.Bottom + vState.Border.Width
407 (*state)[v.Properties.Id] = vState
408 index++
409 }
410 xOffset += maxWidths[i] + marginOffset
411 }
412
413 }
414
415 if flexReversed {
416 colReverse(rows, n, state)
417 }
418
419 if justifyContent != "normal" {
420 justifyCols(rows, n, state, justifyContent, flexReversed)
421 }
422 if alignContent != "normal" || alignItems != "normal" {
423 alignCols(rows, n, state, alignItems, alignContent, innerSizes)
424 }
425 }
426 if n.Style["height"] == "" && n.Style["min-height"] == "" {
427 _, h := getInnerSize(n, state)
428 self.Height = h
429 }
430 (*state)[n.Properties.Id] = self
431 },
432 }
433}
434
435func applyBlock(n *element.Node, state *map[string]element.State) {
436 if len(n.Children) > 0 {
437 accum := float32(0)
438 inlineOffset := float32(0)
439 s := *state
440 lastHeight := float32(0)
441 baseY := s[n.Children[0].Properties.Id].Y
442 for i := 0; i < len(n.Children); i++ {
443 v := &n.Children[i]
444 vState := s[v.Properties.Id]
445
446 if v.Style["display"] != "block" {
447 vState.Y += inlineOffset
448 accum = (vState.Y - baseY)
449 lastHeight = vState.Height
450 } else if v.Style["position"] != "absolute" {
451 vState.Y += accum
452 inlineOffset += (vState.Height + (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom) + lastHeight
453 }
454 (*state)[v.Properties.Id] = vState
455 }
456 }
457}
458
459func deInline(n *element.Node, state *map[string]element.State) {
460 s := *state
461 // self := s[n.Properties.Id]
462 baseX := float32(-1)
463 baseY := float32(-1)
464 for _, v := range n.Children {
465 vState := s[v.Properties.Id]
466
467 if v.Style["display"] == "inline" {
468 if baseX < 0 && baseY < 0 {
469 baseX = vState.X
470 baseY = vState.Y
471 } else {
472 vState.X = baseX
473 vState.Y = baseY
474 (*state)[v.Properties.Id] = vState
475
476 }
477 } else {
478 baseX = float32(-1)
479 baseY = float32(-1)
480 }
481
482 if len(v.Children) > 0 {
483 deInline(&v, state)
484 }
485 }
486
487}
488
489func applyInline(n *element.Node, state *map[string]element.State) {
490 pl := inline.Init()
491 for i := 0; i < len(n.Children); i++ {
492 v := &n.Children[i]
493
494 if len(v.Children) > 0 {
495 applyInline(v, state)
496 }
497
498 if pl.Selector(v) {
499 pl.Handler(v, state)
500 }
501 }
502}
503
504func propagateOffsets(n *element.Node, prevx, prevy, newx, newy float32, state *map[string]element.State) {
505 s := *state
506 for _, v := range n.Children {
507 vState := s[v.Properties.Id]
508 xStore := (vState.X - prevx) + newx
509 yStore := (vState.Y - prevy) + newy
510
511 if len(v.Children) > 0 {
512 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
513 }
514 vState.X = xStore
515 vState.Y = yStore
516 (*state)[v.Properties.Id] = vState
517 }
518
519}
520
521func countText(n element.Node) int {
522 count := 0
523 groups := []int{}
524 for _, v := range n.Children {
525 if v.TagName == "notaspan" {
526 count += 1
527 }
528 if v.Style["display"] == "block" {
529 groups = append(groups, count)
530 count = 0
531 }
532 if len(v.Children) > 0 {
533 count += countText(v)
534 }
535 }
536 groups = append(groups, count)
537
538 sort.Slice(groups, func(i, j int) bool {
539 return groups[i] > groups[j]
540 })
541 return groups[0]
542}
543
544func minHeight(n element.Node, state *map[string]element.State, prev float32) float32 {
545 s := *state
546 self := s[n.Properties.Id]
547 if n.Style["min-height"] != "" {
548 mw := utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Width)
549 return utils.Max(prev, mw)
550 } else {
551 return prev
552 }
553
554}
555
556func getMinHeight(n *element.Node, state *map[string]element.State) float32 {
557 s := *state
558 self := s[n.Properties.Id]
559 selfHeight := float32(0)
560
561 if len(n.Children) > 0 {
562 for _, v := range n.Children {
563 selfHeight = utils.Max(selfHeight, getNodeHeight(&v, state))
564 }
565 } else {
566 selfHeight = self.Height
567 }
568 if n.Style["min-height"] != "" {
569 mh := utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Width)
570 selfHeight = utils.Max(mh, selfHeight)
571 }
572
573 selfHeight += self.Padding.Top + self.Padding.Bottom
574 return selfHeight
575}
576
577func getMinWidth(n *element.Node, state *map[string]element.State) float32 {
578 s := *state
579 self := s[n.Properties.Id]
580 selfWidth := float32(0)
581
582 if len(n.Children) > 0 {
583 for _, v := range n.Children {
584 selfWidth = utils.Max(selfWidth, getNodeWidth(&v, state))
585 }
586 } else {
587 selfWidth = self.Width
588 }
589 if n.Style["min-width"] != "" {
590 mw := utils.ConvertToPixels(n.Style["min-width"], self.EM, s[n.Parent.Properties.Id].Width)
591 selfWidth = utils.Max(mw, selfWidth)
592 }
593
594 selfWidth += self.Padding.Left + self.Padding.Right
595 return selfWidth
596}
597func getMaxWidth(n *element.Node, state *map[string]element.State) float32 {
598 s := *state
599 self := s[n.Properties.Id]
600 selfWidth := float32(0)
601
602 if len(n.Children) > 0 {
603 var maxRowWidth, rowWidth float32
604
605 for _, v := range n.Children {
606 rowWidth += getNodeWidth(&v, state)
607 if v.Style["display"] != "inline" {
608 maxRowWidth = utils.Max(rowWidth, maxRowWidth)
609 rowWidth = 0
610 }
611 }
612 selfWidth = utils.Max(rowWidth, maxRowWidth)
613 } else {
614 selfWidth = self.Width
615 }
616
617 selfWidth += self.Padding.Left + self.Padding.Right
618 return selfWidth
619}
620
621func getNodeWidth(n *element.Node, state *map[string]element.State) float32 {
622 s := *state
623 self := s[n.Properties.Id]
624 w := float32(0)
625 w += self.Padding.Left
626 w += self.Padding.Right
627
628 w += self.Margin.Left
629 w += self.Margin.Right
630
631 w += self.Width
632
633 w += self.Border.Width * 2
634
635 for _, v := range n.Children {
636 w = utils.Max(w, getNodeWidth(&v, state))
637 }
638
639 return w
640}
641
642func setWidth(n *element.Node, state *map[string]element.State, width float32) float32 {
643 s := *state
644 self := s[n.Properties.Id]
645
646 if n.Style["width"] != "" {
647 return utils.ConvertToPixels(n.Style["width"], self.EM, s[n.Parent.Properties.Id].Width) + self.Padding.Left + self.Padding.Right
648 }
649
650 var maxWidth, minWidth float32
651 maxWidth = 10e9
652 if n.Style["min-width"] != "" {
653 minWidth = utils.ConvertToPixels(n.Style["min-width"], self.EM, s[n.Parent.Properties.Id].Width)
654 minWidth += self.Padding.Left + self.Padding.Right
655 }
656 if n.Style["max-width"] != "" {
657 maxWidth = utils.ConvertToPixels(n.Style["min-width"], self.EM, s[n.Parent.Properties.Id].Width)
658 maxWidth += self.Padding.Left + self.Padding.Right
659 }
660
661 return utils.Max(minWidth, utils.Min(width, maxWidth))
662}
663
664func setHeight(n *element.Node, state *map[string]element.State, height float32) float32 {
665 s := *state
666 self := s[n.Properties.Id]
667
668 if n.Style["height"] != "" {
669 return utils.ConvertToPixels(n.Style["height"], self.EM, s[n.Parent.Properties.Id].Height)
670 }
671
672 var maxHeight, minHeight float32
673 maxHeight = 10e9
674 if n.Style["min-height"] != "" {
675 minHeight = utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Height)
676 }
677 if n.Style["max-height"] != "" {
678 maxHeight = utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Height)
679 }
680
681 return utils.Max(minHeight, utils.Min(height, maxHeight))
682}
683
684func getNodeHeight(n *element.Node, state *map[string]element.State) float32 {
685 s := *state
686 self := s[n.Properties.Id]
687 h := float32(0)
688 h += self.Padding.Top
689 h += self.Padding.Bottom
690
691 h += self.Margin.Top
692 h += self.Margin.Bottom
693
694 h += self.Height
695
696 h += self.Border.Width * 2
697
698 for _, v := range n.Children {
699 h = utils.Max(h, getNodeHeight(&v, state))
700 }
701
702 return h
703}
704
705func getInnerSize(n *element.Node, state *map[string]element.State) (float32, float32) {
706 s := *state
707 self := s[n.Properties.Id]
708
709 minx := float32(10e10)
710 maxw := float32(0)
711 miny := float32(10e10)
712 maxh := float32(0)
713 for _, v := range n.Children {
714 vState := s[v.Properties.Id]
715 minx = utils.Min(vState.X, minx)
716 miny = utils.Min(vState.Y-vState.Margin.Top, miny)
717 // Don't add the top or left because the x&y values already take that into account
718 hOffset := (vState.Border.Width) + vState.Margin.Bottom
719 wOffset := (vState.Border.Width) + vState.Margin.Right
720 maxw = utils.Max(vState.X+vState.Width+wOffset, maxw)
721 maxh = utils.Max(vState.Y+vState.Height+hOffset, maxh)
722 }
723 w := maxw - minx
724 h := maxh - miny
725
726 w += self.Padding.Left + self.Padding.Right
727 h += self.Padding.Top + self.Padding.Bottom
728 if n.Style["width"] != "" {
729 w = self.Width
730 }
731 if n.Style["height"] != "" {
732 h = self.Height
733 }
734
735 return w, h
736}
737
738func add2d(arr [][]float32, index int) float32 {
739 var sum float32
740 if len(arr) == 0 {
741 return sum
742 }
743
744 for i := 0; i < len(arr); i++ {
745 if len(arr[i]) <= index {
746 return sum
747 }
748 sum += arr[i][index]
749 }
750
751 return sum
752}
753
754func colReverse(cols [][]int, n *element.Node, state *map[string]element.State) {
755 s := *state
756 for _, col := range cols {
757 tempNodes := []element.Node{}
758 tempStates := []element.State{}
759
760 for i := col[1]; i >= col[0]; i-- {
761 tempNodes = append(tempNodes, n.Children[i])
762 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
763 }
764
765 for i := 0; i < len(tempStates); i++ {
766 e := col[0] + i
767 vState := s[n.Children[e].Properties.Id]
768 propagateOffsets(&n.Children[e], vState.X, vState.Y, tempStates[i].X, tempStates[i].Y, state)
769 vState.Y = tempStates[i].Y
770 (*state)[n.Children[e].Properties.Id] = vState
771 }
772 for i := 0; i < len(tempStates); i++ {
773 e := col[0] + i
774 n.Children[e] = tempNodes[i]
775 }
776 }
777}
778
779func rowReverse(rows [][]int, n *element.Node, state *map[string]element.State) {
780 s := *state
781 for _, row := range rows {
782 tempNodes := []element.Node{}
783 tempStates := []element.State{}
784
785 for i := row[1] - 1; i >= row[0]; i-- {
786 tempNodes = append(tempNodes, n.Children[i])
787 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
788 }
789
790 for i := 0; i < len(tempStates); i++ {
791 e := row[0] + i
792 vState := s[n.Children[e].Properties.Id]
793 propagateOffsets(&n.Children[e], vState.X, vState.Y, tempStates[i].X, tempStates[i].Y, state)
794 vState.X = tempStates[i].X
795 (*state)[n.Children[e].Properties.Id] = vState
796 }
797 for i := 0; i < len(tempStates); i++ {
798 e := row[0] + i
799 n.Children[e] = tempNodes[i]
800 }
801
802 for i := row[1] - 1; i >= row[0]; i-- {
803 vState := s[n.Children[i].Properties.Id]
804 var xChng float32
805 if i < row[1]-1 {
806 sib := s[n.Children[i+1].Properties.Id]
807 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
808 } else {
809 parent := s[n.Properties.Id]
810 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
811
812 }
813 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
814 vState.X = xChng
815 (*state)[n.Children[i].Properties.Id] = vState
816 }
817 }
818}
819
820func justifyRow(rows [][]int, n *element.Node, state *map[string]element.State, justify string, reversed bool) {
821 s := *state
822 for _, row := range rows {
823
824 if (justify == "flex-end" || justify == "end" || justify == "right") && !reversed {
825 for i := row[1] - 1; i >= row[0]; i-- {
826 vState := s[n.Children[i].Properties.Id]
827 var xChng float32
828 if i < row[1]-1 {
829 sib := s[n.Children[i+1].Properties.Id]
830 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
831 } else {
832 parent := s[n.Properties.Id]
833 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
834
835 }
836 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
837 vState.X = xChng
838 (*state)[n.Children[i].Properties.Id] = vState
839 }
840 } else if (justify == "flex-end" || justify == "start" || justify == "left" || justify == "normal") && reversed {
841 for i := row[0]; i < row[1]; i++ {
842 vState := s[n.Children[i].Properties.Id]
843 var xChng float32
844 if i > row[0] {
845 sib := s[n.Children[i-1].Properties.Id]
846 xChng = sib.X + sib.Width + (sib.Border.Width * 2) + sib.Margin.Right + vState.Margin.Left + vState.Border.Width
847 } else {
848 parent := s[n.Properties.Id]
849 xChng = parent.X + parent.Padding.Right + vState.Margin.Left + vState.Border.Width + parent.Border.Width
850
851 }
852 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
853 vState.X = xChng
854 (*state)[n.Children[i].Properties.Id] = vState
855 }
856 } else if justify == "center" {
857 // get width of row then center (by getting last x + w + mr + b)
858 f := s[n.Children[row[0]].Properties.Id]
859 l := s[n.Children[row[1]-1].Properties.Id]
860 parent := s[n.Properties.Id]
861 po := parent.X + parent.Border.Width
862 offset := (parent.Width - ((f.X - po) + (l.X - po) + l.Width + f.Border.Width + l.Border.Width)) / 2
863
864 for i := row[0]; i < row[1]; i++ {
865 vState := s[n.Children[i].Properties.Id]
866
867 if !reversed {
868 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
869 vState.X += offset
870 } else {
871 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
872 vState.X -= offset
873 }
874 (*state)[n.Children[i].Properties.Id] = vState
875 }
876
877 } else if justify == "space-between" {
878 // get width of row then center (by getting last x + w + mr + b)
879 f := s[n.Children[row[0]].Properties.Id]
880 l := s[n.Children[row[1]-1].Properties.Id]
881 parent := s[n.Properties.Id]
882 po := parent.Border.Width + parent.Width
883 po -= parent.Padding.Left + parent.Padding.Right
884
885 // make po repersent the total space between elements
886 for i := row[0]; i < row[1]; i++ {
887 vState := s[n.Children[i].Properties.Id]
888 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
889 }
890
891 po /= float32(((row[1]) - row[0]) - 1)
892
893 if (row[1]-1)-row[0] > 0 {
894 for i := row[0]; i < row[1]; i++ {
895 vState := s[n.Children[i].Properties.Id]
896 var offset float32
897 if i == row[0] {
898 offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
899 } else if i == row[1]-1 {
900 offset = (parent.X + parent.Width) - (l.Margin.Right + l.Border.Width + l.Width + parent.Padding.Right)
901 } else {
902 if !reversed {
903 offset = vState.X + (po * float32(i-row[0]))
904 } else {
905 offset = vState.X - (po * float32(((row[1]-1)-row[0])-(i-row[0])))
906 }
907
908 }
909
910 propagateOffsets(&n.Children[i], vState.X, vState.Y, offset, vState.Y, state)
911 vState.X = offset
912 (*state)[n.Children[i].Properties.Id] = vState
913 }
914 }
915 // else {
916
917 // this is/was causing issues, removed and it fixed its self
918
919 // if there is one element move left
920 // vState := s[n.Children[(row[1]-1)-row[0]].Properties.Id]
921 // var offset float32
922
923 // if !reversed {
924 // offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
925 // propagateOffsets(&n.Children[(row[1]-1)-row[0]], vState.X, vState.Y, offset, vState.Y, state)
926 // vState.X = offset
927
928 // (*state)[n.Children[(row[1]-1)-row[0]].Properties.Id] = vState
929 // }
930
931 // }
932
933 } else if justify == "space-evenly" {
934 // get width of row then center (by getting last x + w + mr + b)
935 parent := s[n.Properties.Id]
936 po := parent.Border.Width + parent.Width
937 po -= parent.Padding.Left + parent.Padding.Right
938
939 // make po repersent the total space between elements
940 for i := row[0]; i < row[1]; i++ {
941 vState := s[n.Children[i].Properties.Id]
942 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
943 }
944
945 po /= float32(((row[1]) - row[0]) + 1)
946
947 // get width of row then center (by getting last x + w + mr + b)
948
949 for i := row[0]; i < row[1]; i++ {
950 vState := s[n.Children[i].Properties.Id]
951
952 if !reversed {
953 offset := po * (float32(i-row[0]) + 1)
954 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
955 vState.X += offset
956 } else {
957 offset := po * float32(((row[1]-1)-row[0])-((i-row[0])-1))
958
959 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
960 vState.X -= offset
961 }
962 (*state)[n.Children[i].Properties.Id] = vState
963 }
964
965 } else if justify == "space-around" {
966 // get width of row then center (by getting last x + w + mr + b)
967 parent := s[n.Properties.Id]
968 po := parent.Border.Width + parent.Width
969 po -= parent.Padding.Left + parent.Padding.Right
970
971 // make po repersent the total space between elements
972 for i := row[0]; i < row[1]; i++ {
973 vState := s[n.Children[i].Properties.Id]
974 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
975 }
976
977 po /= float32(((row[1]) - row[0]))
978
979 // get width of row then center (by getting last x + w + mr + b)
980
981 for i := row[0]; i < row[1]; i++ {
982 vState := s[n.Children[i].Properties.Id]
983
984 if !reversed {
985 m := (float32(i-row[0]) + 1)
986 if i-row[0] == 0 {
987 m = 0.5
988 } else {
989 m -= 0.5
990 }
991 offset := po * m
992 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
993 vState.X += offset
994 } else {
995 m := float32(((row[1] - 1) - row[0]) - ((i - row[0]) - 1))
996 m -= 0.5
997 offset := po * m
998
999 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
1000 vState.X -= offset
1001 }
1002 (*state)[n.Children[i].Properties.Id] = vState
1003 }
1004
1005 }
1006
1007 }
1008}
1009
1010func alignRow(rows [][]int, n *element.Node, state *map[string]element.State, align, content string) {
1011 // !ISSUE: Baseline isn't properly impleamented
1012 s := *state
1013 self := s[n.Properties.Id]
1014
1015 maxes := []float32{}
1016 var maxesTotal float32
1017 for _, row := range rows {
1018 var maxH float32
1019 for i := row[0]; i < row[1]; i++ {
1020 v := n.Children[i]
1021 vState := s[v.Properties.Id]
1022 _, h := getInnerSize(&v, state)
1023 h = minHeight(v, state, h)
1024 h = setHeight(&v, state, h)
1025 vState.Height = h
1026 h += vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
1027 maxH = utils.Max(maxH, h)
1028 (*state)[v.Properties.Id] = vState
1029 }
1030 maxes = append(maxes, maxH)
1031 maxesTotal += maxH
1032 }
1033
1034 os := ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows))
1035 if os < 0 || content != "normal" {
1036 os = 0
1037 }
1038
1039 var contentOffset float32
1040
1041 if content == "center" {
1042 contentOffset = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / 2
1043 } else if content == "end" || content == "flex-end" {
1044 contentOffset = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal)
1045 } else if content == "start" || content == "flex-start" || content == "baseline" {
1046 // This is redundent but it helps keep track
1047 contentOffset = 0
1048 } else if content == "space-between" {
1049 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows)-1)
1050 } else if content == "space-around" {
1051 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows))
1052 contentOffset = os / 2
1053 } else if content == "space-evenly" {
1054 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows)+1)
1055 contentOffset = os
1056 }
1057
1058 for c, row := range rows {
1059 maxH := maxes[c]
1060 var sum float32
1061 for i := 0; i < c; i++ {
1062 sum += maxes[i]
1063 }
1064 if align == "start" || align == "flex-start" || align == "self-start" || align == "normal" {
1065 for i := row[0]; i < row[1]; i++ {
1066 vState := s[n.Children[i].Properties.Id]
1067
1068 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1069
1070 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1071 offset += ((os) * float32(c))
1072 }
1073
1074 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1075 vState.Y = offset
1076 (*state)[n.Children[i].Properties.Id] = vState
1077 }
1078 } else if align == "center" {
1079 for i := row[0]; i < row[1]; i++ {
1080 vState := s[n.Children[i].Properties.Id]
1081
1082 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1083
1084 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1085 offset += (os * float32(c+1)) - (os / 2)
1086 }
1087
1088 if vState.Height+vState.Margin.Top+vState.Margin.Bottom+(vState.Border.Width*2) < maxH {
1089 offset += (maxH - (vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))) / 2
1090 }
1091 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1092 vState.Y = offset
1093 (*state)[n.Children[i].Properties.Id] = vState
1094 }
1095 } else if align == "end" || align == "flex-end" || align == "self-end" {
1096 for i := row[0]; i < row[1]; i++ {
1097 vState := s[n.Children[i].Properties.Id]
1098
1099 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1100
1101 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1102 offset += os * float32(c+1)
1103 }
1104
1105 if vState.Height+vState.Margin.Top+vState.Margin.Bottom+(vState.Border.Width*2) < maxH {
1106 offset += (maxH - (vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)))
1107 }
1108 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1109 vState.Y = offset
1110 (*state)[n.Children[i].Properties.Id] = vState
1111
1112 }
1113 } else if align == "stretch" {
1114 for i := row[0]; i < row[1]; i++ {
1115 vState := s[n.Children[i].Properties.Id]
1116
1117 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top
1118
1119 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1120 offset += ((os) * float32(c))
1121 }
1122
1123 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1124 vState.Y = offset
1125 vState.Height = maxH - (vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))
1126 (*state)[n.Children[i].Properties.Id] = vState
1127
1128 }
1129 }
1130 }
1131}
1132
1133func justifyCols(cols [][]int, n *element.Node, state *map[string]element.State, justify string, reversed bool) {
1134 s := *state
1135 self := s[n.Properties.Id]
1136
1137 selfHeight := (self.Height) - (self.Padding.Top + self.Padding.Bottom)
1138 for _, col := range cols {
1139 yCollect := self.Y + self.Padding.Top
1140 var colHeight float32
1141 for i := col[0]; i <= col[1]; i++ {
1142 v := n.Children[i]
1143 vState := s[v.Properties.Id]
1144 colHeight += vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
1145 }
1146
1147 if justify == "center" {
1148 offset := ((selfHeight - colHeight) / 2)
1149 yCollect += offset
1150 for i := col[0]; i <= col[1]; i++ {
1151 v := n.Children[i]
1152 vState := s[v.Properties.Id]
1153 yStore := vState.Y
1154 vState.Y = yCollect + vState.Margin.Top
1155 yCollect += vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width
1156 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
1157 (*state)[v.Properties.Id] = vState
1158 }
1159 }
1160
1161 if justify == "end" || justify == "flex-end" {
1162 offset := (selfHeight - colHeight)
1163 yCollect += offset
1164 for i := col[0]; i <= col[1]; i++ {
1165 v := n.Children[i]
1166 vState := s[v.Properties.Id]
1167 yStore := vState.Y
1168 vState.Y = yCollect + vState.Border.Width + vState.Margin.Top
1169 yCollect += vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width
1170 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
1171 (*state)[v.Properties.Id] = vState
1172 }
1173 }
1174
1175 if justify == "space-evenly" {
1176 offset := (selfHeight - colHeight) / (float32(col[1]-col[0]) + 2)
1177 for i := col[0]; i <= col[1]; i++ {
1178 v := n.Children[i]
1179 vState := s[v.Properties.Id]
1180 yStore := vState.Y
1181 vState.Y = yCollect + vState.Border.Width + vState.Margin.Top + offset
1182 yCollect += vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width + offset
1183 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
1184 (*state)[v.Properties.Id] = vState
1185 }
1186 }
1187
1188 if justify == "space-between" {
1189 offset := (selfHeight - colHeight) / (float32(col[1] - col[0]))
1190 for i := col[0]; i <= col[1]; i++ {
1191 v := n.Children[i]
1192 vState := s[v.Properties.Id]
1193 yStore := vState.Y
1194 vState.Y = yCollect + vState.Border.Width + vState.Margin.Top
1195 if col[1]-col[0] != 0 {
1196 vState.Y += offset * float32(i-col[0])
1197 } else if reversed {
1198 vState.Y += selfHeight - (vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width)
1199 }
1200 yCollect += vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width
1201 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
1202 (*state)[v.Properties.Id] = vState
1203 }
1204 }
1205 if justify == "space-around" {
1206 offset := (selfHeight - colHeight) / (float32(col[1]-col[0]) + 1)
1207 for i := col[0]; i <= col[1]; i++ {
1208 v := n.Children[i]
1209 vState := s[v.Properties.Id]
1210 yStore := vState.Y
1211 vState.Y = yCollect + vState.Border.Width + vState.Margin.Top
1212 if col[1]-col[0] == 0 {
1213 vState.Y += offset / 2
1214 } else {
1215 vState.Y += (offset * float32(i-col[0])) + (offset / 2)
1216 }
1217 yCollect += vState.Height + vState.Margin.Bottom + vState.Border.Width + vState.Margin.Top + vState.Border.Width
1218 propagateOffsets(&n.Children[i], vState.X, yStore, vState.X, vState.Y, state)
1219 (*state)[v.Properties.Id] = vState
1220 }
1221 }
1222 }
1223}
1224
1225func alignCols(cols [][]int, n *element.Node, state *map[string]element.State, align, content string, minWidths [][]float32) {
1226 s := *state
1227 self := s[n.Properties.Id]
1228
1229 if (align == "stretch" && content == "stretch") || (align == "stretch" && content == "normal") || (align == "normal" && content == "stretch") {
1230 return
1231 }
1232
1233 selfWidth := (self.Width - self.Padding.Left) - self.Padding.Right
1234
1235 var rowWidth float32
1236 colWidths := []float32{}
1237 for _, col := range cols {
1238 var maxWidth float32
1239 for i := col[0]; i <= col[1]; i++ {
1240 v := n.Children[i]
1241 vState := s[v.Properties.Id]
1242 vState.Width = setWidth(&v, state, minWidths[i][0])
1243 maxWidth = utils.Max(maxWidth, vState.Width+vState.Margin.Left+vState.Margin.Right+(vState.Border.Width*2))
1244 (*state)[v.Properties.Id] = vState
1245 }
1246 colWidths = append(colWidths, maxWidth)
1247 rowWidth += maxWidth
1248 }
1249 var xOffset float32
1250 for c, col := range cols {
1251
1252 if align != "normal" {
1253 // var offset float32
1254 for i := col[0]; i <= col[1]; i++ {
1255 v := n.Children[i]
1256 vState := s[v.Properties.Id]
1257 xStore := vState.X
1258 vState.X = self.X + self.Padding.Left + self.Border.Width + xOffset + vState.Margin.Left + vState.Border.Width
1259 if align == "center" {
1260 vState.X += ((colWidths[c] - (vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2))) / 2)
1261 } else if align == "end" || align == "flex-end" || align == "self-end" {
1262 vState.X += (colWidths[c] - (vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)))
1263 } else if align == "stretch" {
1264 vState.Width = (colWidths[c] - (vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)))
1265 }
1266 propagateOffsets(&n.Children[i], xStore, vState.Y, vState.X, vState.Y, state)
1267 (*state)[v.Properties.Id] = vState
1268 }
1269 xOffset += colWidths[c]
1270 } else {
1271 // if align is set to normal then make the elements the size of the column
1272 // the size of the column is
1273 for i := col[0]; i <= col[1]; i++ {
1274 v := n.Children[i]
1275 vState := s[v.Properties.Id]
1276 vState.Width = setWidth(&v, state, colWidths[c]-(vState.Margin.Left+vState.Margin.Right+(vState.Border.Width*2)))
1277 (*state)[v.Properties.Id] = vState
1278 }
1279 }
1280 }
1281
1282 var offset float32
1283 if selfWidth < rowWidth {
1284 selfWidth = rowWidth
1285 }
1286 if content == "center" {
1287 offset = ((selfWidth - rowWidth) / 2)
1288 }
1289 if content == "end" || content == "flex-end" {
1290 offset = (selfWidth - rowWidth)
1291 }
1292 if content == "space-evenly" {
1293 offset = (selfWidth - rowWidth) / (float32(len(cols) + 1))
1294 }
1295 if content == "space-between" {
1296 offset = (selfWidth - rowWidth) / (float32(len(cols) - 1))
1297 }
1298 if content == "space-around" {
1299 offset = (selfWidth - rowWidth) / (float32(len(cols)))
1300 }
1301 if content == "normal" || content == "stretch" {
1302 if align == "start" || align == "end" || content == "flex-end" {
1303 offset = (selfWidth - rowWidth) / (float32(len(cols)))
1304 }
1305 if align == "center" {
1306 offset = ((selfWidth - rowWidth) / (float32(len(cols))))
1307 }
1308 }
1309 for c, col := range cols {
1310
1311 for i := col[0]; i <= col[1]; i++ {
1312 v := n.Children[i]
1313 vState := s[v.Properties.Id]
1314 xStore := vState.X
1315 if content == "center" || content == "end" || content == "flex-end" {
1316 vState.X += offset
1317 } else if content == "space-evenly" {
1318 vState.X += offset * float32(c+1)
1319 } else if content == "space-between" {
1320 vState.X += offset * float32(c)
1321 } else if content == "space-around" {
1322 if c == 0 {
1323 vState.X += offset / 2
1324 } else {
1325 vState.X += (offset * float32(c)) + (offset / 2)
1326 }
1327 } else if content == "normal" || content == "stretch" {
1328 if align == "start" {
1329 vState.X += offset * float32(c)
1330 }
1331 if align == "end" || content == "flex-end" {
1332 vState.X += offset * float32(c+1)
1333 }
1334 if align == "center" {
1335 if c == 0 {
1336 vState.X += offset / 2
1337 } else {
1338 vState.X += (offset * float32(c)) + (offset / 2)
1339 }
1340 }
1341 }
1342 propagateOffsets(&n.Children[i], xStore, vState.Y, vState.X, vState.Y, state)
1343 (*state)[v.Properties.Id] = vState
1344 }
1345 }
1346}