github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/node.go (about) 1 package widget 2 3 import ( 4 "container/list" 5 "fmt" 6 "image" 7 "image/color" 8 "strings" 9 10 "github.com/jmigpin/editor/util/fontutil" 11 "github.com/jmigpin/editor/util/imageutil" 12 "github.com/jmigpin/editor/util/uiutil/event" 13 ) 14 15 type Node interface { 16 fullNode() // ensure that EmbNode can't be directly assigned to a Node 17 18 Embed() *EmbedNode 19 20 InsertBefore(n Node, mark *EmbedNode) 21 Append(n ...Node) 22 Remove(child Node) 23 24 Measure(hint image.Point) image.Point 25 26 LayoutMarked() 27 LayoutTree() 28 Layout() // set childs bounds, don't call childs layout 29 ChildsLayoutTree() 30 31 PaintMarked() image.Rectangle 32 PaintTree() bool 33 PaintBase() // pre-paint step, useful for widgets with a pre-paint stage 34 Paint() 35 ChildsPaintTree() 36 37 OnThemeChange() 38 OnChildMarked(child Node, newMarks Marks) 39 OnInputEvent(ev interface{}, p image.Point) event.Handled 40 } 41 42 //---------- 43 44 // Doesn't allow embed to be assigned to a Node directly, which prevents a range of programming mistakes. This is the node other widgets should inherit from. 45 type ENode struct { 46 EmbedNode 47 } 48 49 func (ENode) fullNode() {} 50 51 //---------- 52 53 type EmbedNode struct { 54 Bounds image.Rectangle 55 Cursor event.Cursor 56 Wrapper Node 57 Parent *EmbedNode 58 59 marks Marks 60 childs list.List 61 elem *list.Element 62 63 theme Theme 64 } 65 66 //---------- 67 68 func (en *EmbedNode) Embed() *EmbedNode { 69 return en 70 } 71 72 // Only the root node should need to set the wrapper explicitly. 73 func (en *EmbedNode) SetWrapperForRoot(n Node) { 74 en.Wrapper = n 75 } 76 77 //---------- 78 79 // If a node wants its InsertBefore implementation to be used, the wrapper must be set. 80 func (en *EmbedNode) Append(nodes ...Node) { 81 for _, n := range nodes { 82 if en.Wrapper != nil { 83 en.Wrapper.InsertBefore(n, nil) 84 } else { 85 en.InsertBefore(n, nil) 86 } 87 } 88 } 89 90 func (en *EmbedNode) InsertBefore(child Node, next *EmbedNode) { 91 childe := child.Embed() 92 93 if childe == en { 94 panic("inserting into itself") 95 } 96 if childe.Parent != nil { 97 panic("element already has a parent") 98 } 99 100 // insert in list and get element 101 var elem *list.Element 102 if next == nil { 103 elem = en.childs.PushBack(childe) 104 } else { 105 // ensure next element is a child of this node 106 if next.Parent != en { 107 panic("next is not a child of this node") 108 } 109 110 elem = en.childs.InsertBefore(childe, next.elem) 111 } 112 if elem == nil { 113 panic("element not inserted") 114 } 115 116 childe.elem = elem 117 childe.Parent = en 118 childe.Wrapper = child // auto set the wrapper 119 120 en.MarkNeedsLayoutAndPaint() 121 122 childe.themeChangeCallback() 123 } 124 125 //---------- 126 127 func (en *EmbedNode) Remove(child Node) { 128 childe := child.Embed() 129 if childe.Parent != en { 130 panic("not a child of this node") 131 } 132 en.childs.Remove(childe.elem) 133 childe.elem = nil 134 childe.Parent = nil 135 136 en.MarkNeedsLayoutAndPaint() 137 } 138 139 //---------- 140 141 // Doesn't use Remove/Insert. So implementing nodes overriding those will not see their functions used. 142 func (en *EmbedNode) Swap(u Node) { 143 eu := u.Embed() 144 if en.Parent != eu.Parent { 145 panic("nodes don't have the same parent") 146 } 147 l := &en.Parent.childs 148 e1 := en.elem 149 e2 := eu.elem 150 if e1.Next() == e2 { 151 l.MoveAfter(e1, e2) 152 } else if e2.Next() == e1 { 153 l.MoveAfter(e2, e1) 154 } else { 155 prev := e1.Prev() 156 l.MoveAfter(e1, e2) 157 if prev == nil { 158 l.MoveToFront(e2) 159 } else { 160 l.MoveAfter(e2, prev) 161 } 162 } 163 } 164 165 //---------- 166 167 func (en *EmbedNode) ChildsLen() int { 168 return en.childs.Len() 169 } 170 171 //---------- 172 173 func elemEmbed(e *list.Element) *EmbedNode { 174 if e == nil { 175 return nil 176 } 177 return e.Value.(*EmbedNode) 178 } 179 func elemWrapper(e *list.Element) Node { 180 if e == nil { 181 return nil 182 } 183 return e.Value.(*EmbedNode).Wrapper 184 } 185 186 //---------- 187 188 func (en *EmbedNode) FirstChild() *EmbedNode { 189 return elemEmbed(en.childs.Front()) 190 } 191 func (en *EmbedNode) LastChild() *EmbedNode { 192 return elemEmbed(en.childs.Back()) 193 } 194 func (en *EmbedNode) NextSibling() *EmbedNode { 195 return elemEmbed(en.elem.Next()) 196 } 197 func (en *EmbedNode) PrevSibling() *EmbedNode { 198 return elemEmbed(en.elem.Prev()) 199 } 200 201 //---------- 202 203 func (en *EmbedNode) FirstChildWrapper() Node { 204 return elemWrapper(en.childs.Front()) 205 } 206 func (en *EmbedNode) LastChildWrapper() Node { 207 return elemWrapper(en.childs.Back()) 208 } 209 func (en *EmbedNode) NextSiblingWrapper() Node { 210 return elemWrapper(en.elem.Next()) 211 } 212 func (en *EmbedNode) PrevSiblingWrapper() Node { 213 return elemWrapper(en.elem.Prev()) 214 } 215 216 //---------- 217 218 func (en *EmbedNode) Iterate(f func(*EmbedNode) bool) { 219 for e := en.childs.Front(); e != nil; e = e.Next() { 220 if !f(elemEmbed(e)) { 221 break 222 } 223 } 224 } 225 func (en *EmbedNode) IterateReverse(f func(*EmbedNode) bool) { 226 for e := en.childs.Back(); e != nil; e = e.Prev() { 227 if !f(elemEmbed(e)) { 228 break 229 } 230 } 231 } 232 func (en *EmbedNode) IterateWrappers(f func(Node) bool) { 233 for e := en.childs.Front(); e != nil; e = e.Next() { 234 if !f(elemWrapper(e)) { 235 break 236 } 237 } 238 } 239 func (en *EmbedNode) IterateWrappersReverse(f func(Node) bool) { 240 for e := en.childs.Back(); e != nil; e = e.Prev() { 241 if !f(elemWrapper(e)) { 242 break 243 } 244 } 245 } 246 247 //---------- 248 249 // Iterate2 family functions: iterate all without break possibility. 250 251 func (en *EmbedNode) Iterate2(f func(*EmbedNode)) { 252 for e := en.childs.Front(); e != nil; e = e.Next() { 253 f(elemEmbed(e)) 254 } 255 } 256 func (en *EmbedNode) IterateReverse2(f func(*EmbedNode)) { 257 for e := en.childs.Back(); e != nil; e = e.Prev() { 258 f(elemEmbed(e)) 259 } 260 } 261 func (en *EmbedNode) IterateWrappers2(f func(Node)) { 262 for e := en.childs.Front(); e != nil; e = e.Next() { 263 f(elemWrapper(e)) 264 } 265 } 266 func (en *EmbedNode) IterateWrappersReverse2(f func(Node)) { 267 for e := en.childs.Back(); e != nil; e = e.Prev() { 268 f(elemWrapper(e)) 269 } 270 } 271 272 //---------- 273 274 func (en *EmbedNode) ChildsWrappers() []Node { 275 w := []Node{} 276 en.IterateWrappers2(func(c Node) { 277 w = append(w, c) 278 }) 279 return w 280 } 281 282 //---------- 283 284 func (en *EmbedNode) HasAnyMarks(m Marks) bool { 285 return en.marks.HasAny(m) 286 } 287 288 func (en *EmbedNode) AddMarks(m Marks) { 289 en.markUp(m, nil, 0) 290 } 291 292 func (en *EmbedNode) RemoveMarks(m Marks) { 293 // direcly non-removable marks 294 u := MarkNeedsPaint | MarkNeedsLayout | 295 MarkChildNeedsPaint | MarkChildNeedsLayout 296 if m.HasAny(u) { 297 panic(fmt.Sprintf("mark not directly removable: %v", u)) 298 } 299 en.marks.Remove(m) 300 } 301 302 //---------- 303 304 func (en *EmbedNode) markUp(m Marks, child Node, childChangedMarks Marks) { 305 old := en.marks 306 en.marks |= m 307 changed := en.marks ^ old 308 309 // this node is a parent, run callback as soon as it gets marked (now) 310 if en.Wrapper != nil && child != nil && childChangedMarks != 0 { 311 en.Wrapper.OnChildMarked(child, childChangedMarks) 312 } 313 314 if en.Parent != nil && changed != 0 { 315 // setup marks to add to parent 316 var u Marks 317 if changed.HasAny(MarkNeedsPaint | MarkChildNeedsPaint) { 318 u.Add(MarkChildNeedsPaint) 319 } 320 if changed.HasAny(MarkNeedsLayout | MarkChildNeedsLayout) { 321 u.Add(MarkChildNeedsLayout) 322 } 323 324 // mark parent 325 en.Parent.markUp(u, en.Wrapper, changed) 326 } 327 } 328 329 func (en *EmbedNode) OnChildMarked(child Node, newMarks Marks) { 330 } 331 332 //---------- 333 334 func (en *EmbedNode) MarkNeedsLayout() { 335 en.AddMarks(MarkNeedsLayout) 336 } 337 func (en *EmbedNode) MarkNeedsPaint() { 338 en.AddMarks(MarkNeedsPaint) 339 } 340 func (en *EmbedNode) MarkNeedsLayoutAndPaint() { 341 en.AddMarks(MarkNeedsLayout | MarkNeedsPaint) 342 } 343 344 //---------- 345 346 func (en *EmbedNode) TreeNeedsPaint() bool { 347 return en.HasAnyMarks(MarkNeedsPaint | MarkChildNeedsPaint) 348 } 349 350 func (en *EmbedNode) TreeNeedsLayout() bool { 351 return en.HasAnyMarks(MarkNeedsLayout | MarkChildNeedsLayout) 352 } 353 354 //---------- 355 356 func (en *EmbedNode) Measure(hint image.Point) image.Point { 357 var max image.Point 358 en.IterateWrappers2(func(c Node) { 359 m := c.Measure(hint) 360 max = imageutil.MaxPoint(max, m) 361 }) 362 return max 363 } 364 365 //---------- 366 367 func (en *EmbedNode) LayoutMarked() { 368 if en.HasAnyMarks(MarkNeedsLayout) { 369 en.Wrapper.LayoutTree() 370 } else if en.HasAnyMarks(MarkChildNeedsLayout) { 371 en.marks.Remove(MarkChildNeedsLayout) 372 en.IterateWrappers2(func(c Node) { 373 c.LayoutMarked() 374 }) 375 } 376 } 377 378 //var depth int 379 380 func (en *EmbedNode) LayoutTree() { 381 //fmt.Printf("%*s layouttree %T %v\n", depth*4, "", en.Wrapper, en.Bounds) 382 //depth++ 383 //defer func() { depth-- }() 384 385 en.marks.Remove(MarkNeedsLayout | MarkChildNeedsLayout) 386 387 // keep/set default bounds before layouting childs 388 cbm := map[*EmbedNode]image.Rectangle{} 389 en.Iterate2(func(c *EmbedNode) { 390 cbm[c] = c.Bounds 391 c.Bounds = en.Bounds // parent bounds 392 393 // set to empty if not visible 394 if c.HasAnyMarks(MarkForceZeroBounds) { 395 c.Bounds = image.Rectangle{} 396 } 397 }) 398 399 en.Wrapper.Layout() 400 en.Wrapper.ChildsLayoutTree() 401 402 // auto detect if it needs paint if bounds change 403 en.Iterate2(func(c *EmbedNode) { 404 if cb, ok := cbm[c]; ok && c.Bounds != cb { 405 c.MarkNeedsPaint() 406 } 407 }) 408 } 409 410 func (en *EmbedNode) Layout() { 411 } 412 413 func (en *EmbedNode) ChildsLayoutTree() { 414 en.IterateWrappers2(func(c Node) { 415 c.LayoutTree() 416 }) 417 } 418 419 //---------- 420 421 func (en *EmbedNode) PaintMarked() image.Rectangle { 422 u := image.Rectangle{} 423 if en.HasAnyMarks(MarkNeedsPaint) { 424 if en.Wrapper.PaintTree() { 425 u = u.Union(en.Bounds) 426 } 427 } else if en.HasAnyMarks(MarkChildNeedsPaint) { 428 en.marks.Remove(MarkChildNeedsPaint) 429 en.IterateWrappers2(func(c Node) { 430 r := c.PaintMarked() 431 u = u.Union(r) 432 }) 433 } 434 return u 435 } 436 437 func (en *EmbedNode) PaintTree() bool { 438 en.marks.Remove(MarkNeedsPaint | MarkChildNeedsPaint) 439 440 if en.HasAnyMarks(MarkNotPaintable | MarkForceZeroBounds) { 441 return false 442 } 443 444 en.Wrapper.PaintBase() 445 en.Wrapper.Paint() 446 en.Wrapper.ChildsPaintTree() 447 return true 448 } 449 450 func (en *EmbedNode) PaintBase() { 451 } 452 453 func (en *EmbedNode) Paint() { 454 } 455 456 func (en *EmbedNode) ChildsPaintTree() { 457 en.IterateWrappers2(func(c Node) { 458 c.PaintTree() 459 }) 460 } 461 462 //---------- 463 464 func (en *EmbedNode) OnInputEvent(ev interface{}, p image.Point) event.Handled { 465 return false 466 } 467 468 //---------- 469 470 func (en *EmbedNode) SetTheme(t Theme) { 471 defer en.themeChangeCallback() 472 defer en.MarkNeedsPaint() // possible palette change/update 473 defer en.MarkNeedsLayout() // possible font change 474 475 en.theme = t 476 } 477 478 func (en *EmbedNode) Theme() *Theme { 479 return &en.theme 480 } 481 482 //---------- 483 484 func (en *EmbedNode) ThemePalette() Palette { 485 return en.theme.Palette 486 } 487 488 func (en *EmbedNode) SetThemePalette(p Palette) { 489 defer en.themeChangeCallback() 490 defer en.MarkNeedsPaint() 491 492 en.theme.SetPalette(p) 493 } 494 495 func (en *EmbedNode) SetThemePaletteColor(name string, c color.Color) { 496 defer en.themeChangeCallback() 497 defer en.MarkNeedsPaint() 498 499 en.theme.SetPaletteColor(name, c) 500 } 501 502 func (en *EmbedNode) SetThemePaletteNamePrefix(prefix string) { 503 defer en.themeChangeCallback() 504 defer en.MarkNeedsPaint() 505 506 en.theme.SetPaletteNamePrefix(prefix) 507 } 508 509 //---------- 510 511 func (en *EmbedNode) TreeThemePaletteColor(name string) color.Color { 512 if c, ok := en.treeThemePaletteColor2(name); ok { 513 return c 514 } 515 // last resort: a color that is not white/black to help debug 516 return cint(0xff0000) 517 } 518 519 func (en *EmbedNode) treeThemePaletteColor2(name string) (color.Color, bool) { 520 if !strings.HasPrefix(name, en.theme.PaletteNamePrefix) { 521 s := en.theme.PaletteNamePrefix + name 522 if c, ok := en.treeThemePaletteColor2(s); ok { 523 return c, true 524 } 525 } 526 if c, ok := en.theme.Palette[name]; ok { 527 return c, true 528 } 529 if en.Parent != nil { 530 if c, ok := en.Parent.treeThemePaletteColor2(name); ok { 531 return c, true 532 } 533 } 534 // at root tree (parent is nil) and not found, try default palette 535 if c, ok := DefaultPalette[name]; ok { 536 return c, true 537 } 538 return nil, false 539 } 540 541 //---------- 542 543 func (en *EmbedNode) SetThemeFontFace(ff *fontutil.FontFace) { 544 defer en.themeChangeCallback() 545 defer en.MarkNeedsLayout() 546 547 en.theme.SetFontFace(ff) 548 } 549 550 func (en *EmbedNode) TreeThemeFontFace() *fontutil.FontFace { 551 for n := en; n != nil; n = n.Parent { 552 if n.theme.FontFace != nil { 553 return n.theme.FontFace 554 } 555 } 556 return fontutil.DefaultFontFace() 557 } 558 559 //---------- 560 561 func (en *EmbedNode) themeChangeCallback() { 562 if en.Wrapper != nil { 563 en.Wrapper.OnThemeChange() 564 } 565 en.Iterate2(func(c *EmbedNode) { 566 c.themeChangeCallback() 567 }) 568 } 569 570 func (en *EmbedNode) OnThemeChange() { 571 } 572 573 //---------- 574 575 type Marks uint16 576 577 func (m *Marks) Add(u Marks) { *m |= u } 578 func (m *Marks) Remove(u Marks) { *m &^= u } 579 func (m Marks) Mask(u Marks) Marks { return m & u } 580 func (m Marks) HasAny(u Marks) bool { return m.Mask(u) > 0 } 581 582 //func (m *Marks) Modify(u Marks, v bool) { 583 // if v { 584 // m.Add(u) 585 // } else { 586 // m.Remove(u) 587 // } 588 //} 589 //func (m Marks) Changes(u Marks) Marks { 590 // old := m 591 // m |= u 592 // return m ^ old 593 //} 594 595 //---------- 596 597 const ( 598 MarkNeedsPaint Marks = 1 << iota 599 MarkNeedsLayout 600 601 MarkChildNeedsPaint 602 MarkChildNeedsLayout 603 604 MarkPointerInside // mouseEnter/mouseLeave events 605 MarkNotDraggable // won't emit mouseDrag events 606 607 MarkForceZeroBounds // sets bounds to zero (aka not visible) 608 609 MarkInBoundsHandlesEvent // helps with layer nodes keep events 610 611 // For transparent widgets that cross two or more other widgets (ex: a non visible separator handle). Improves on detecting if others need paint. 612 MarkNotPaintable 613 )