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 "fmt"
5 "gui/cstyle"
6 "gui/cstyle/plugins/inline"
7 "gui/element"
8 "gui/utils"
9 "sort"
10 "strings"
11)
12
13func Init() cstyle.Plugin {
14 return cstyle.Plugin{
15 Selector: func(n *element.Node) bool {
16 styles := map[string]string{
17 "display": "flex",
18 }
19 matches := true
20 for name, value := range styles {
21 if n.Style[name] != value && !(value == "*") && n.Style[name] != "" {
22 matches = false
23 }
24 }
25 return matches
26 },
27 Level: 3,
28 Handler: func(n *element.Node, state *map[string]element.State) {
29 s := *state
30 self := s[n.Properties.Id]
31
32 verbs := strings.Split(n.Style["flex-direction"], "-")
33 flexDirection := verbs[0]
34 flexReversed := false
35 if len(verbs) > 1 {
36 flexReversed = true
37 }
38
39 flexWrapped := !(n.Style["flex-wrap"] != "nowrap")
40
41 hAlign := n.Style["align-content"]
42 if hAlign == "" {
43 hAlign = "normal"
44 }
45 vAlign := n.Style["align-items"]
46 if vAlign == "" {
47 vAlign = "normal"
48 }
49 justify := n.Style["justify-items"]
50 if justify == "" {
51 justify = "normal"
52 }
53
54 fmt.Println(flexDirection, flexReversed, flexWrapped, hAlign, vAlign, justify)
55
56 if flexDirection == "row" && !flexReversed && !flexWrapped {
57 textTotal := 0
58 textCounts := []int{}
59 widths := []float32{}
60 innerSizes := [][]float32{}
61 minWidths := []float32{}
62 for _, v := range n.Children {
63 // vState := s[v.Properties.Id]
64 count := countText(v)
65 textTotal += count
66 textCounts = append(textCounts, count)
67
68 minw := getMinWidth(&v, state)
69 minWidths = append(minWidths, minw)
70
71 w, h := getInnerSize(&v, state)
72 innerSizes = append(innerSizes, []float32{w, h})
73 }
74 selfWidth := (self.Width - self.Padding.Left) - self.Padding.Right
75 // If the elements are under the size of the container to need to resize
76 if add2d(innerSizes, 0) < selfWidth {
77 for _, v := range innerSizes {
78 widths = append(widths, v[0])
79 }
80 } else {
81 // Modifiy the widths so they aren't under the mins
82 for i, v := range n.Children {
83 vState := s[v.Properties.Id]
84
85 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
86 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
87
88 if w < minWidths[i] {
89 selfWidth -= minWidths[i] + vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
90 textTotal -= textCounts[i]
91 textCounts[i] = 0
92 }
93
94 }
95 for i, v := range n.Children {
96 vState := s[v.Properties.Id]
97
98 w := ((selfWidth / float32(textTotal)) * float32(textCounts[i]))
99 w -= vState.Margin.Left + vState.Margin.Right + (vState.Border.Width * 2)
100 if w < minWidths[i] {
101 w = minWidths[i]
102 }
103
104 widths = append(widths, w)
105 }
106 }
107 // Apply the new widths
108 fState := s[n.Children[0].Properties.Id]
109 for i, v := range n.Children {
110 vState := s[v.Properties.Id]
111
112 vState.Width = widths[i]
113 xStore := vState.X
114 if i > 0 {
115 sState := s[n.Children[i-1].Properties.Id]
116 vState.X = sState.X + sState.Width + sState.Margin.Right + vState.Margin.Left + sState.Border.Width + vState.Border.Width
117 propagateOffsets(&v, xStore, vState.Y, vState.X, fState.Y, state)
118 }
119
120 vState.Y = fState.Y
121
122 (*state)[v.Properties.Id] = vState
123 deInline(&v, state)
124 applyInline(&v, state)
125 applyBlock(&v, state)
126 }
127 // Set the heights based on the tallest one
128 if n.Style["height"] == "" {
129
130 innerSizes = [][]float32{}
131 for _, v := range n.Children {
132 w, h := getInnerSize(&v, state)
133 innerSizes = append(innerSizes, []float32{w, h})
134 }
135 sort.Slice(innerSizes, func(i, j int) bool {
136 return innerSizes[i][1] > innerSizes[j][1]
137 })
138 } else {
139 innerSizes[0][1] = self.Height
140 }
141 for _, v := range n.Children {
142 vState := s[v.Properties.Id]
143 vState.Height = innerSizes[0][1]
144 (*state)[v.Properties.Id] = vState
145 }
146
147 }
148 if n.Style["height"] == "" {
149 _, h := getInnerSize(n, state)
150 self.Height = h
151 }
152 (*state)[n.Properties.Id] = self
153 },
154 }
155}
156
157func applyBlock(n *element.Node, state *map[string]element.State) {
158 accum := float32(0)
159 inlineOffset := float32(0)
160 s := *state
161 lastHeight := float32(0)
162 baseY := s[n.Children[0].Properties.Id].Y
163 for i := 0; i < len(n.Children); i++ {
164 v := &n.Children[i]
165 vState := s[v.Properties.Id]
166
167 if v.Style["display"] != "block" {
168 vState.Y += inlineOffset
169 accum = (vState.Y - baseY)
170 lastHeight = vState.Height
171 } else if v.Style["position"] != "absolute" {
172 vState.Y += accum
173 inlineOffset += (vState.Height + (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom) + lastHeight
174 }
175 (*state)[v.Properties.Id] = vState
176 }
177}
178
179func deInline(n *element.Node, state *map[string]element.State) {
180 s := *state
181 // self := s[n.Properties.Id]
182 baseX := float32(-1)
183 baseY := float32(-1)
184 for _, v := range n.Children {
185 vState := s[v.Properties.Id]
186
187 if v.Style["display"] == "inline" {
188 if baseX < 0 && baseY < 0 {
189 baseX = vState.X
190 baseY = vState.Y
191 } else {
192 vState.X = baseX
193 vState.Y = baseY
194 (*state)[v.Properties.Id] = vState
195
196 }
197 } else {
198 baseX = float32(-1)
199 baseY = float32(-1)
200 }
201
202 if len(v.Children) > 0 {
203 deInline(&v, state)
204 }
205 }
206
207}
208
209func applyInline(n *element.Node, state *map[string]element.State) {
210 pl := inline.Init()
211 for i := 0; i < len(n.Children); i++ {
212 v := &n.Children[i]
213
214 if len(v.Children) > 0 {
215 applyInline(v, state)
216 }
217
218 if pl.Selector(v) {
219 pl.Handler(v, state)
220 }
221 }
222}
223
224func propagateOffsets(n *element.Node, prevx, prevy, newx, newy float32, state *map[string]element.State) {
225 s := *state
226 for _, v := range n.Children {
227 vState := s[v.Properties.Id]
228 xStore := (vState.X - prevx) + newx
229 yStore := (vState.Y - prevy) + newy
230
231 if len(v.Children) > 0 {
232 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
233 }
234 vState.X = xStore
235 vState.Y = yStore
236 (*state)[v.Properties.Id] = vState
237 }
238
239}
240
241func countText(n element.Node) int {
242 count := 0
243 groups := []int{}
244 for _, v := range n.Children {
245 if v.TagName == "notaspan" {
246 count += 1
247 }
248 if v.Style["display"] == "block" {
249 groups = append(groups, count)
250 count = 0
251 }
252 if len(v.Children) > 0 {
253 count += countText(v)
254 }
255 }
256 groups = append(groups, count)
257
258 sort.Slice(groups, func(i, j int) bool {
259 return groups[i] > groups[j]
260 })
261 return groups[0]
262}
263
264func getMinWidth(n *element.Node, state *map[string]element.State) float32 {
265 s := *state
266 self := s[n.Properties.Id]
267 selfWidth := float32(0)
268
269 if len(n.Children) > 0 {
270 for _, v := range n.Children {
271 selfWidth = utils.Max(selfWidth, getNodeWidth(&v, state))
272 }
273 } else {
274 selfWidth = self.Width
275 }
276
277 selfWidth += self.Padding.Left + self.Padding.Right
278 return selfWidth
279}
280
281func getNodeWidth(n *element.Node, state *map[string]element.State) float32 {
282 s := *state
283 self := s[n.Properties.Id]
284 w := float32(0)
285 w += self.Padding.Left
286 w += self.Padding.Right
287
288 w += self.Margin.Left
289 w += self.Margin.Right
290
291 w += self.Width
292
293 w += self.Border.Width * 2
294
295 for _, v := range n.Children {
296 w = utils.Max(w, getNodeWidth(&v, state))
297 }
298
299 return w
300}
301
302func getInnerSize(n *element.Node, state *map[string]element.State) (float32, float32) {
303 s := *state
304 self := s[n.Properties.Id]
305
306 minx := float32(10e10)
307 maxw := float32(0)
308 miny := float32(10e10)
309 maxh := float32(0)
310 for _, v := range n.Children {
311 vState := s[v.Properties.Id]
312 minx = utils.Min(vState.X, minx)
313 miny = utils.Min(vState.Y, miny)
314 hOffset := (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom
315 wOffset := (vState.Border.Width * 2) + vState.Margin.Left + vState.Margin.Right
316 maxw = utils.Max(vState.X+vState.Width+wOffset, maxw)
317 maxh = utils.Max(vState.Y+vState.Height+hOffset, maxh)
318 }
319 w := maxw - minx
320 h := maxh - miny
321
322 w += self.Padding.Left + self.Padding.Right
323 h += self.Padding.Top + self.Padding.Bottom
324 return w, h
325}
326
327func add2d(arr [][]float32, index int) float32 {
328 var sum float32
329 if len(arr) == 0 {
330 return sum
331 }
332
333 for i := 0; i < len(arr); i++ {
334 if len(arr[i]) <= index {
335 return sum
336 }
337 sum += arr[i][index]
338 }
339
340 return sum
341}
342
343// getMinHeight(n,state)
344// calcHeight(n,width,state)
345// calcWidth(n,height,state)