boxlayout.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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 isVerticalSpacer(obj fyne.CanvasObject) bool {
  26. if spacer, ok := obj.(SpacerObject); ok {
  27. return spacer.ExpandVertical()
  28. }
  29. return false
  30. }
  31. func isHorizontalSpacer(obj fyne.CanvasObject) bool {
  32. if spacer, ok := obj.(SpacerObject); ok {
  33. return spacer.ExpandHorizontal()
  34. }
  35. return false
  36. }
  37. func (g *boxLayout) isSpacer(obj fyne.CanvasObject) bool {
  38. // invisible spacers don't impact layout
  39. if !obj.Visible() {
  40. return false
  41. }
  42. if g.horizontal {
  43. return isHorizontalSpacer(obj)
  44. }
  45. return isVerticalSpacer(obj)
  46. }
  47. // Layout is called to pack all child objects into a specified size.
  48. // For a VBoxLayout this will pack objects into a single column where each item
  49. // is full width but the height is the minimum required.
  50. // Any spacers added will pad the view, sharing the space if there are two or more.
  51. func (g *boxLayout) Layout(objects []fyne.CanvasObject, size fyne.Size) {
  52. spacers := 0
  53. total := float32(0)
  54. for _, child := range objects {
  55. if !child.Visible() {
  56. continue
  57. }
  58. if g.isSpacer(child) {
  59. spacers++
  60. continue
  61. }
  62. if g.horizontal {
  63. total += child.MinSize().Width
  64. } else {
  65. total += child.MinSize().Height
  66. }
  67. }
  68. x, y := float32(0), float32(0)
  69. var extra float32
  70. if g.horizontal {
  71. extra = size.Width - total - (theme.Padding() * float32(len(objects)-spacers-1))
  72. } else {
  73. extra = size.Height - total - (theme.Padding() * float32(len(objects)-spacers-1))
  74. }
  75. extraCell := float32(0)
  76. if spacers > 0 {
  77. extraCell = extra / float32(spacers)
  78. }
  79. for _, child := range objects {
  80. if !child.Visible() {
  81. continue
  82. }
  83. width := child.MinSize().Width
  84. height := child.MinSize().Height
  85. if g.isSpacer(child) {
  86. if g.horizontal {
  87. x += extraCell
  88. } else {
  89. y += extraCell
  90. }
  91. continue
  92. }
  93. child.Move(fyne.NewPos(x, y))
  94. if g.horizontal {
  95. x += theme.Padding() + width
  96. child.Resize(fyne.NewSize(width, size.Height))
  97. } else {
  98. y += theme.Padding() + height
  99. child.Resize(fyne.NewSize(size.Width, height))
  100. }
  101. }
  102. }
  103. // MinSize finds the smallest size that satisfies all the child objects.
  104. // For a BoxLayout this is the width of the widest item and the height is
  105. // the sum of of all children combined with padding between each.
  106. func (g *boxLayout) MinSize(objects []fyne.CanvasObject) fyne.Size {
  107. minSize := fyne.NewSize(0, 0)
  108. addPadding := false
  109. for _, child := range objects {
  110. if !child.Visible() {
  111. continue
  112. }
  113. if g.isSpacer(child) {
  114. continue
  115. }
  116. if g.horizontal {
  117. minSize.Height = fyne.Max(child.MinSize().Height, minSize.Height)
  118. minSize.Width += child.MinSize().Width
  119. if addPadding {
  120. minSize.Width += theme.Padding()
  121. }
  122. } else {
  123. minSize.Width = fyne.Max(child.MinSize().Width, minSize.Width)
  124. minSize.Height += child.MinSize().Height
  125. if addPadding {
  126. minSize.Height += theme.Padding()
  127. }
  128. }
  129. addPadding = true
  130. }
  131. return minSize
  132. }