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/element"
6 "gui/utils"
7 "slices"
8 "strings"
9)
10
11func Init() cstyle.Plugin {
12 return cstyle.Plugin{
13 Selector: func(n *element.Node) bool {
14 styles := map[string]string{
15 "display": "flex",
16 "justify-content": "*",
17 "align-content": "*",
18 "align-items": "*",
19 "flex-wrap": "*",
20 "flex-direction": "*",
21 }
22 matches := true
23 for name, value := range styles {
24 if n.Style[name] != value && !(value == "*") && n.Style[name] != "" {
25 matches = false
26 }
27 }
28 return matches
29 },
30 Level: 2,
31 Handler: func(n *element.Node, state *map[string]element.State) {
32 // !ISSUE: align-items is not impleamented
33 s := *state
34 self := s[n.Properties.Id]
35 // Brief: justify does not align the bottom row correctly
36 // y axis also needs to be done
37 verbs := strings.Split(n.Style["flex-direction"], "-")
38
39 orderedNode := order(*n, state, n.Children, verbs[0], len(verbs) > 1, n.Style["flex-wrap"] == "wrap")
40
41 // var i int
42
43 colWidth := self.Width / float32(len(orderedNode))
44
45 var xOffset, yOffset float32
46 // if n.Style["justify-content"] == "space-evenly" {
47 // b, _ := utils.ConvertToPixels(n.Children[i].Properties.Border.Width, n.Children[i].Properties.EM, n.Properties.Computed["width"])
48 // cwV := utils.Max((colWidth-(n.Children[i].Properties.Computed["width"]+(b*2)))/2, 0)
49 // xOffset = cwV
50 // }
51
52 posTrack := map[int]int{}
53 p := 0
54
55 for a, column := range orderedNode {
56 var maxColumnHeight float32
57 for _, item := range column {
58 itemState := s[item.Properties.Id]
59 maxColumnHeight = utils.Max(itemState.Height, maxColumnHeight)
60 }
61
62 yOffset = s[n.Children[0].Properties.Id].Y
63 for _, item := range column {
64 var i int
65 for c, v := range n.Children {
66 if v.Properties.Id == item.Properties.Id {
67 i = c
68 }
69 }
70 posTrack[p] = i
71 p++
72 itemState := s[item.Properties.Id]
73 cState := s[n.Children[i].Properties.Id]
74 // n.Children[i] = item
75 if n.Style["justify-content"] == "space-between" {
76 cwV := utils.Max((colWidth - (itemState.Width)), 0)
77 // fmt.Println(colWidth, (itemState.Width), cwV, xOffset)
78 if a == 0 {
79 cState.X += 0
80 } else if a == len(orderedNode)-1 {
81 cState.X += cwV
82 } else {
83 cState.X += cwV / 2
84 }
85 } else if n.Style["justify-content"] == "flex-end" || n.Style["justify-content"] == "center" {
86 dif := self.Width - (xOffset)
87 if n.Style["justify-content"] == "center" {
88 dif = dif / 2
89 }
90 cState.X += dif
91 } else if n.Style["justify-content"] == "flex-start" || n.Style["justify-content"] == "" {
92 cState.X += xOffset
93 } else {
94 cwV := utils.Max((colWidth-(itemState.Width))/2, 0)
95 var offset float32
96 if n.Style["justify-content"] == "space-evenly" {
97 offset = ((cwV * 2) / float32(len(orderedNode))) * float32(a)
98 }
99 cState.X += xOffset + (cwV - offset)
100 }
101 cState.Y = yOffset
102 yOffset += maxColumnHeight
103 (*state)[n.Children[i].Properties.Id] = cState
104 i++
105 }
106 xOffset += colWidth
107 }
108
109 content := n.Style["align-content"]
110
111 if n.Style["flex-direction"] == "column" {
112 content = n.Style["justify-content"]
113 }
114
115 if content != "" && content != "flex-start" {
116 var min, max, rows, col, currY float32
117 min = 1000000000000
118 for _, v := range n.Children {
119 vState := s[v.Properties.Id]
120 min = utils.Min(min, vState.Y)
121 max = utils.Max(max, vState.Height+vState.Y)
122 if vState.Y > currY {
123 rows++
124 currY = vState.Y
125 }
126 }
127
128 height := max - min
129 rowHeight := ((self.Height - height) / rows)
130 for e := range n.Children {
131 i := posTrack[e]
132 cState := s[n.Children[i].Properties.Id]
133 row := float32(int(e % int(rows)))
134 if row == 0 {
135 col++
136 }
137 if len(orderedNode[int(col)-1]) <= int(row) {
138 row = 0
139 }
140
141 if content == "center" {
142 cState.Y += (self.Height - height) / 2
143 } else if content == "flex-end" {
144 cState.Y += (self.Height - height)
145 } else if content == "space-around" {
146 cState.Y += (rowHeight * row) + (rowHeight / 2)
147 } else if content == "space-evenly" {
148 cState.Y += (rowHeight * row) + (rowHeight / 2)
149 } else if content == "space-between" {
150 cState.Y += (((self.Height - height) / (rows - 1)) * row)
151 } else if content == "stretch" {
152 cState.Y += (rowHeight * row)
153 if n.Children[i].Style["height"] == "" {
154 cState.Height = self.Height / rows
155 }
156 }
157 (*state)[n.Children[i].Properties.Id] = cState
158
159 }
160
161 }
162 (*state)[n.Properties.Id] = self
163 },
164 }
165}
166
167func order(p element.Node, state *map[string]element.State, elements []element.Node, direction string, reversed, wrap bool) [][]element.Node {
168 s := *state
169 self := s[p.Properties.Id]
170 var dir, marginStart, marginEnd string
171 if direction == "column" {
172 dir = "Height"
173 marginStart = "Top"
174 marginEnd = "Bottom"
175 } else {
176 dir = "Width"
177 marginStart = "Left"
178 marginEnd = "Right"
179 }
180 max, _ := utils.GetStructField(&self, dir)
181
182 nodes := [][]element.Node{}
183
184 if wrap {
185 counter := 0
186 if direction == "column" {
187 collector := []element.Node{}
188 for _, v := range elements {
189 vState := s[v.Properties.Id]
190 elMax := vState.Height
191 elMS, _ := utils.GetStructField(&vState.Margin, marginStart)
192 elME, _ := utils.GetStructField(&vState.Margin, marginEnd)
193 tMax := elMax + elMS.(float32) + elME.(float32)
194 if counter+int(tMax) < int(max.(float32)) {
195 collector = append(collector, v)
196 } else {
197 if reversed {
198 slices.Reverse(collector)
199 }
200 nodes = append(nodes, collector)
201 collector = []element.Node{}
202 collector = append(collector, v)
203 counter = 0
204 }
205 counter += int(tMax)
206 }
207 if len(collector) > 0 {
208 nodes = append(nodes, collector)
209 }
210 } else {
211 var mod int
212 for _, v := range elements {
213 vState := s[v.Properties.Id]
214 elMax := vState.Width
215 elMS, _ := utils.GetStructField(&vState.Margin, marginStart)
216 elME, _ := utils.GetStructField(&vState.Margin, marginEnd)
217 tMax := elMax + elMS.(float32) + elME.(float32)
218 if counter+int(tMax) < int(max.(float32)) {
219 if len(nodes)-1 < mod {
220 nodes = append(nodes, []element.Node{v})
221 } else {
222 nodes[mod] = append(nodes[mod], v)
223 }
224 } else {
225 mod = 0
226 counter = 0
227 if len(nodes)-1 < mod {
228 nodes = append(nodes, []element.Node{v})
229 } else {
230 nodes[mod] = append(nodes[mod], v)
231 }
232 }
233 counter += int(tMax)
234 mod++
235 }
236 if reversed {
237 slices.Reverse(nodes)
238 }
239 }
240 } else {
241 var tMax float32
242 for _, v := range elements {
243 vState := s[v.Properties.Id]
244 elMax, _ := utils.GetStructField(&vState, dir)
245 elMS, _ := utils.GetStructField(&vState.Margin, marginStart)
246 elME, _ := utils.GetStructField(&vState.Margin, marginEnd)
247 tMax += elMax.(float32) + elMS.(float32) + elME.(float32)
248 }
249
250 pMax, _ := utils.GetStructField(&self, dir)
251
252 // Resize node to fit
253 var newSize float32
254 if tMax > pMax.(float32) {
255 newSize = pMax.(float32) / float32(len(elements))
256 }
257 if dir == "Width" {
258 for _, v := range elements {
259 vState := s[v.Properties.Id]
260 if newSize != 0 {
261 vState.Width = newSize
262 }
263 nodes = append(nodes, []element.Node{v})
264 }
265 if reversed {
266 slices.Reverse(nodes)
267 }
268 } else {
269 nodes = append(nodes, []element.Node{})
270 for _, v := range elements {
271 vState := s[v.Properties.Id]
272 if newSize != 0 {
273 vState.Height = newSize
274 }
275 nodes[0] = append(nodes[0], v)
276 }
277 if reversed {
278 slices.Reverse(nodes[0])
279 }
280 }
281
282 }
283 (*state)[p.Properties.Id] = self
284
285 return nodes
286}