Commits
Diff
diff --git a/docs/cstyle/index.html b/docs/cstyle/index.html
index 8b6c686..d381442 100644
--- a/docs/cstyle/index.html
+++ b/docs/cstyle/index.html
@@ -283,52 +283,52 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
-242 self .X = x
- 243 self .Y = y
- 244 self .Width = width
- 245 self .Height = height + self .Padding .Bottom
- 246 (* state )[n .Properties .Id ] = self
- 247
- 248 if !utils .ChildrenHaveText (n ) && len(n .InnerText ) > 0 {
- 249 // Confirm text exists
- 250 words := strings .Split (strings .TrimSpace (n .InnerText ), " " )
-251 if len(words ) != 1 {
- 252 if n .Style ["display" ] == "inline" {
- 253 n .InnerText = words [0 ]
- 254 el := * n
- 255 el .InnerText = strings .Join (words [1 :], " " )
- 256 n .Parent .InsertAfter (el , * n )
- 257 }
- 258 // !ISSUE: change genTextNode to basically a image generator for a word.
- 259 // + make it so this api can be used to also do images
- 260 // + element.State.Texture?
- 261 }
-262 if len(strings .TrimSpace (n .InnerText )) > 0 {
- 263 n .InnerText = strings .TrimSpace (n .InnerText )
- 264 self = genTextNode (n , state )
- 265 }
- 266 }
- 267
- 268 (* state )[n .Properties .Id ] = self
- 269 (* state )[n .Parent .Properties .Id ] = parent
- 270
- 271 // Call children here
- 272
-273 var childYOffset float32
- 274 for i := 0 ; i < len(n .Children ); i ++ {
- 275 v := n .Children [i ]
- 276 v .Parent = n
- 277 n .Children [i ] = * c .ComputeNodeStyle (& v , state )
- 278 cState := (* state )[n .Children [i ].Properties .Id ]
- 279 if n .Style ["height" ] == "" {
- 280 if n .Children [i ].Style ["position" ] != "absolute" && cState .Y > childYOffset {
- 281 childYOffset = cState .Y
- 282 self .Height += cState .Height
- 283 self .Height += cState .Margin .Top
- 284 self .Height += cState .Margin .Bottom
- 285 self .Height += cState .Padding .Top
- 286 self .Height += cState .Padding .Bottom
- 287 }
- 288 }
- 289 // fmt.Println(n.TagName, self.Width, v.TagName, cState.Width)
- 290 if cState .Width > self .Width {
-291 self .Width = cState .Width
- 292 }
- 293 }
+ 242 if !utils .ChildrenHaveText (n ) {
+ 243 // Confirm text exists
+ 244 if len(n .InnerText ) > 0 {
+245 words := strings .Split (n .InnerText , " " )
+ 246 if len(words ) != 1 && !strings .Contains (n .Properties .Id , "copy" ) && n .Style ["display" ] == "inline" {
+ 247 n .InnerText = words [0 ]
+ 248 n .Properties .Id = "copy" + n .Properties .Id
+ 249 // slices.Reverse(words)
+ 250 // !ISSUE: I thinkthis is a good route to take, might make a style prop for text that a plugins can be used to turn the groups
+ 251 // + into psuedo elements but make them inline
+ 252 // + that would take care of wrapping and it would also work for display block
+ 253 for _ , v := range words {
+254 if len(strings .TrimSpace (v )) > 0 {
+ 255 el := * n
+ 256 el .InnerText = v
+ 257 for k , val := range n .Style {
+ 258 el .Style [k ] = val
+ 259 }
+ 260 n .Parent .InsertAfter (el , * n )
+ 261 }
+ 262 }
+ 263 // !ISSUE: can probally use flex to do it need to group text so it doesn't include block element
+ 264 // + if there is a block element break then go on
+ 265 // fmt.Println(n.Parent.Children)
+ 266 }
+267 innerWidth := width
+ 268 innerHeight := height
+ 269 (* state )[n .Properties .Id ] = self
+ 270 // !ISSUE: change genTextNode to basically a image generator for a word.
+ 271 // + make it so this api can be used to also do images
+ 272 self = genTextNode (n , & innerWidth , & innerHeight , p , state )
+273 width = innerWidth + p .Left + p .Right
+ 274 height = innerHeight
+ 275 }
+ 276 }
+ 277
+ 278 // if !utils.ChildrenHaveText(n) {
+ 279 // // Confirm text exists
+ 280 // if len(n.InnerText) > 0 {
+ 281 // innerWidth := width
+ 282 // innerHeight := height
+ 283 // (*state)[n.Properties.Id] = self
+ 284 // self = genTextNode(n, &innerWidth, &innerHeight, p, state)
+ 285 // width = innerWidth + p.Left + p.Right
+ 286 // height = innerHeight
+ 287 // }
+ 288 // }
+ 289
+290 self .X = x
+ 291 self .Y = y
+ 292 self .Width = width
+ 293 self .Height = height
@@ -337,43 +337,43 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 296
- 297 // Sorting the array by the Level field
- 298 sort .Slice (plugins , func (i , j int ) bool {
-299 return plugins [i ].Level < plugins [j ].Level
- 300 })
- 301
- 302 for _ , v := range plugins {
- 303 matches := true
- 304 for name , value := range v .Styles {
- 305 if n .Style [name ] != value && !(value == "*" ) {
- 306 matches = false
- 307 }
- 308 }
- 309 if matches {
- 310 v .Handler (n , state )
- 311 }
- 312 }
- 313
- 314 for i := range n .Children {
- 315 cState := (* state )[n .Children [i ].Properties .Id ]
- 316 cState .Y += self .Padding .Top
- 317 (* state )[n .Children [i ].Properties .Id ] = cState
- 318 }
- 319
- 320 // CheckNode(n, state)
- 321
-322 return n
- 323 }
- 324
- 325 func parseBorderShorthand (borderShorthand string ) (element .Border , error ) {
-326 // Split the shorthand into components
- 327 borderComponents := strings .Fields (borderShorthand )
-328
- 329 // Ensure there are at least 1 component (width or style or color)
- 330 if len(borderComponents ) >= 1 {
-331 width := "0px" // Default width
- 332 style := "solid"
-333 borderColor := "#000000" // Default color
- 334
-335 // Extract style and color if available
- 336 if len(borderComponents ) >= 1 {
-337 width = borderComponents [0 ]
- 338 }
+ 296 (* state )[n .Parent .Properties .Id ] = parent
+ 297
+ 298 // Call children here
+ 299
+300 var childYOffset float32
+ 301 for i , v := range n .Children {
+ 302 v .Parent = n
+ 303 n .Children [i ] = * c .ComputeNodeStyle (& v , state )
+ 304 cState := (* state )[n .Children [i ].Properties .Id ]
+ 305 if n .Style ["height" ] == "" {
+ 306 if n .Children [i ].Style ["position" ] != "absolute" && cState .Y > childYOffset {
+ 307 childYOffset = cState .Y
+ 308 self .Height += cState .Height
+ 309 self .Height += cState .Margin .Top
+ 310 self .Height += cState .Margin .Bottom
+ 311 self .Height += cState .Padding .Top
+ 312 self .Height += cState .Padding .Bottom
+ 313 }
+ 314 }
+ 315 // fmt.Println(n.TagName, self.Width, v.TagName, cState.Width)
+ 316 if cState .Width > self .Width {
+317 self .Width = cState .Width
+ 318 }
+ 319 }
+ 320
+ 321 (* state )[n .Properties .Id ] = self
+ 322
+ 323 // Sorting the array by the Level field
+ 324 sort .Slice (plugins , func (i , j int ) bool {
+325 return plugins [i ].Level < plugins [j ].Level
+ 326 })
+ 327
+ 328 for _ , v := range plugins {
+ 329 matches := true
+ 330 for name , value := range v .Styles {
+ 331 if n .Style [name ] != value && !(value == "*" ) {
+ 332 matches = false
+ 333 }
+ 334 }
+ 335 if matches {
+ 336 v .Handler (n , state )
+ 337 }
+ 338 }
@@ -381,27 +381,27 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 340 // Extract style and color if available
- 341 if len(borderComponents ) >= 2 {
-342 style = borderComponents [1 ]
- 343 }
- 344 if len(borderComponents ) >= 3 {
- 345 borderColor = borderComponents [2 ]
- 346 }
- 347
- 348 parsedColor , _ := color .Color (borderColor )
- 349
- 350 return element .Border {
- 351 Width : width ,
- 352 Style : style ,
- 353 Color : parsedColor ,
- 354 Radius : "" , // Default radius
- 355 }, nil
-356 }
- 357
- 358 return element .Border {}, fmt .Errorf ("invalid border shorthand format" )
- 359 }
- 360
- 361 func CompleteBorder (cssProperties map [string ]string ) (element .Border , error ) {
-362 border , err := parseBorderShorthand (cssProperties ["border" ])
- 363 border .Radius = cssProperties ["border-radius" ]
- 364
- 365 return border , err
- 366 }
+ 340 CheckNode (n , state )
+ 341
+ 342 return n
+ 343 }
+ 344
+ 345 func parseBorderShorthand (borderShorthand string ) (element .Border , error ) {
+346 // Split the shorthand into components
+ 347 borderComponents := strings .Fields (borderShorthand )
+348
+ 349 // Ensure there are at least 1 component (width or style or color)
+ 350 if len(borderComponents ) >= 1 {
+351 width := "0px" // Default width
+ 352 style := "solid"
+353 borderColor := "#000000" // Default color
+ 354
+355 // Extract style and color if available
+ 356 if len(borderComponents ) >= 1 {
+357 width = borderComponents [0 ]
+ 358 }
+ 359
+ 360 // Extract style and color if available
+ 361 if len(borderComponents ) >= 2 {
+362 style = borderComponents [1 ]
+ 363 }
+ 364 if len(borderComponents ) >= 3 {
+ 365 borderColor = borderComponents [2 ]
+ 366 }
@@ -409,19 +409,19 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 368 func genTextNode (n * element .Node , state * map [string ]element .State ) element .State {
-369 s := * state
- 370 self := s [n .Properties .Id ]
- 371 parent := s [n .Parent .Properties .Id ]
- 372
- 373 bold , italic := false , false
- 374
- 375 if n .Style ["font-weight" ] == "bold" {
- 376 bold = true
- 377 }
- 378
- 379 if n .Style ["font-style" ] == "italic" {
- 380 italic = true
- 381 }
- 382
- 383 if self .Text .Font == nil {
- 384 f , _ := font .LoadFont (n .Style ["font-family" ], int(self .EM ), bold , italic )
- 385 self .Text .Font = f
- 386 }
+ 368 parsedColor , _ := color .Color (borderColor )
+ 369
+ 370 return element .Border {
+ 371 Width : width ,
+ 372 Style : style ,
+ 373 Color : parsedColor ,
+ 374 Radius : "" , // Default radius
+ 375 }, nil
+376 }
+ 377
+ 378 return element .Border {}, fmt .Errorf ("invalid border shorthand format" )
+ 379 }
+ 380
+ 381 func CompleteBorder (cssProperties map [string ]string ) (element .Border , error ) {
+382 border , err := parseBorderShorthand (cssProperties ["border" ])
+ 383 border .Radius = cssProperties ["border-radius" ]
+ 384
+ 385 return border , err
+ 386 }
@@ -429,6 +429,6 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 388 letterSpacing , _ := utils .ConvertToPixels (n .Style ["letter-spacing" ], self .EM , parent .Width )
- 389 wordSpacing , _ := utils .ConvertToPixels (n .Style ["word-spacing" ], self .EM , parent .Width )
- 390 lineHeight , _ := utils .ConvertToPixels (n .Style ["line-height" ], self .EM , parent .Width )
- 391 if lineHeight == 0 {
- 392 lineHeight = self .EM + 3
- 393 }
+ 388 func genTextNode (n * element .Node , width , height * float32 , p element .MarginPadding , state * map [string ]element .State ) element .State {
+389 s := * state
+ 390 self := s [n .Properties .Id ]
+ 391 parent := s [n .Parent .Properties .Id ]
+ 392
+ 393 bold , italic := false , false
@@ -436,11 +436,11 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 395 self .Text .LineHeight = int(lineHeight )
- 396 self .Text .WordSpacing = int(wordSpacing )
- 397 self .Text .LetterSpacing = int(letterSpacing )
- 398 wb := " "
- 399
- 400 if n .Style ["word-wrap" ] == "break-word" {
- 401 wb = ""
- 402 }
- 403
- 404 if n .Style ["text-wrap" ] == "wrap" || n .Style ["text-wrap" ] == "balance" {
- 405 wb = ""
+ 395 if n .Style ["font-weight" ] == "bold" {
+ 396 bold = true
+ 397 }
+ 398
+ 399 if n .Style ["font-style" ] == "italic" {
+ 400 italic = true
+ 401 }
+ 402
+ 403 if self .Text .Font == nil {
+ 404 f , _ := font .LoadFont (n .Style ["font-family" ], int(self .EM ), bold , italic )
+ 405 self .Text .Font = f
@@ -449,40 +449,66 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 408 var dt float32
- 409
- 410 if n .Style ["text-decoration-thickness" ] == "auto" || n .Style ["text-decoration-thickness" ] == "" {
- 411 dt = 3
- 412 } else {
- 413 dt , _ = utils .ConvertToPixels (n .Style ["text-decoration-thickness" ], self .EM , parent .Width )
- 414 }
- 415
- 416 col := color .Parse (n .Style , "font" )
- 417
- 418 self .Text .Color = col
- 419 self .Text .DecorationColor = color .Parse (n .Style , "decoration" )
- 420 self .Text .Align = n .Style ["text-align" ]
- 421 self .Text .WordBreak = wb
- 422 self .Text .WordSpacing = int(wordSpacing )
- 423 self .Text .LetterSpacing = int(letterSpacing )
- 424 self .Text .WhiteSpace = n .Style ["white-space" ]
- 425 self .Text .DecorationThickness = int(dt )
- 426 self .Text .Overlined = n .Style ["text-decoration" ] == "overline"
- 427 self .Text .Underlined = n .Style ["text-decoration" ] == "underline"
- 428 self .Text .LineThrough = n .Style ["text-decoration" ] == "linethrough"
- 429 self .Text .EM = int(self .EM )
- 430 self .Text .Width = int(parent .Width )
- 431 self .Text .Text = n .InnerText
- 432
- 433 if n .Style ["word-spacing" ] == "" {
- 434 self .Text .WordSpacing = font .MeasureSpace (& self .Text )
- 435 }
- 436
- 437 img , width := font .Render (& self )
- 438 self .Text .Image = img
- 439 self .Text .Width = int(width )
- 440 self .Width = float32(width )
- 441
- 442 if n .Style ["height" ] == "" {
- 443 self .Height = float32(self .Text .LineHeight )
- 444 }
- 445
- 446 return self
- 447 }
+ 408 letterSpacing , _ := utils .ConvertToPixels (n .Style ["letter-spacing" ], self .EM , * width )
+ 409 wordSpacing , _ := utils .ConvertToPixels (n .Style ["word-spacing" ], self .EM , * width )
+ 410 lineHeight , _ := utils .ConvertToPixels (n .Style ["line-height" ], self .EM , * width )
+ 411 if lineHeight == 0 {
+ 412 lineHeight = self .EM + 3
+ 413 }
+ 414
+ 415 self .Text .LineHeight = int(lineHeight )
+ 416 self .Text .WordSpacing = int(wordSpacing )
+ 417 self .Text .LetterSpacing = int(letterSpacing )
+ 418 wb := " "
+ 419
+ 420 if n .Style ["word-wrap" ] == "break-word" {
+ 421 wb = ""
+ 422 }
+ 423
+ 424 if n .Style ["text-wrap" ] == "wrap" || n .Style ["text-wrap" ] == "balance" {
+ 425 wb = ""
+ 426 }
+ 427
+ 428 var dt float32
+ 429
+ 430 if n .Style ["text-decoration-thickness" ] == "auto" || n .Style ["text-decoration-thickness" ] == "" {
+ 431 dt = 3
+ 432 } else {
+ 433 dt , _ = utils .ConvertToPixels (n .Style ["text-decoration-thickness" ], self .EM , * width )
+ 434 }
+ 435
+ 436 col := color .Parse (n .Style , "font" )
+ 437
+ 438 self .Text .Color = col
+ 439 self .Text .DecorationColor = color .Parse (n .Style , "decoration" )
+ 440 self .Text .Align = n .Style ["text-align" ]
+ 441 self .Text .WordBreak = wb
+ 442 self .Text .WordSpacing = int(wordSpacing )
+ 443 self .Text .LetterSpacing = int(letterSpacing )
+ 444 self .Text .WhiteSpace = n .Style ["white-space" ]
+ 445 self .Text .DecorationThickness = int(dt )
+ 446 self .Text .Overlined = n .Style ["text-decoration" ] == "overline"
+ 447 self .Text .Underlined = n .Style ["text-decoration" ] == "underline"
+ 448 self .Text .LineThrough = n .Style ["text-decoration" ] == "linethrough"
+ 449 self .Text .EM = int(self .EM )
+ 450 self .Text .Width = int(parent .Width )
+ 451 self .Text .Text = n .InnerText
+ 452
+ 453 if n .Style ["word-spacing" ] == "" {
+ 454 self .Text .WordSpacing = font .MeasureSpace (& self .Text )
+ 455 }
+ 456 if parent .Width != 0 && n .Style ["display" ] != "inline" && n .Style ["width" ] == "" {
+ 457 * width = (parent .Width - p .Right ) - p .Left
+ 458 } else if n .Style ["width" ] == "" {
+ 459 * width = utils .Max (* width , float32(font .MeasureLongest (& self )))
+ 460 } else if n .Style ["width" ] != "" {
+ 461 * width , _ = utils .ConvertToPixels (n .Style ["width" ], self .EM , parent .Width )
+ 462 }
+ 463
+ 464 self .Text .Width = int(* width )
+ 465 self .Width = * width
+ 466 h := font .Render (& self )
+ 467 if n .Style ["height" ] == "" {
+ 468 * height = h
+ 469 }
+ 470
+ 471 return self
+ 472
+ 473 }
GRIM UI
GRIM
Docs
CStyle
CStyle is a single pass style computer.
# StyleSheet?(go)
# StyleTag?(go)
# GetStyles?(go)
# AddPlugin?(go)
See /cstyle/plugins/
# ComputeNodeStyle?(go)
if !utils.ChildrenHaveText(n)
The utils.ChildrenHaveText function is called here instead of checking if the node directly has text is because in the case below
1 <em ><b >Text</b ></em >
The em
element does not have text but it has a element with text insid, but the element will still need to be rendered as text.
# parseBorderShorthand?(go)
# CompleteBorder?(go)
# genTextNode?(go)
1 package cstyle
2
3 // package aui/goldie
4 // https://pkg.go.dev/automated.sh/goldie
5 // https://pkg.go.dev/automated.sh/aui
6 // https://pkg.go.dev/automated.sh/oat
7
8 import (
9 "fmt"
10 "gui/color"
11 "gui/element"
12 "gui/font"
13 "gui/parser"
14 "gui/utils"
15 "os"
16 "slices"
17 "sort"
18 "strings"
19 )
20
21 type Plugin struct {
22 Styles map [string ]string
23 Level int
24 Handler func (* element .Node , * map [string ]element .State )
25 }
26
27 type CSS struct {
28 Width float32
29 Height float32
30 StyleSheets []map [string ]map [string ]string
31 Plugins []Plugin
32 Document * element .Node
33 }
34
35 func (c * CSS ) StyleSheet (path string ) {
36 // Parse the CSS file
37 dat , err := os .ReadFile (path )
38 utils .Check (err )
39 styles := parser .ParseCSS (string(dat ))
40
41 c .StyleSheets = append(c .StyleSheets , styles )
42 }
43
44 func (c * CSS ) StyleTag (css string ) {
45 styles := parser .ParseCSS (css )
46 c .StyleSheets = append(c .StyleSheets , styles )
47 }
48
49 var inheritedProps = []string {
50 "color" ,
51 "cursor" ,
52 "font" ,
53 "font-family" ,
54 "font-size" ,
55 "font-style" ,
56 "font-weight" ,
57 "letter-spacing" ,
58 "line-height" ,
59 "text-align" ,
60 "text-indent" ,
61 "text-justify" ,
62 "text-shadow" ,
63 "text-transform" ,
64 "visibility" ,
65 "word-spacing" ,
66 "display" ,
67 }
68
69 func (c * CSS ) GetStyles (n element .Node ) map [string ]string {
70 styles := map [string ]string {}
71
72 if n .Parent != nil {
73 ps := c .GetStyles (* n .Parent )
74 for _ , v := range inheritedProps {
75 if ps [v ] != "" {
76 styles [v ] = ps [v ]
77 }
78 }
79 }
80 for k , v := range n .Style {
81 styles [k ] = v
82 }
83 hovered := false
84 if slices .Contains (n .ClassList .Classes , ":hover" ) {
85 hovered = true
86 }
87
88 for _ , styleSheet := range c .StyleSheets {
89 for selector := range styleSheet {
90 // fmt.Println(selector, n.Properties.Id)
91 key := selector
92 if strings .Contains (selector , ":hover" ) && hovered {
93 selector = strings .Replace (selector , ":hover" , "" , - 1 )
94 }
95 if element .TestSelector (selector , & n ) {
96 for k , v := range styleSheet [key ] {
97 styles [k ] = v
98 }
99 }
100
101 }
102 }
103
104 // !ISSUE: why is this needed, the "attribute" is n.Style that should be mapped during init
105 // + when a user adds a style via the style attirbute it will just be in the .Style prop...
106 inline := parser .ParseStyleAttribute (n .GetAttribute ("style" ) + ";" )
107 styles = utils .Merge (styles , inline )
108 // add hover and focus css events
109
110 return styles
111 }
112
113 func (c * CSS ) AddPlugin (plugin Plugin ) {
114 c .Plugins = append(c .Plugins , plugin )
115 }
116
117 func CheckNode (n * element .Node , state * map [string ]element .State ) {
118 s := * state
119 self := s [n .Properties .Id ]
120
121 fmt .Println (n .TagName , n .Properties .Id )
122 fmt .Printf ("ID: %v\n" , n .Id )
123 fmt .Printf ("Parent: %v\n" , n .Parent .TagName )
124 fmt .Printf ("Classes: %v\n" , n .ClassList .Classes )
125 fmt .Printf ("Text: %v\n" , self .Text .Text )
126 fmt .Printf ("X: %v, Y: %v\n" , self .X , self .Y )
127 fmt .Printf ("Width: %v, Height: %v\n" , self .Width , self .Height )
128 fmt .Printf ("Styles: %v\n" , n .Style )
129 fmt .Printf ("Background: %v\n" , self .Background )
130 fmt .Printf ("Border: %v\n\n\n" , self .Border )
131 }
132
133 func (c * CSS ) ComputeNodeStyle (n * element .Node , state * map [string ]element .State ) * element .Node {
134 // Head is not renderable
135 if utils .IsParent (* n , "head" ) {
136 return n
137 }
138 plugins := c .Plugins
139 // !ISSUE: This should add to state.Style instead as the element.Node should be un effected by the engine
140 // + currently this adds styles to the style attribute that the use did not explisitly set
141 // + this also applies to the margin/padding and border completer functions
142
143 n .Style = c .GetStyles (* n )
144 s := * state
145 self := s [n .Properties .Id ]
146 parent := s [n .Parent .Properties .Id ]
147
148 self .Background = color .Parse (n .Style , "background" )
149 self .Border , _ = CompleteBorder (n .Style )
150
151 fs , _ := utils .ConvertToPixels (n .Style ["font-size" ], parent .EM , parent .Width )
152 self .EM = fs
153
154 if n .Style ["display" ] == "none" {
155 self .X = 0
156 self .Y = 0
157 self .Width = 0
158 self .Height = 0
159 return n
160 }
161
162 wh := utils .GetWH (* n )
163 width := wh .Width
164 height := wh .Height
165
166 x , y := parent .X , parent .Y
167
168 var top , left , right , bottom bool = false , false , false , false
169
170 m := utils .GetMP (* n , "margin" )
171 p := utils .GetMP (* n , "padding" )
172
173 self .Margin = m
174 self .Padding = p
175
176 if n .Style ["position" ] == "absolute" {
177 bas := utils .GetPositionOffsetNode (n )
178 base := s [bas .Properties .Id ]
179 if n .Style ["top" ] != "" {
180 v , _ := utils .ConvertToPixels (n .Style ["top" ], self .EM , parent .Width )
181 y = v + base .Y
182 top = true
183 }
184 if n .Style ["left" ] != "" {
185 v , _ := utils .ConvertToPixels (n .Style ["left" ], self .EM , parent .Width )
186 x = v + base .X
187 left = true
188 }
189 if n .Style ["right" ] != "" {
190 v , _ := utils .ConvertToPixels (n .Style ["right" ], self .EM , parent .Width )
191 x = (base .Width - width ) - v
192 right = true
193 }
194 if n .Style ["bottom" ] != "" {
195 v , _ := utils .ConvertToPixels (n .Style ["bottom" ], self .EM , parent .Width )
196 y = (base .Height - height ) - v
197 bottom = true
198 }
199 } else {
200 for i , v := range n .Parent .Children {
201 if v .Properties .Id == n .Properties .Id {
202 if i - 1 > 0 {
203 sib := n .Parent .Children [i - 1 ]
204 sibling := s [sib .Properties .Id ]
205 if n .Style ["display" ] == "inline" {
206 if sib .Style ["display" ] == "inline" {
207 y = sibling .Y
208 } else {
209 y = sibling .Y + sibling .Height
210 }
211 } else {
212 y = sibling .Y + sibling .Height
213 }
214 }
215 break
216 } else if n .Style ["display" ] != "inline" {
217 vState := s [v .Properties .Id ]
218 y += vState .Margin .Top + vState .Margin .Bottom + vState .Padding .Top + vState .Padding .Bottom + vState .Height
219 }
220 }
221 }
222
223 // Display modes need to be calculated here
224
225 relPos := !top && !left && !right && !bottom
226
227 if left || relPos {
228 x += m .Left
229 }
230 if top || relPos {
231 y += m .Top
232 }
233 if right {
234 x -= m .Right
235 }
236 if bottom {
237 y -= m .Bottom
238 }
239
240 // fmt.Println(n.InnerText, len(n.Children))
241
242 self .X = x
243 self .Y = y
244 self .Width = width
245 self .Height = height + self .Padding .Bottom
246 (* state )[n .Properties .Id ] = self
247
248 if !utils .ChildrenHaveText (n ) && len(n .InnerText ) > 0 {
249 // Confirm text exists
250 words := strings .Split (strings .TrimSpace (n .InnerText ), " " )
251 if len(words ) != 1 {
252 if n .Style ["display" ] == "inline" {
253 n .InnerText = words [0 ]
254 el := * n
255 el .InnerText = strings .Join (words [1 :], " " )
256 n .Parent .InsertAfter (el , * n )
257 }
258 // !ISSUE: change genTextNode to basically a image generator for a word.
259 // + make it so this api can be used to also do images
260 // + element.State.Texture?
261 }
262 if len(strings .TrimSpace (n .InnerText )) > 0 {
263 n .InnerText = strings .TrimSpace (n .InnerText )
264 self = genTextNode (n , state )
265 }
266 }
267
268 (* state )[n .Properties .Id ] = self
269 (* state )[n .Parent .Properties .Id ] = parent
270
271 // Call children here
272
273 var childYOffset float32
274 for i := 0 ; i < len(n .Children ); i ++ {
275 v := n .Children [i ]
276 v .Parent = n
277 n .Children [i ] = * c .ComputeNodeStyle (& v , state )
278 cState := (* state )[n .Children [i ].Properties .Id ]
279 if n .Style ["height" ] == "" {
280 if n .Children [i ].Style ["position" ] != "absolute" && cState .Y > childYOffset {
281 childYOffset = cState .Y
282 self .Height += cState .Height
283 self .Height += cState .Margin .Top
284 self .Height += cState .Margin .Bottom
285 self .Height += cState .Padding .Top
286 self .Height += cState .Padding .Bottom
287 }
288 }
289 // fmt.Println(n.TagName, self.Width, v.TagName, cState.Width)
290 if cState .Width > self .Width {
291 self .Width = cState .Width
292 }
293 }
294
295 (* state )[n .Properties .Id ] = self
296
297 // Sorting the array by the Level field
298 sort .Slice (plugins , func (i , j int ) bool {
299 return plugins [i ].Level < plugins [j ].Level
300 })
301
302 for _ , v := range plugins {
303 matches := true
304 for name , value := range v .Styles {
305 if n .Style [name ] != value && !(value == "*" ) {
306 matches = false
307 }
308 }
309 if matches {
310 v .Handler (n , state )
311 }
312 }
313
314 for i := range n .Children {
315 cState := (* state )[n .Children [i ].Properties .Id ]
316 cState .Y += self .Padding .Top
317 (* state )[n .Children [i ].Properties .Id ] = cState
318 }
319
320 // CheckNode(n, state)
321
322 return n
323 }
324
325 func parseBorderShorthand (borderShorthand string ) (element .Border , error ) {
326 // Split the shorthand into components
327 borderComponents := strings .Fields (borderShorthand )
328
329 // Ensure there are at least 1 component (width or style or color)
330 if len(borderComponents ) >= 1 {
331 width := "0px" // Default width
332 style := "solid"
333 borderColor := "#000000" // Default color
334
335 // Extract style and color if available
336 if len(borderComponents ) >= 1 {
337 width = borderComponents [0 ]
338 }
339
340 // Extract style and color if available
341 if len(borderComponents ) >= 2 {
342 style = borderComponents [1 ]
343 }
344 if len(borderComponents ) >= 3 {
345 borderColor = borderComponents [2 ]
346 }
347
348 parsedColor , _ := color .Color (borderColor )
349
350 return element .Border {
351 Width : width ,
352 Style : style ,
353 Color : parsedColor ,
354 Radius : "" , // Default radius
355 }, nil
356 }
357
358 return element .Border {}, fmt .Errorf ("invalid border shorthand format" )
359 }
360
361 func CompleteBorder (cssProperties map [string ]string ) (element .Border , error ) {
362 border , err := parseBorderShorthand (cssProperties ["border" ])
363 border .Radius = cssProperties ["border-radius" ]
364
365 return border , err
366 }
367
368 func genTextNode (n * element .Node , state * map [string ]element .State ) element .State {
369 s := * state
370 self := s [n .Properties .Id ]
371 parent := s [n .Parent .Properties .Id ]
372
373 bold , italic := false , false
374
375 if n .Style ["font-weight" ] == "bold" {
376 bold = true
377 }
378
379 if n .Style ["font-style" ] == "italic" {
380 italic = true
381 }
382
383 if self .Text .Font == nil {
384 f , _ := font .LoadFont (n .Style ["font-family" ], int(self .EM ), bold , italic )
385 self .Text .Font = f
386 }
387
388 letterSpacing , _ := utils .ConvertToPixels (n .Style ["letter-spacing" ], self .EM , parent .Width )
389 wordSpacing , _ := utils .ConvertToPixels (n .Style ["word-spacing" ], self .EM , parent .Width )
390 lineHeight , _ := utils .ConvertToPixels (n .Style ["line-height" ], self .EM , parent .Width )
391 if lineHeight == 0 {
392 lineHeight = self .EM + 3
393 }
394
395 self .Text .LineHeight = int(lineHeight )
396 self .Text .WordSpacing = int(wordSpacing )
397 self .Text .LetterSpacing = int(letterSpacing )
398 wb := " "
399
400 if n .Style ["word-wrap" ] == "break-word" {
401 wb = ""
402 }
403
404 if n .Style ["text-wrap" ] == "wrap" || n .Style ["text-wrap" ] == "balance" {
405 wb = ""
406 }
407
408 var dt float32
409
410 if n .Style ["text-decoration-thickness" ] == "auto" || n .Style ["text-decoration-thickness" ] == "" {
411 dt = 3
412 } else {
413 dt , _ = utils .ConvertToPixels (n .Style ["text-decoration-thickness" ], self .EM , parent .Width )
414 }
415
416 col := color .Parse (n .Style , "font" )
417
418 self .Text .Color = col
419 self .Text .DecorationColor = color .Parse (n .Style , "decoration" )
420 self .Text .Align = n .Style ["text-align" ]
421 self .Text .WordBreak = wb
422 self .Text .WordSpacing = int(wordSpacing )
423 self .Text .LetterSpacing = int(letterSpacing )
424 self .Text .WhiteSpace = n .Style ["white-space" ]
425 self .Text .DecorationThickness = int(dt )
426 self .Text .Overlined = n .Style ["text-decoration" ] == "overline"
427 self .Text .Underlined = n .Style ["text-decoration" ] == "underline"
428 self .Text .LineThrough = n .Style ["text-decoration" ] == "linethrough"
429 self .Text .EM = int(self .EM )
430 self .Text .Width = int(parent .Width )
431 self .Text .Text = n .InnerText
432
433 if n .Style ["word-spacing" ] == "" {
434 self .Text .WordSpacing = font .MeasureSpace (& self .Text )
435 }
436
437 img , width := font .Render (& self )
438 self .Text .Image = img
439 self .Text .Width = int(width )
440 self .Width = float32(width )
441
442 if n .Style ["height" ] == "" {
443 self .Height = float32(self .Text .LineHeight )
444 }
445
446 return self
447 }