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  }