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