boxlayout.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package layout
  2. import (
  3. "fyne.io/fyne/v2"
  4. "fyne.io/fyne/v2/theme"
  5. )
  6. // Declare conformity with Layout interface
  7. var _ fyne.Layout = (*boxLayout)(nil)
  8. type boxLayout struct {
  9. horizontal bool
  10. }
  11. // NewHBoxLayout returns a horizontal box layout for stacking a number of child
  12. // canvas objects or widgets left to right. The objects are always displayed
  13. // at their horizontal MinSize. Use a different layout if the objects are intended
  14. // to be larger then their horizontal MinSize.
  15. func NewHBoxLayout() fyne.Layout {
  16. return &boxLayout{true}
  17. }
  18. // NewVBoxLayout returns a vertical box layout for stacking a number of child
  19. // canvas objects or widgets top to bottom. The objects are always displayed
  20. // at their vertical MinSize. Use a different layout if the objects are intended
  21. // to be larger then their vertical MinSize.
  22. func NewVBoxLayout() fyne.Layout {
  23. return &boxLayout{false}
  24. }
  25. func (g *boxLayout) isSpacer(obj fyne.CanvasObject) bool {
  26. if !obj.Visible() {
  27. return false // invisible spacers don't impact layout
  28. }
  29. spacer, ok := obj.(SpacerObject)
  30. if !ok {
  31. return false
  32. }
  33. if g.horizontal {
  34. return spacer.ExpandHorizontal()
  35. }
  36. return spacer.ExpandVertical()
  37. }
  38. // Layout is called to pack all child objects into a specified size.
  39. // For a VBoxLayout this will pack objects into a single column where each item
  40. // is full width but the height is the minimum required.
  41. // Any spacers added will pad the view, sharing the space if there are two or more.
  42. func (g *boxLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
  43. spacers := 0
  44. visibleObjects := 0
  45. // Size taken up by visible objects
  46. total := float32(0)
  47. for _, child := range objects {
  48. if !child.Visible() {
  49. continue
  50. }
  51. if g.isSpacer(child) {
  52. spacers++
  53. continue
  54. }
  55. visibleObjects++
  56. if g.horizontal {
  57. total += child.MinSize().Width
  58. } else {
  59. total += child.MinSize().Height
  60. }
  61. }
  62. padding := theme.Padding()
  63. // Amount of space not taken up by visible objects and inter-object padding
  64. var extra float32
  65. if g.horizontal {
  66. extra = size.Width - total - (padding * float32(visibleObjects-1))
  67. } else {
  68. extra = size.Height - total - (padding * float32(visibleObjects-1))
  69. }
  70. // Spacers split extra space equally
  71. spacerSize := float32(0)
  72. if spacers > 0 {
  73. spacerSize = extra / float32(spacers)
  74. }
  75. x, y := float32(0), float32(0)
  76. for _, child := range objects {
  77. if !child.Visible() {
  78. continue
  79. }
  80. if g.isSpacer(child) {
  81. if g.horizontal {
  82. x += spacerSize
  83. } else {
  84. y += spacerSize
  85. }
  86. continue
  87. }
  88. child.Move(fyne.NewPos(x, y))
  89. if g.horizontal {
  90. width := child.MinSize().Width
  91. x += padding + width
  92. child.Resize(fyne.NewSize(width, size.Height))
  93. } else {
  94. height := child.MinSize().Height
  95. y += padding + height
  96. child.Resize(fyne.NewSize(size.Width, height))
  97. }
  98. }
  99. }
  100. // MinSize finds the smallest size that satisfies all the child objects.
  101. // For a BoxLayout this is the width of the widest item and the height is
  102. // the sum of of all children combined with padding between each.
  103. func (g *boxLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
  104. minSize := fyne.NewSize(0, 0)
  105. addPadding := false
  106. padding := theme.Padding()
  107. for _, child := range objects {
  108. if !child.Visible() || g.isSpacer(child) {
  109. continue
  110. }
  111. childMin := child.MinSize()
  112. if g.horizontal {
  113. minSize.Height = fyne.Max(childMin.Height, minSize.Height)
  114. minSize.Width += childMin.Width
  115. if addPadding {
  116. minSize.Width += padding
  117. }
  118. } else {
  119. minSize.Width = fyne.Max(childMin.Width, minSize.Width)
  120. minSize.Height += childMin.Height
  121. if addPadding {
  122. minSize.Height += padding
  123. }
  124. }
  125. addPadding = true
  126. }
  127. return minSize
  128. }