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, v := range n.Children {
108 vState := s[v.Properties.Id]
109
110 w := innerSizes[i][0]
111 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
112 widths = append(widths, w)
113 }
114 } else {
115 // Modifiy the widths so they aren't under the mins
116 for i, v := range n.Children {
117 vState := s[v.Properties.Id]
118
119 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
120 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
121
122 if w < minWidths[i] {
123 selfWidth -= minWidths[i] + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
124 textTotal -= textCounts[i]
125 textCounts[i] = 0
126 }
127
128 }
129 for i, v := range n.Children {
130 vState := s[v.Properties.Id]
131
132 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
133 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
134 // (w!=w) is of NaN
135 if w < minWidths[i] || (w != w) {
136 w = minWidths[i]
137 }
138 widths = append(widths, w)
139 }
140 }
141 // Apply the new widths
142 fState := s[n.Children[0].Properties.Id]
143 for i, v := range n.Children {
144 vState := s[v.Properties.Id]
145
146 vState.Width = widths[i]
147 xStore := vState.X
148 if i > 0 {
149 sState := s[n.Children[i-1].Properties.Id]
150 vState.X = sState.X + sState.Width + sState.Margin.Right + vState.Margin.Left + sState.Border.Width + vState.Border.Width
151 propagateOffsets(&v, xStore, vState.Y, vState.X, fState.Y+vState.Margin.Top, state)
152 }
153
154 vState.Y = fState.Y + vState.Margin.Top
155
156 (*state)[v.Properties.Id] = vState
157 deInline(&v, state)
158 applyInline(&v, state)
159 applyBlock(&v, state)
160 _, h := getInnerSize(&v, state)
161 h = utils.Max(h, vState.Height)
162 maxH = utils.Max(maxH, h)
163 }
164 // When not wrapping everything will be on the same row
165 rows = append(rows, []int{0, len(n.Children), int(maxH)})
166 } else {
167 // Flex Wrapped
168 sum := innerSizes[0][0]
169 for i := 0; i < len(n.Children); i++ {
170 v := n.Children[i]
171 vState := s[v.Properties.Id]
172
173 // if the next plus current will break then
174 w := innerSizes[i][0]
175 if i > 0 {
176 sib := s[n.Children[i-1].Properties.Id]
177 if maxWidths[i] > selfWidth {
178 w = selfWidth - vState.Margin.Left - vState.Margin.Right - (vState.Border.Width * 2)
179 }
180 if w+sum > selfWidth {
181 sum = w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
182 } else {
183 propagateOffsets(&v, vState.X, vState.Y, vState.X, sib.Y, state)
184 vState.Y = sib.Y
185 (*state)[v.Properties.Id] = vState
186 sum += w + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
187 }
188 }
189
190 widths = append(widths, w)
191 }
192
193 // Move the elements into the correct position
194 start := 0
195 var prevOffset float32
196 for i := 0; i < len(n.Children); i++ {
197 v := n.Children[i]
198 vState := s[v.Properties.Id]
199
200 vState.Width = widths[i]
201 xStore := vState.X
202 yStore := vState.Y
203
204 if i > 0 {
205 sib := s[n.Children[i-1].Properties.Id]
206 if vState.Y+prevOffset == sib.Y {
207 yStore += prevOffset
208
209 if vState.Height < sib.Height {
210 vState.Height = minHeight(v, state, sib.Height)
211 }
212 // Shift right if on a row with sibling
213 xStore = sib.X + sib.Width + sib.Margin.Right + sib.Border.Width + vState.Margin.Left + vState.Border.Width
214 } else {
215 // Shift under sibling
216 yStore = sib.Y + sib.Height + sib.Margin.Top + sib.Margin.Bottom + sib.Border.Width*2
217 prevOffset = yStore - vState.Y
218 rows = append(rows, []int{start, i, int(maxH)})
219 start = i
220 maxH = 0
221 }
222 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
223 }
224 vState.X = xStore
225 vState.Y = yStore
226
227 (*state)[v.Properties.Id] = vState
228 deInline(&v, state)
229 applyInline(&v, state)
230 applyBlock(&v, state)
231 _, h := getInnerSize(&v, state)
232 h = utils.Max(h, vState.Height)
233 maxH = utils.Max(maxH, h)
234 vState.Height = minHeight(v, state, h)
235 (*state)[v.Properties.Id] = vState
236 }
237 if start < len(n.Children) {
238 rows = append(rows, []int{start, len(n.Children), int(maxH)})
239 }
240 }
241
242 for _, v := range rows {
243 for i := v[0]; i < v[1]; i++ {
244 vState := s[n.Children[i].Properties.Id]
245 height := float32(v[2])
246 if (n.Style["height"] != "" || n.Style["min-height"] != "") && !flexWrapped {
247 height = self.Height - self.Padding.Top - self.Padding.Bottom - vState.Margin.Top - vState.Margin.Bottom - (vState.Border.Width * 2)
248 }
249 vState.Height = minHeight(n.Children[i], state, height)
250 (*state)[n.Children[i].Properties.Id] = vState
251 }
252 }
253 // Reverse elements
254 if flexReversed {
255 rowReverse(rows, n, state)
256 }
257
258 if justifyContent != "" && justifyContent != "normal" {
259 justifyRow(rows, n, state, justifyContent, flexReversed)
260 }
261
262 alignRow(rows, n, state, alignItems, alignContent)
263
264 }
265
266 // Column doesn't really need a lot done bc it is basically block styling rn
267 if flexDirection == "column" {
268 if !flexWrapped {
269 // if the container has a size restriction
270 if n.Style["height"] != "" {
271 var totalHeight float32
272 var fixedHeightElements int
273 for _, v := range n.Children {
274 vState := s[v.Properties.Id]
275 if v.Style["min-height"] != "" {
276 selfHeight -= vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
277 fixedHeightElements++
278 } else {
279 // accoutn for element min height
280 totalHeight += vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
281 }
282 }
283
284 heightDelta := selfHeight - totalHeight
285 if heightDelta < 0 {
286 heightDelta = -heightDelta
287 }
288 heightAdj := heightDelta / float32(len(n.Children)-fixedHeightElements)
289 if heightAdj < 0 {
290 heightAdj = -heightAdj
291 }
292 // We are calculating the amount a element needs to shrink because of its siblings
293 for i, v := range n.Children {
294 vState := s[v.Properties.Id]
295 if v.Style["min-height"] == "" {
296 vState.Height -= heightAdj
297 yStore := vState.Y
298 if vState.Height < minHeights[i] {
299 vState.Height = minHeights[i]
300 }
301 if i > 0 {
302 sib := s[n.Children[i-1].Properties.Id]
303
304 vState.Y = sib.Y + sib.Height + sib.Margin.Bottom + sib.Border.Width + vState.Margin.Top + vState.Border.Width
305 }
306 propagateOffsets(&v, vState.X, yStore, vState.X, vState.Y, state)
307
308 (*state)[v.Properties.Id] = vState
309 }
310 }
311
312 }
313 for i, v := range n.Children {
314 vState := s[v.Properties.Id]
315 rows = append(rows, []int{i, i + 1, int(vState.Height)})
316 }
317
318 } else {
319 var colHeight float32
320 var colIndex int
321 cols := [][][]float32{}
322
323 // Map elements to columns
324 for i, v := range n.Children {
325 vState := s[v.Properties.Id]
326
327 height := vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
328 if colHeight+height > selfHeight {
329 colHeight = height
330 colIndex++
331 width := vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
332 if colIndex >= len(cols) {
333 cols = append(cols, [][]float32{})
334 }
335 cols[colIndex] = append(cols[colIndex], []float32{float32(i), height, width})
336 } else {
337 colHeight += height
338 width := vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
339 if colIndex >= len(cols) {
340 cols = append(cols, [][]float32{})
341 }
342 cols[colIndex] = append(cols[colIndex], []float32{float32(i), height, width})
343 }
344 }
345
346 // Find the max total width of all columns
347 var totalMaxWidth float32
348 maxWidths := []float32{}
349 for _, col := range cols {
350 var maxWidth float32
351 for _, element := range col {
352 maxWidth = utils.Max(element[2], maxWidth)
353 }
354 totalMaxWidth += maxWidth
355 maxWidths = append(maxWidths, maxWidth)
356 }
357 offset := (selfWidth - totalMaxWidth) / float32(len(cols))
358 // Move the elements into the correct position
359 var xOffset float32
360 for i, col := range cols {
361 // Move the elements into the correct position
362 yOffset := self.Y + self.Border.Width + self.Padding.Top
363 for _, element := range col {
364 vState := s[n.Children[int(element[0])].Properties.Id]
365 xStore := vState.X
366 yStore := vState.Y
367 vState.X = self.X + self.Padding.Left + self.Border.Width + xOffset + vState.Margin.Left + vState.Border.Width
368 vState.Y = yOffset + vState.Margin.Top + vState.Border.Width
369 propagateOffsets(&n.Children[int(element[0])], xStore, yStore, vState.X, vState.Y, state)
370
371 yOffset += vState.Margin.Top + vState.Border.Width + vState.Height + vState.Margin.Bottom + vState.Border.Width
372 (*state)[n.Children[int(element[0])].Properties.Id] = vState
373 }
374 xOffset += maxWidths[i] + offset
375 }
376
377 }
378 if flexReversed {
379 colReverse(n, state)
380 }
381 }
382 if n.Style["height"] == "" || n.Style["min-height"] == "" {
383 _, h := getInnerSize(n, state)
384 // fmt.Println(h)
385 self.Height = h
386 }
387 (*state)[n.Properties.Id] = self
388 },
389 }
390}
391
392func applyBlock(n *element.Node, state *map[string]element.State) {
393 if len(n.Children) > 0 {
394 accum := float32(0)
395 inlineOffset := float32(0)
396 s := *state
397 lastHeight := float32(0)
398 baseY := s[n.Children[0].Properties.Id].Y
399 for i := 0; i < len(n.Children); i++ {
400 v := &n.Children[i]
401 vState := s[v.Properties.Id]
402
403 if v.Style["display"] != "block" {
404 vState.Y += inlineOffset
405 accum = (vState.Y - baseY)
406 lastHeight = vState.Height
407 } else if v.Style["position"] != "absolute" {
408 vState.Y += accum
409 inlineOffset += (vState.Height + (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom) + lastHeight
410 }
411 (*state)[v.Properties.Id] = vState
412 }
413 }
414}
415
416func deInline(n *element.Node, state *map[string]element.State) {
417 s := *state
418 // self := s[n.Properties.Id]
419 baseX := float32(-1)
420 baseY := float32(-1)
421 for _, v := range n.Children {
422 vState := s[v.Properties.Id]
423
424 if v.Style["display"] == "inline" {
425 if baseX < 0 && baseY < 0 {
426 baseX = vState.X
427 baseY = vState.Y
428 } else {
429 vState.X = baseX
430 vState.Y = baseY
431 (*state)[v.Properties.Id] = vState
432
433 }
434 } else {
435 baseX = float32(-1)
436 baseY = float32(-1)
437 }
438
439 if len(v.Children) > 0 {
440 deInline(&v, state)
441 }
442 }
443
444}
445
446func applyInline(n *element.Node, state *map[string]element.State) {
447 pl := inline.Init()
448 for i := 0; i < len(n.Children); i++ {
449 v := &n.Children[i]
450
451 if len(v.Children) > 0 {
452 applyInline(v, state)
453 }
454
455 if pl.Selector(v) {
456 pl.Handler(v, state)
457 }
458 }
459}
460
461func propagateOffsets(n *element.Node, prevx, prevy, newx, newy float32, state *map[string]element.State) {
462 s := *state
463 for _, v := range n.Children {
464 vState := s[v.Properties.Id]
465 xStore := (vState.X - prevx) + newx
466 yStore := (vState.Y - prevy) + newy
467
468 if len(v.Children) > 0 {
469 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
470 }
471 vState.X = xStore
472 vState.Y = yStore
473 (*state)[v.Properties.Id] = vState
474 }
475
476}
477
478func countText(n element.Node) int {
479 count := 0
480 groups := []int{}
481 for _, v := range n.Children {
482 if v.TagName == "notaspan" {
483 count += 1
484 }
485 if v.Style["display"] == "block" {
486 groups = append(groups, count)
487 count = 0
488 }
489 if len(v.Children) > 0 {
490 count += countText(v)
491 }
492 }
493 groups = append(groups, count)
494
495 sort.Slice(groups, func(i, j int) bool {
496 return groups[i] > groups[j]
497 })
498 return groups[0]
499}
500
501func minHeight(n element.Node, state *map[string]element.State, prev float32) float32 {
502 s := *state
503 self := s[n.Properties.Id]
504 if n.Style["min-height"] != "" {
505 mw := utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Width)
506 return utils.Max(prev, mw)
507 } else {
508 return prev
509 }
510
511}
512
513func getMinHeight(n *element.Node, state *map[string]element.State) float32 {
514 s := *state
515 self := s[n.Properties.Id]
516 selfHeight := float32(0)
517
518 if len(n.Children) > 0 {
519 for _, v := range n.Children {
520 selfHeight = utils.Max(selfHeight, getNodeHeight(&v, state))
521 }
522 } else {
523 selfHeight = self.Height
524 }
525 if n.Style["min-height"] != "" {
526 mh := utils.ConvertToPixels(n.Style["min-height"], self.EM, s[n.Parent.Properties.Id].Width)
527 selfHeight = utils.Max(mh, selfHeight)
528 }
529
530 selfHeight += self.Padding.Top + self.Padding.Bottom
531 return selfHeight
532}
533
534func getMinWidth(n *element.Node, state *map[string]element.State) float32 {
535 s := *state
536 self := s[n.Properties.Id]
537 selfWidth := float32(0)
538
539 if len(n.Children) > 0 {
540 for _, v := range n.Children {
541 selfWidth = utils.Max(selfWidth, getNodeWidth(&v, state))
542 }
543 } else {
544 selfWidth = self.Width
545 }
546 if n.Style["min-width"] != "" {
547 mw := utils.ConvertToPixels(n.Style["min-width"], self.EM, s[n.Parent.Properties.Id].Width)
548 selfWidth = utils.Max(mw, selfWidth)
549 }
550
551 selfWidth += self.Padding.Left + self.Padding.Right
552 return selfWidth
553}
554func getMaxWidth(n *element.Node, state *map[string]element.State) float32 {
555 s := *state
556 self := s[n.Properties.Id]
557 selfWidth := float32(0)
558
559 if len(n.Children) > 0 {
560 var maxRowWidth, rowWidth float32
561
562 for _, v := range n.Children {
563 rowWidth += getNodeWidth(&v, state)
564 if v.Style["display"] != "inline" {
565 maxRowWidth = utils.Max(rowWidth, maxRowWidth)
566 rowWidth = 0
567 }
568 }
569 selfWidth = utils.Max(rowWidth, maxRowWidth)
570 } else {
571 selfWidth = self.Width
572 }
573
574 selfWidth += self.Padding.Left + self.Padding.Right
575 return selfWidth
576}
577
578func getNodeWidth(n *element.Node, state *map[string]element.State) float32 {
579 s := *state
580 self := s[n.Properties.Id]
581 w := float32(0)
582 w += self.Padding.Left
583 w += self.Padding.Right
584
585 w += self.Margin.Left
586 w += self.Margin.Right
587
588 w += self.Width
589
590 w += self.Border.Width * 2
591
592 for _, v := range n.Children {
593 w = utils.Max(w, getNodeWidth(&v, state))
594 }
595
596 return w
597}
598func getMaxHeight(n *element.Node, state *map[string]element.State) float32 {
599 s := *state
600 self := s[n.Properties.Id]
601 selfHeight := float32(0)
602
603 if len(n.Children) > 0 {
604 var maxRowHeight, rowHeight float32
605
606 for _, v := range n.Children {
607 rowHeight += getNodeHeight(&v, state)
608 if v.Style["display"] != "inline" {
609 maxRowHeight = utils.Max(rowHeight, maxRowHeight)
610 rowHeight = 0
611 }
612 }
613 selfHeight = utils.Max(rowHeight, maxRowHeight)
614 } else {
615 selfHeight = self.Height
616 }
617
618 selfHeight += self.Padding.Top + self.Padding.Bottom
619 return selfHeight
620}
621
622func getNodeHeight(n *element.Node, state *map[string]element.State) float32 {
623 s := *state
624 self := s[n.Properties.Id]
625 h := float32(0)
626 h += self.Padding.Top
627 h += self.Padding.Bottom
628
629 h += self.Margin.Top
630 h += self.Margin.Bottom
631
632 h += self.Height
633
634 h += self.Border.Width * 2
635
636 for _, v := range n.Children {
637 h = utils.Max(h, getNodeHeight(&v, state))
638 }
639
640 return h
641}
642
643func getInnerSize(n *element.Node, state *map[string]element.State) (float32, float32) {
644 s := *state
645 self := s[n.Properties.Id]
646
647 minx := float32(10e10)
648 maxw := float32(0)
649 miny := float32(10e10)
650 maxh := float32(0)
651 for _, v := range n.Children {
652 vState := s[v.Properties.Id]
653 minx = utils.Min(vState.X, minx)
654 miny = utils.Min(vState.Y-vState.Margin.Top, miny)
655 // Don't add the top or left because the x&y values already take that into account
656 hOffset := (vState.Border.Width * 2) + vState.Margin.Bottom
657 wOffset := (vState.Border.Width * 2) + vState.Margin.Right
658 maxw = utils.Max(vState.X+vState.Width+wOffset, maxw)
659 maxh = utils.Max(vState.Y+vState.Height+hOffset, maxh)
660 }
661 w := maxw - minx
662 h := maxh - miny
663
664 w += self.Padding.Left + self.Padding.Right
665 h += self.Padding.Top + self.Padding.Bottom
666 if n.Style["width"] != "" {
667 w = self.Width
668 }
669 if n.Style["height"] != "" {
670 h = self.Height
671 }
672
673 return w, h
674}
675
676func add2d(arr [][]float32, index int) float32 {
677 var sum float32
678 if len(arr) == 0 {
679 return sum
680 }
681
682 for i := 0; i < len(arr); i++ {
683 if len(arr[i]) <= index {
684 return sum
685 }
686 sum += arr[i][index]
687 }
688
689 return sum
690}
691
692func colReverse(n *element.Node, state *map[string]element.State) {
693 s := *state
694 tempNodes := []element.Node{}
695 tempStates := []element.State{}
696 for i := len(n.Children) - 1; i >= 0; i-- {
697 tempNodes = append(tempNodes, n.Children[i])
698 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
699 }
700
701 for i := 0; i < len(tempStates); i++ {
702 vState := s[n.Children[i].Properties.Id]
703 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, tempStates[i].Y, state)
704 vState.Y = tempStates[i].Y
705 (*state)[n.Children[i].Properties.Id] = vState
706 }
707
708 n.Children = tempNodes
709}
710
711func rowReverse(rows [][]int, n *element.Node, state *map[string]element.State) {
712 s := *state
713 for _, row := range rows {
714 tempNodes := []element.Node{}
715 tempStates := []element.State{}
716
717 for i := row[1] - 1; i >= row[0]; i-- {
718 tempNodes = append(tempNodes, n.Children[i])
719 tempStates = append(tempStates, s[n.Children[i].Properties.Id])
720 }
721
722 for i := 0; i < len(tempStates); i++ {
723 e := row[0] + i
724 vState := s[n.Children[e].Properties.Id]
725 propagateOffsets(&n.Children[e], vState.X, vState.Y, tempStates[i].X, tempStates[i].Y, state)
726 vState.X = tempStates[i].X
727 (*state)[n.Children[e].Properties.Id] = vState
728 }
729 for i := 0; i < len(tempStates); i++ {
730 e := row[0] + i
731 n.Children[e] = tempNodes[i]
732 }
733
734 for i := row[1] - 1; i >= row[0]; i-- {
735 vState := s[n.Children[i].Properties.Id]
736 var xChng float32
737 if i < row[1]-1 {
738 sib := s[n.Children[i+1].Properties.Id]
739 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
740 } else {
741 parent := s[n.Properties.Id]
742 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
743
744 }
745 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
746 vState.X = xChng
747 (*state)[n.Children[i].Properties.Id] = vState
748 }
749 }
750}
751
752func justifyRow(rows [][]int, n *element.Node, state *map[string]element.State, justify string, reversed bool) {
753 s := *state
754 for _, row := range rows {
755
756 if (justify == "flex-end" || justify == "end" || justify == "right") && !reversed {
757 for i := row[1] - 1; i >= row[0]; i-- {
758 vState := s[n.Children[i].Properties.Id]
759 var xChng float32
760 if i < row[1]-1 {
761 sib := s[n.Children[i+1].Properties.Id]
762 xChng = sib.X - (sib.Border.Width + sib.Margin.Left + vState.Margin.Right + vState.Border.Width + vState.Width)
763 } else {
764 parent := s[n.Properties.Id]
765 xChng = ((((parent.X + parent.Width) - parent.Padding.Right) - vState.Width) - vState.Margin.Right) - (vState.Border.Width)
766
767 }
768 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
769 vState.X = xChng
770 (*state)[n.Children[i].Properties.Id] = vState
771 }
772 } else if (justify == "flex-end" || justify == "start" || justify == "left" || justify == "normal") && reversed {
773 for i := row[0]; i < row[1]; i++ {
774 vState := s[n.Children[i].Properties.Id]
775 var xChng float32
776 if i > row[0] {
777 sib := s[n.Children[i-1].Properties.Id]
778 xChng = sib.X + sib.Width + (sib.Border.Width * 2) + sib.Margin.Right + vState.Margin.Left + vState.Border.Width
779 } else {
780 parent := s[n.Properties.Id]
781 xChng = parent.X + parent.Padding.Right + vState.Margin.Left + vState.Border.Width + parent.Border.Width
782
783 }
784 propagateOffsets(&n.Children[i], vState.X, vState.Y, xChng, vState.Y, state)
785 vState.X = xChng
786 (*state)[n.Children[i].Properties.Id] = vState
787 }
788 } else if justify == "center" {
789 // get width of row then center (by getting last x + w + mr + b)
790 f := s[n.Children[row[0]].Properties.Id]
791 l := s[n.Children[row[1]-1].Properties.Id]
792 parent := s[n.Properties.Id]
793 po := parent.X + parent.Border.Width
794 offset := (parent.Width - ((f.X - po) + (l.X - po) + l.Width + f.Border.Width + l.Border.Width)) / 2
795
796 for i := row[0]; i < row[1]; i++ {
797 vState := s[n.Children[i].Properties.Id]
798
799 if !reversed {
800 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
801 vState.X += offset
802 } else {
803 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
804 vState.X -= offset
805 }
806 (*state)[n.Children[i].Properties.Id] = vState
807 }
808
809 } else if justify == "space-between" {
810 // get width of row then center (by getting last x + w + mr + b)
811 f := s[n.Children[row[0]].Properties.Id]
812 l := s[n.Children[row[1]-1].Properties.Id]
813 parent := s[n.Properties.Id]
814 po := parent.Border.Width + parent.Width
815 po -= parent.Padding.Left + parent.Padding.Right
816
817 // make po repersent the total space between elements
818 for i := row[0]; i < row[1]; i++ {
819 vState := s[n.Children[i].Properties.Id]
820 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
821 }
822
823 po /= float32(((row[1]) - row[0]) - 1)
824
825 if (row[1]-1)-row[0] > 0 {
826 for i := row[0]; i < row[1]; i++ {
827 vState := s[n.Children[i].Properties.Id]
828 var offset float32
829 if i == row[0] {
830 offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
831 } else if i == row[1]-1 {
832 offset = (parent.X + parent.Width) - (l.Margin.Right + l.Border.Width + l.Width + parent.Padding.Right)
833 } else {
834 if !reversed {
835 offset = vState.X + (po * float32(i-row[0]))
836 } else {
837 offset = vState.X - (po * float32(((row[1]-1)-row[0])-(i-row[0])))
838 }
839
840 }
841
842 propagateOffsets(&n.Children[i], vState.X, vState.Y, offset, vState.Y, state)
843 vState.X = offset
844 (*state)[n.Children[i].Properties.Id] = vState
845 }
846 }
847 // else {
848
849 // this is/was causing issues, removed and it fixed its self
850
851 // if there is one element move left
852 // vState := s[n.Children[(row[1]-1)-row[0]].Properties.Id]
853 // var offset float32
854
855 // if !reversed {
856 // offset = parent.X + parent.Padding.Left + f.Margin.Left + f.Border.Width
857 // propagateOffsets(&n.Children[(row[1]-1)-row[0]], vState.X, vState.Y, offset, vState.Y, state)
858 // vState.X = offset
859
860 // (*state)[n.Children[(row[1]-1)-row[0]].Properties.Id] = vState
861 // }
862
863 // }
864
865 } else if justify == "space-evenly" {
866 // get width of row then center (by getting last x + w + mr + b)
867 parent := s[n.Properties.Id]
868 po := parent.Border.Width + parent.Width
869 po -= parent.Padding.Left + parent.Padding.Right
870
871 // make po repersent the total space between elements
872 for i := row[0]; i < row[1]; i++ {
873 vState := s[n.Children[i].Properties.Id]
874 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
875 }
876
877 po /= float32(((row[1]) - row[0]) + 1)
878
879 // get width of row then center (by getting last x + w + mr + b)
880
881 for i := row[0]; i < row[1]; i++ {
882 vState := s[n.Children[i].Properties.Id]
883
884 if !reversed {
885 offset := po * (float32(i-row[0]) + 1)
886 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
887 vState.X += offset
888 } else {
889 offset := po * float32(((row[1]-1)-row[0])-((i-row[0])-1))
890
891 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
892 vState.X -= offset
893 }
894 (*state)[n.Children[i].Properties.Id] = vState
895 }
896
897 } else if justify == "space-around" {
898 // get width of row then center (by getting last x + w + mr + b)
899 parent := s[n.Properties.Id]
900 po := parent.Border.Width + parent.Width
901 po -= parent.Padding.Left + parent.Padding.Right
902
903 // make po repersent the total space between elements
904 for i := row[0]; i < row[1]; i++ {
905 vState := s[n.Children[i].Properties.Id]
906 po -= vState.Width + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
907 }
908
909 po /= float32(((row[1]) - row[0]))
910
911 // get width of row then center (by getting last x + w + mr + b)
912
913 for i := row[0]; i < row[1]; i++ {
914 vState := s[n.Children[i].Properties.Id]
915
916 if !reversed {
917 m := (float32(i-row[0]) + 1)
918 if i-row[0] == 0 {
919 m = 0.5
920 } else {
921 m -= 0.5
922 }
923 offset := po * m
924 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X+offset, vState.Y, state)
925 vState.X += offset
926 } else {
927 m := float32(((row[1] - 1) - row[0]) - ((i - row[0]) - 1))
928 m -= 0.5
929 offset := po * m
930
931 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X-offset, vState.Y, state)
932 vState.X -= offset
933 }
934 (*state)[n.Children[i].Properties.Id] = vState
935 }
936
937 }
938
939 }
940}
941
942func alignRow(rows [][]int, n *element.Node, state *map[string]element.State, align, content string) {
943 // !ISSUE: Baseline isn't properly impleamented
944
945 s := *state
946 self := s[n.Properties.Id]
947
948 maxes := []float32{}
949 var maxesTotal float32
950 for _, row := range rows {
951 var maxH float32
952 for i := row[0]; i < row[1]; i++ {
953 vState := s[n.Children[i].Properties.Id]
954 _, h := getInnerSize(&n.Children[i], state)
955 h = minHeight(n.Children[i], state, h)
956 vState.Height = h
957 h += vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)
958 maxH = utils.Max(maxH, h)
959 (*state)[n.Children[i].Properties.Id] = vState
960 }
961 maxes = append(maxes, maxH)
962 maxesTotal += maxH
963 }
964
965 os := ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows))
966 if os < 0 || content != "normal" {
967 os = 0
968 }
969
970 var contentOffset float32
971
972 if content == "center" {
973 contentOffset = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / 2
974 } else if content == "end" || content == "flex-end" {
975 contentOffset = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal)
976 } else if content == "start" || content == "flex-start" || content == "baseline" {
977 // This is redundent but it helps keep track
978 contentOffset = 0
979 } else if content == "space-between" {
980 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows)-1)
981 } else if content == "space-around" {
982 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows))
983 contentOffset = os / 2
984 } else if content == "space-evenly" {
985 os = ((self.Height - (self.Padding.Top + self.Padding.Bottom + (self.Border.Width * 2))) - maxesTotal) / float32(len(rows)+1)
986 contentOffset = os
987 }
988
989 for c, row := range rows {
990 maxH := maxes[c]
991 var sum float32
992 for i := 0; i < c; i++ {
993 sum += maxes[i]
994 }
995 if align == "start" || align == "flex-start" || align == "self-start" || align == "normal" {
996 for i := row[0]; i < row[1]; i++ {
997 vState := s[n.Children[i].Properties.Id]
998
999 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1000
1001 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1002 offset += ((os) * float32(c))
1003 }
1004
1005 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1006 vState.Y = offset
1007 (*state)[n.Children[i].Properties.Id] = vState
1008 }
1009 } else if align == "center" {
1010 for i := row[0]; i < row[1]; i++ {
1011 vState := s[n.Children[i].Properties.Id]
1012
1013 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1014
1015 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1016 offset += (os * float32(c+1)) - (os / 2)
1017 }
1018
1019 if vState.Height+vState.Margin.Top+vState.Margin.Bottom+(vState.Border.Width*2) < maxH {
1020 offset += (maxH - (vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))) / 2
1021 }
1022 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1023 vState.Y = offset
1024 (*state)[n.Children[i].Properties.Id] = vState
1025 }
1026 } else if align == "end" || align == "flex-end" || align == "self-end" {
1027 for i := row[0]; i < row[1]; i++ {
1028 vState := s[n.Children[i].Properties.Id]
1029
1030 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top + contentOffset
1031
1032 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1033 offset += os * float32(c+1)
1034 }
1035
1036 if vState.Height+vState.Margin.Top+vState.Margin.Bottom+(vState.Border.Width*2) < maxH {
1037 offset += (maxH - (vState.Height + vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2)))
1038 }
1039 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1040 vState.Y = offset
1041 (*state)[n.Children[i].Properties.Id] = vState
1042
1043 }
1044 } else if align == "stretch" {
1045 for i := row[0]; i < row[1]; i++ {
1046 vState := s[n.Children[i].Properties.Id]
1047
1048 offset := sum + self.Y + self.Padding.Top + vState.Margin.Top
1049
1050 if n.Style["height"] != "" || n.Style["min-height"] != "" {
1051 offset += ((os) * float32(c))
1052 }
1053
1054 propagateOffsets(&n.Children[i], vState.X, vState.Y, vState.X, offset, state)
1055 vState.Y = offset
1056 vState.Height = maxH - (vState.Margin.Top + vState.Margin.Bottom + (vState.Border.Width * 2))
1057 (*state)[n.Children[i].Properties.Id] = vState
1058
1059 }
1060 }
1061 }
1062}