stack.go 3.3 KB

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