github.com/jmigpin/editor@v1.6.0/util/uiutil/widget/boxlayout.go (about)

     1  package widget
     2  
     3  import (
     4  	"image"
     5  )
     6  
     7  type BoxLayout struct {
     8  	ENode
     9  	YAxis bool
    10  
    11  	flex map[Node]XYAxisBoolPair // used in measure+layout, has priority over fill
    12  	fill map[Node]XYAxisBoolPair // used only in layout
    13  }
    14  
    15  func NewBoxLayout() *BoxLayout {
    16  	bl := &BoxLayout{
    17  		flex: make(map[Node]XYAxisBoolPair),
    18  		fill: make(map[Node]XYAxisBoolPair),
    19  	}
    20  	return bl
    21  }
    22  
    23  func (bl *BoxLayout) Measure(hint image.Point) image.Point {
    24  	bounds := bl.childsBounds(hint, true)
    25  	xya := &XYAxis{bl.YAxis}
    26  	var max image.Point
    27  	for _, b := range bounds {
    28  		s := b.Size()
    29  		size := xya.Point(&s)
    30  		max.X += size.X
    31  		if size.Y > max.Y {
    32  			max.Y = size.Y
    33  		}
    34  	}
    35  	return xya.Point(&max)
    36  }
    37  
    38  func (bl *BoxLayout) Layout() {
    39  	bounds := bl.childsBounds(bl.Bounds.Size(), false)
    40  
    41  	// set bounds
    42  	bl.IterateWrappers2(func(child Node) {
    43  		b := bounds[child]
    44  		r := b.Add(bl.Bounds.Min)
    45  		r3 := r.Intersect(bl.Bounds)
    46  		child.Embed().Bounds = r3
    47  	})
    48  }
    49  
    50  func (bl *BoxLayout) childsBounds(max image.Point, measure bool) map[Node]image.Rectangle {
    51  	xya := &XYAxis{bl.YAxis}
    52  	max2 := xya.Point(&max)
    53  	sizes := make(map[Node]image.Point, bl.ChildsLen())
    54  
    55  	// count flex/fill
    56  	nFlexX := 0
    57  	nFillX := 0
    58  	var lastFlexXNode, lastFillXNode Node
    59  	bl.IterateWrappers2(func(child Node) {
    60  		bp := xya.BoolPair(bl.flex[child])
    61  		if bp.X {
    62  			nFlexX++
    63  			lastFlexXNode = child
    64  		}
    65  		if !measure {
    66  			bp := xya.BoolPair(bl.fill[child])
    67  			if bp.X {
    68  				nFillX++
    69  				lastFillXNode = child
    70  			}
    71  		}
    72  	})
    73  
    74  	// x fills are only considered if there are no x flexes (priority for flex)
    75  	flexingX := nFlexX > 0
    76  	nX := nFlexX
    77  	fillingX := false
    78  	if !flexingX && !measure && nFillX > 0 {
    79  		fillingX = true
    80  		nX = nFillX
    81  	}
    82  
    83  	// measure non-flexible childs first to get remaining space
    84  	available := max2
    85  	bl.IterateWrappers2(func(child Node) {
    86  		bp := xya.BoolPair(bl.flex[child])
    87  		bp2 := xya.BoolPair(bl.fill[child])
    88  		if (!flexingX && !fillingX) || (flexingX && !bp.X) || (fillingX && !bp2.X) {
    89  			// flex: -X-Y
    90  			m0 := child.Measure(xya.Point(&available))
    91  			m := xya.Point(&m0)
    92  
    93  			// flex: -X+Y
    94  			if bp.Y || (!measure && bp2.Y) {
    95  				m.Y = available.Y
    96  			}
    97  
    98  			sizes[child] = m
    99  			available.X -= m.X
   100  			if available.X < 0 {
   101  				available.X = 0
   102  			}
   103  		}
   104  	})
   105  
   106  	// x flex childs
   107  	{
   108  		// divide remaining space among the flexible childs
   109  		share := available
   110  		if nX > 0 {
   111  			share.X = available.X / nX
   112  		}
   113  
   114  		// measure flexible childs
   115  		bl.IterateWrappers2(func(child Node) {
   116  			bp := xya.BoolPair(bl.flex[child])
   117  			bp2 := xya.BoolPair(bl.fill[child])
   118  			if (flexingX && bp.X) || (fillingX && bp2.X) {
   119  				var m image.Point
   120  
   121  				// flex: +X+Y
   122  				if bp.Y || (!measure && bp2.Y) {
   123  					m = share
   124  				} else {
   125  					// flex: +X-Y
   126  					m0 := child.Measure(xya.Point(&share))
   127  					m = xya.Point(&m0)
   128  					m.X = share.X
   129  				}
   130  
   131  				// correct rounding errors on last node
   132  				if child == lastFlexXNode || child == lastFillXNode {
   133  					m.X = available.X - (share.X * (nX - 1))
   134  				}
   135  
   136  				sizes[child] = m
   137  			}
   138  		})
   139  	}
   140  
   141  	// setup bounds
   142  	bounds := make(map[Node]image.Rectangle, bl.ChildsLen())
   143  	x := 0
   144  	bl.IterateWrappers2(func(child Node) {
   145  		size := sizes[child]
   146  		r := image.Rect(x, 0, x+size.X, size.Y)
   147  		bounds[child] = xya.Rectangle(&r)
   148  		x += size.X
   149  	})
   150  	return bounds
   151  }
   152  
   153  func (bl *BoxLayout) SetChildFlex(node Node, x, y bool) {
   154  	bl.flex[node] = XYAxisBoolPair{x, y}
   155  }
   156  
   157  func (bl *BoxLayout) SetChildFill(node Node, x, y bool) {
   158  	bl.fill[node] = XYAxisBoolPair{x, y}
   159  }