vboxlayout.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. type VBoxLayout struct {
  6. pan IPanel
  7. spacing float32 // vertical spacing between the children in pixels.
  8. alignV Align // vertical alignment of the whole block of children
  9. }
  10. // Parameters for individual children
  11. type VBoxLayoutParams struct {
  12. Expand float32 // item expand vertically factor (0 - no expand)
  13. AlignH Align // item horizontal alignment
  14. }
  15. // NewVBoxLayout creates and returns a pointer to a new horizontal box layout
  16. func NewVBoxLayout() *VBoxLayout {
  17. bl := new(VBoxLayout)
  18. bl.spacing = 0
  19. bl.alignV = AlignTop
  20. return bl
  21. }
  22. // SetSpacing sets the horizontal spacing between the items in pixels
  23. // and updates the layout if possible
  24. func (bl *VBoxLayout) SetSpacing(spacing float32) {
  25. bl.spacing = spacing
  26. bl.Recalc(bl.pan)
  27. }
  28. // SetAlignH sets the horizontal alignment of the whole group of items
  29. // inside the parent panel and updates the layout if possible.
  30. // This only has any effect if there are no expanded items.
  31. func (bl *VBoxLayout) SetAlignV(align Align) {
  32. bl.alignV = align
  33. bl.Recalc(bl.pan)
  34. }
  35. // Recalc recalculates and sets the position and sizes of all children
  36. func (bl *VBoxLayout) Recalc(ipan IPanel) {
  37. // Saves the received panel
  38. bl.pan = ipan
  39. if bl.pan == nil {
  40. return
  41. }
  42. parent := ipan.GetPanel()
  43. if len(parent.Children()) == 0 {
  44. return
  45. }
  46. // Calculates the total height, expanded height, fixed height and
  47. // the sum of the expand factor for all items.
  48. var theight float32 = 0
  49. var eheight float32 = 0
  50. var fheight float32 = 0
  51. var texpand float32 = 0
  52. ecount := 0
  53. paramsDef := VBoxLayoutParams{Expand: 0, AlignH: AlignLeft}
  54. for pos, obj := range parent.Children() {
  55. pan := obj.(IPanel).GetPanel()
  56. // Get item layout parameters or use default
  57. params := paramsDef
  58. if pan.layoutParams != nil {
  59. params = *pan.layoutParams.(*VBoxLayoutParams)
  60. }
  61. // Calculate total height
  62. theight += pan.Height()
  63. if pos > 0 {
  64. theight += bl.spacing
  65. }
  66. // Calculate height of expanded items
  67. if params.Expand > 0 {
  68. texpand += params.Expand
  69. eheight += pan.Height()
  70. if pos > 0 {
  71. eheight += bl.spacing
  72. }
  73. ecount++
  74. // Calculate width of fixed items
  75. } else {
  76. fheight += pan.Height()
  77. if pos > 0 {
  78. fheight += bl.spacing
  79. }
  80. }
  81. }
  82. // If there is at least on expanded item, all free space will be occupied
  83. spaceMiddle := bl.spacing
  84. var posY float32 = 0
  85. if texpand > 0 {
  86. // If there is free space, distribute space between expanded items
  87. totalSpace := parent.ContentHeight() - theight
  88. if totalSpace > 0 {
  89. for _, obj := range parent.Children() {
  90. pan := obj.(IPanel).GetPanel()
  91. // Get item layout parameters or use default
  92. params := paramsDef
  93. if pan.layoutParams != nil {
  94. params = *pan.layoutParams.(*VBoxLayoutParams)
  95. }
  96. if params.Expand > 0 {
  97. iheight := totalSpace * params.Expand / texpand
  98. pan.SetHeight(pan.Height() + iheight)
  99. }
  100. }
  101. // No free space: distribute expanded items heights
  102. } else {
  103. for _, obj := range parent.Children() {
  104. pan := obj.(IPanel).GetPanel()
  105. // Get item layout parameters or use default
  106. params := paramsDef
  107. if pan.layoutParams != nil {
  108. params = *pan.layoutParams.(*VBoxLayoutParams)
  109. }
  110. if params.Expand > 0 {
  111. spacing := bl.spacing * float32(ecount-1)
  112. iheight := (parent.ContentHeight() - spacing - fheight - bl.spacing) * params.Expand / texpand
  113. pan.SetHeight(iheight)
  114. }
  115. }
  116. }
  117. // No expanded items: checks block vertical alignment
  118. } else {
  119. // Calculates initial y position which depends
  120. // on the current horizontal alignment.
  121. switch bl.alignV {
  122. case AlignTop:
  123. posY = 0
  124. case AlignCenter:
  125. posY = (parent.ContentHeight() - theight) / 2
  126. case AlignBottom:
  127. posY = parent.ContentHeight() - theight
  128. case AlignHeight:
  129. space := parent.ContentHeight() - theight + bl.spacing*float32(len(parent.Children())-1)
  130. if space < 0 {
  131. space = bl.spacing * float32(len(parent.Children())-1)
  132. }
  133. spaceMiddle = space / float32(len(parent.Children())+1)
  134. posY = spaceMiddle
  135. default:
  136. log.Fatal("VBoxLayout: invalid global vertical alignment")
  137. }
  138. }
  139. // Calculates the X position of each item considering
  140. // it horizontal alignment
  141. var posX float32
  142. width := parent.ContentWidth()
  143. for pos, obj := range parent.Children() {
  144. pan := obj.(IPanel).GetPanel()
  145. // Get item layout parameters or use default
  146. params := paramsDef
  147. if pan.layoutParams != nil {
  148. params = *pan.layoutParams.(*VBoxLayoutParams)
  149. }
  150. cwidth := pan.Width()
  151. switch params.AlignH {
  152. case AlignLeft:
  153. posX = 0
  154. case AlignCenter:
  155. posX = (width - cwidth) / 2
  156. case AlignRight:
  157. posX = width - cwidth
  158. case AlignWidth:
  159. posX = 0
  160. pan.SetWidth(width)
  161. default:
  162. log.Fatal("VBoxLayout: invalid item horizontal alignment")
  163. }
  164. // Sets the child position
  165. pan.SetPosition(posX, posY)
  166. // Calculates next position
  167. posY += pan.Height()
  168. if pos < len(parent.Children())-1 {
  169. posY += spaceMiddle
  170. }
  171. }
  172. }