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 innerSizes = [][]float32{}
129 for _, v := range n.Children {
130 w, h := getInnerSize(&v, state)
131 innerSizes = append(innerSizes, []float32{w, h})
132 }
133 sort.Slice(innerSizes, func(i, j int) bool {
134 return innerSizes[i][1] > innerSizes[j][1]
135 })
136 for _, v := range n.Children {
137 vState := s[v.Properties.Id]
138 vState.Height = innerSizes[0][1]
139 (*state)[v.Properties.Id] = vState
140 }
141
142 }
143 _, h := getInnerSize(n, state)
144 self.Height = h
145 (*state)[n.Properties.Id] = self
146 },
147 }
148}
149
150func applyBlock(n *element.Node, state *map[string]element.State) {
151 accum := float32(0)
152 inlineOffset := float32(0)
153 s := *state
154 baseY := s[n.Children[0].Properties.Id].Y
155 for i := 0; i < len(n.Children); i++ {
156 v := &n.Children[i]
157 vState := s[v.Properties.Id]
158
159 if v.Style["display"] != "block" {
160 accum = (vState.Y - baseY)
161 vState.Y += inlineOffset
162 } else if v.Style["position"] != "absolute" {
163 vState.Y += accum
164 // !ISSUE: need to account fo rmultiple block elements and margin padding border and non asbsoulute
165 inlineOffset = ((vState.Y - baseY) + (vState.Height + (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom)) - accum
166 accum = 0
167 }
168 (*state)[v.Properties.Id] = vState
169 }
170}
171
172func deInline(n *element.Node, state *map[string]element.State) {
173 s := *state
174 // self := s[n.Properties.Id]
175 baseX := float32(-1)
176 baseY := float32(-1)
177 for _, v := range n.Children {
178 vState := s[v.Properties.Id]
179
180 if v.Style["display"] == "inline" {
181 if baseX < 0 && baseY < 0 {
182 baseX = vState.X
183 baseY = vState.Y
184 } else {
185 vState.X = baseX
186 vState.Y = baseY
187 (*state)[v.Properties.Id] = vState
188
189 }
190 } else {
191 baseX = float32(-1)
192 baseY = float32(-1)
193 }
194
195 if len(v.Children) > 0 {
196 deInline(&v, state)
197 }
198 }
199
200}
201
202func applyInline(n *element.Node, state *map[string]element.State) {
203 pl := inline.Init()
204 for i := 0; i < len(n.Children); i++ {
205 v := &n.Children[i]
206
207 if len(v.Children) > 0 {
208 applyInline(v, state)
209 }
210
211 if pl.Selector(v) {
212 pl.Handler(v, state)
213 }
214 }
215}
216
217func propagateOffsets(n *element.Node, prevx, prevy, newx, newy float32, state *map[string]element.State) {
218 s := *state
219 for _, v := range n.Children {
220 vState := s[v.Properties.Id]
221 xStore := (vState.X - prevx) + newx
222 yStore := (vState.Y - prevy) + newy
223
224 if len(v.Children) > 0 {
225 propagateOffsets(&v, vState.X, vState.Y, xStore, yStore, state)
226 }
227 vState.X = xStore
228 vState.Y = yStore
229 (*state)[v.Properties.Id] = vState
230 }
231
232}
233
234func countText(n element.Node) int {
235 count := 0
236 groups := []int{}
237 for _, v := range n.Children {
238 if v.TagName == "notaspan" {
239 count += 1
240 }
241 if v.Style["display"] == "block" {
242 groups = append(groups, count)
243 count = 0
244 }
245 if len(v.Children) > 0 {
246 count += countText(v)
247 }
248 }
249 groups = append(groups, count)
250
251 sort.Slice(groups, func(i, j int) bool {
252 return groups[i] > groups[j]
253 })
254 return groups[0]
255}
256
257func getMinWidth(n *element.Node, state *map[string]element.State) float32 {
258 s := *state
259 self := s[n.Properties.Id]
260 selfWidth := float32(0)
261
262 if len(n.Children) > 0 {
263 for _, v := range n.Children {
264 selfWidth = utils.Max(selfWidth, getNodeWidth(&v, state))
265 }
266 } else {
267 selfWidth = self.Width
268 }
269
270 selfWidth += self.Padding.Left + self.Padding.Right
271 return selfWidth
272}
273
274func getNodeWidth(n *element.Node, state *map[string]element.State) float32 {
275 s := *state
276 self := s[n.Properties.Id]
277 w := float32(0)
278 w += self.Padding.Left
279 w += self.Padding.Right
280
281 w += self.Margin.Left
282 w += self.Margin.Right
283
284 w += self.Width
285
286 w += self.Border.Width * 2
287
288 for _, v := range n.Children {
289 w = utils.Max(w, getNodeWidth(&v, state))
290 }
291
292 return w
293}
294
295func getInnerSize(n *element.Node, state *map[string]element.State) (float32, float32) {
296 s := *state
297 self := s[n.Properties.Id]
298
299 minx := float32(10e10)
300 maxw := float32(0)
301 miny := float32(10e10)
302 maxh := float32(0)
303 for _, v := range n.Children {
304 vState := s[v.Properties.Id]
305 minx = utils.Min(vState.X, minx)
306 miny = utils.Min(vState.Y, miny)
307 hOffset := (vState.Border.Width * 2) + vState.Margin.Top + vState.Margin.Bottom
308 wOffset := (vState.Border.Width * 2) + vState.Margin.Left + vState.Margin.Right
309 maxw = utils.Max(vState.X+vState.Width+wOffset, maxw)
310 maxh = utils.Max(vState.Y+vState.Height+hOffset, maxh)
311 }
312 w := maxw - minx
313 h := maxh - miny
314
315 w += self.Padding.Left + self.Padding.Right
316 h += self.Padding.Top + self.Padding.Bottom
317 return w, h
318}
319
320func add2d(arr [][]float32, index int) float32 {
321 var sum float32
322 if len(arr) == 0 {
323 return sum
324 }
325
326 for i := 0; i < len(arr); i++ {
327 if len(arr[i]) <= index {
328 return sum
329 }
330 sum += arr[i][index]
331 }
332
333 return sum
334}
335
336// getMinHeight(n,state)
337// calcHeight(n,width,state)
338// calcWidth(n,height,state)