github.com/utopiagio/gio@v0.0.8/layout/stack.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package layout 4 5 import ( 6 "image" 7 8 "github.com/utopiagio/gio/op" 9 ) 10 11 // Stack lays out child elements on top of each other, 12 // according to an alignment direction. 13 type Stack struct { 14 // Alignment is the direction to align children 15 // smaller than the available space. 16 Alignment Direction 17 } 18 19 // StackChild represents a child for a Stack layout. 20 type StackChild struct { 21 expanded bool 22 widget Widget 23 24 // Scratch space. 25 call op.CallOp 26 dims Dimensions 27 } 28 29 // Stacked returns a Stack child that is laid out with no minimum 30 // constraints and the maximum constraints passed to Stack.Layout. 31 func Stacked(w Widget) StackChild { 32 return StackChild{ 33 widget: w, 34 } 35 } 36 37 // Expanded returns a Stack child with the minimum constraints set 38 // to the largest Stacked child. The maximum constraints are set to 39 // the same as passed to Stack.Layout. 40 func Expanded(w Widget) StackChild { 41 return StackChild{ 42 expanded: true, 43 widget: w, 44 } 45 } 46 47 // Layout a stack of children. The position of the children are 48 // determined by the specified order, but Stacked children are laid out 49 // before Expanded children. 50 func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { 51 var maxSZ image.Point 52 // First lay out Stacked children. 53 cgtx := gtx 54 cgtx.Constraints.Min = image.Point{} 55 for i, w := range children { 56 if w.expanded { 57 continue 58 } 59 macro := op.Record(gtx.Ops) 60 dims := w.widget(cgtx) 61 call := macro.Stop() 62 if w := dims.Size.X; w > maxSZ.X { 63 maxSZ.X = w 64 } 65 if h := dims.Size.Y; h > maxSZ.Y { 66 maxSZ.Y = h 67 } 68 children[i].call = call 69 children[i].dims = dims 70 } 71 // Then lay out Expanded children. 72 for i, w := range children { 73 if !w.expanded { 74 continue 75 } 76 macro := op.Record(gtx.Ops) 77 cgtx.Constraints.Min = maxSZ 78 dims := w.widget(cgtx) 79 call := macro.Stop() 80 if w := dims.Size.X; w > maxSZ.X { 81 maxSZ.X = w 82 } 83 if h := dims.Size.Y; h > maxSZ.Y { 84 maxSZ.Y = h 85 } 86 children[i].call = call 87 children[i].dims = dims 88 } 89 90 maxSZ = gtx.Constraints.Constrain(maxSZ) 91 var baseline int 92 for _, ch := range children { 93 sz := ch.dims.Size 94 var p image.Point 95 switch s.Alignment { 96 case N, S, Center: 97 p.X = (maxSZ.X - sz.X) / 2 98 case NE, SE, E: 99 p.X = maxSZ.X - sz.X 100 } 101 switch s.Alignment { 102 case W, Center, E: 103 p.Y = (maxSZ.Y - sz.Y) / 2 104 case SW, S, SE: 105 p.Y = maxSZ.Y - sz.Y 106 } 107 trans := op.Offset(p).Push(gtx.Ops) 108 ch.call.Add(gtx.Ops) 109 trans.Pop() 110 if baseline == 0 { 111 if b := ch.dims.Baseline; b != 0 { 112 baseline = b + maxSZ.Y - sz.Y - p.Y 113 } 114 } 115 } 116 return Dimensions{ 117 Size: maxSZ, 118 Baseline: baseline, 119 } 120 } 121 122 // Background lays out single child widget on top of a background, 123 // centering, if necessary. 124 type Background struct{} 125 126 // Layout a widget and then add a background to it. 127 func (Background) Layout(gtx Context, background, widget Widget) Dimensions { 128 macro := op.Record(gtx.Ops) 129 wdims := widget(gtx) 130 baseline := wdims.Baseline 131 call := macro.Stop() 132 133 cgtx := gtx 134 cgtx.Constraints.Min = gtx.Constraints.Constrain(wdims.Size) 135 bdims := background(cgtx) 136 137 if bdims.Size != wdims.Size { 138 p := image.Point{ 139 X: (bdims.Size.X - wdims.Size.X) / 2, 140 Y: (bdims.Size.Y - wdims.Size.Y) / 2, 141 } 142 baseline += (bdims.Size.Y - wdims.Size.Y) / 2 143 trans := op.Offset(p).Push(gtx.Ops) 144 defer trans.Pop() 145 } 146 147 call.Add(gtx.Ops) 148 149 return Dimensions{ 150 Size: bdims.Size, 151 Baseline: baseline, 152 } 153 }