github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/multilayer.go (about) 1 package widget 2 3 import ( 4 "container/list" 5 "image" 6 ) 7 8 // First/last child is the bottom/top layer. 9 type MultiLayer struct { 10 ENode 11 12 BgLayer *BgLayer 13 SeparatorLayer *ENode 14 ContextLayer *FloatLayer 15 MenuLayer *FloatLayer 16 17 rects list.List 18 } 19 20 func NewMultiLayer() *MultiLayer { 21 ml := &MultiLayer{} 22 23 ml.BgLayer = &BgLayer{ml: ml} 24 ml.SeparatorLayer = &ENode{} 25 ml.ContextLayer = &FloatLayer{ml: ml} 26 ml.MenuLayer = &FloatLayer{ml: ml} 27 28 // order matters 29 ml.Append( 30 ml.BgLayer, 31 ml.SeparatorLayer, 32 ml.ContextLayer, 33 ml.MenuLayer, 34 ) 35 36 ml.Iterate2(func(en *EmbedNode) { 37 // allow drag events to fall through to lower layers nodes 38 en.AddMarks(MarkNotDraggable) 39 40 }) 41 42 return ml 43 } 44 45 func (ml *MultiLayer) InsertBefore(col Node, next *EmbedNode) { 46 panic("nodes should be inserted into one of the layers directly") 47 } 48 49 //---------- 50 51 func (ml *MultiLayer) PaintMarked() image.Rectangle { 52 ml.markAddedRects() 53 ml.markFloatLayers() 54 return ml.ENode.PaintMarked() 55 } 56 57 //---------- 58 59 func (ml *MultiLayer) AddMarkRect(r image.Rectangle) { 60 ml.rects.PushBack(&r) 61 } 62 63 func (ml *MultiLayer) markAddedRects() { 64 for elem := ml.rects.Front(); elem != nil; elem = elem.Next() { 65 r := elem.Value.(*image.Rectangle) 66 ml.markRect(nil, *r) 67 } 68 ml.rects = list.List{} 69 } 70 71 //---------- 72 73 func (ml *MultiLayer) markFloatLayers() { 74 ml.IterateWrappers2(func(n Node) { 75 if fl, ok := n.(*FloatLayer); ok { 76 ml.markVisibleNodes(fl) 77 } 78 }) 79 } 80 81 func (ml *MultiLayer) markVisibleNodes(fl *FloatLayer) { 82 vnodes := fl.visibleNodes() 83 for _, n := range vnodes { 84 ne := n.Embed() 85 if ml.rectNeedsPaint(ne.Bounds) { 86 ne.MarkNeedsPaint() 87 ml.markRect(fl, ne.Bounds) 88 } 89 } 90 } 91 92 //---------- 93 94 func (ml *MultiLayer) rectNeedsPaint(r image.Rectangle) bool { 95 found := false 96 ml.IterateWrappers(func(layer Node) bool { 97 found = intersectingNodeNeedingPaintExists(layer, r) 98 return !found // continue if not found 99 }) 100 return found 101 } 102 103 func (ml *MultiLayer) markRect(callLayer Node, r image.Rectangle) { 104 ml.IterateWrappers2(func(layer Node) { 105 if layer != callLayer { // performance 106 markIntersectingNodesNotNeedingPaint(layer, r) 107 } 108 }) 109 } 110 111 //---------- 112 113 type BgLayer struct { 114 ENode 115 ml *MultiLayer 116 } 117 118 //---------- 119 120 type FloatLayer struct { 121 ENode 122 ml *MultiLayer 123 } 124 125 func (fl *FloatLayer) OnChildMarked(child Node, newMarks Marks) { 126 // force float layer to recalc childs bounds to avoid childs having to consult the floatlayer bounds 127 if newMarks.HasAny(MarkNeedsLayout | MarkChildNeedsLayout) { 128 fl.MarkNeedsLayout() 129 } 130 //if newMarks.HasAny(MarkNeedsPaint | MarkChildNeedsPaint) { 131 // log.Printf("float needs paint: %p", fl) 132 // child.Embed().MarkNeedsPaint() 133 //} 134 } 135 136 func (fl *FloatLayer) visibleNodes() []Node { 137 return visibleChildNodes(fl) 138 } 139 140 //---------- 141 142 func visibleChildNodes(node Node) []Node { 143 z := []Node{} 144 node.Embed().IterateWrappers2(func(child Node) { 145 if !child.Embed().HasAnyMarks(MarkForceZeroBounds) { 146 z = append(z, child) 147 } 148 }) 149 return z 150 } 151 152 //---------- 153 154 func intersectingNodeNeedingPaintExists(node Node, r image.Rectangle) bool { 155 found := false 156 node.Embed().IterateWrappers(func(child Node) bool { 157 ce := child.Embed() 158 if ce.Bounds.Overlaps(r) { 159 if ce.HasAnyMarks(MarkNeedsPaint) { 160 found = true 161 } else if ce.HasAnyMarks(MarkChildNeedsPaint) { 162 found = intersectingNodeNeedingPaintExists(child, r) 163 } 164 } 165 return !found // continue while not found 166 }) 167 return found 168 } 169 170 //---------- 171 172 func markIntersectingNodesNotNeedingPaint(node Node, r image.Rectangle) image.Rectangle { 173 u := image.Rectangle{} 174 node.Embed().IterateWrappers2(func(child Node) { 175 ce := child.Embed() 176 if ce.Bounds.Overlaps(r) { 177 if !ce.HasAnyMarks(MarkNeedsPaint) { 178 179 // improve selection with subchilds 180 if r.In(ce.Bounds) { 181 w := markIntersectingNodesNotNeedingPaint(child, r) 182 u = u.Union(w) 183 if r.In(w) { 184 return 185 } 186 } 187 188 u = u.Union(ce.Bounds) 189 ce.MarkNeedsPaint() 190 } 191 } 192 }) 193 return u 194 }