hboxlayout.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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 HBoxLayout struct {
  6. pan IPanel
  7. spacing float32 // horizontal spacing between the children in pixels.
  8. alignH Align // horizontal alignment of the whole block of children
  9. }
  10. // Parameters for individual children
  11. type HBoxLayoutParams struct {
  12. Expand float32 // item expand horizontally factor (0 - no expand)
  13. AlignV Align // item vertical alignment
  14. }
  15. // NewHBoxLayout creates and returns a pointer to a new horizontal box layout
  16. func NewHBoxLayout() *HBoxLayout {
  17. bl := new(HBoxLayout)
  18. bl.spacing = 0
  19. bl.alignH = AlignLeft
  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 *HBoxLayout) 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 *HBoxLayout) SetAlignH(align Align) {
  32. bl.alignH = align
  33. bl.Recalc(bl.pan)
  34. }
  35. // Recalc recalculates and sets the position and sizes of all children
  36. func (bl *HBoxLayout) 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 width, expanded width, fixed width and
  47. // the sum of the expand factor for all items.
  48. var twidth float32 = 0
  49. var ewidth float32 = 0
  50. var fwidth float32 = 0
  51. var texpand float32 = 0
  52. ecount := 0
  53. paramsDef := HBoxLayoutParams{Expand: 0, AlignV: AlignTop}
  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.(*HBoxLayoutParams)
  60. }
  61. // Calculate total width
  62. twidth += pan.Width()
  63. if pos > 0 {
  64. twidth += bl.spacing
  65. }
  66. // Calculate width of expanded items
  67. if params.Expand > 0 {
  68. texpand += params.Expand
  69. ewidth += pan.Width()
  70. if pos > 0 {
  71. ewidth += bl.spacing
  72. }
  73. ecount++
  74. // Calculate width of fixed items
  75. } else {
  76. fwidth += pan.Width()
  77. if pos > 0 {
  78. fwidth += 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 posX float32 = 0
  85. if texpand > 0 {
  86. // If there is free space, distribute space between expanded items
  87. totalSpace := parent.ContentWidth() - twidth
  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.(*HBoxLayoutParams)
  95. }
  96. if params.Expand > 0 {
  97. iwidth := totalSpace * params.Expand / texpand
  98. pan.SetWidth(pan.Width() + iwidth)
  99. }
  100. }
  101. // No free space: distribute expanded items widths
  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.(*HBoxLayoutParams)
  109. }
  110. if params.Expand > 0 {
  111. spacing := bl.spacing * (float32(ecount) - 1)
  112. iwidth := (parent.ContentWidth() - spacing - fwidth - bl.spacing) * params.Expand / texpand
  113. pan.SetWidth(iwidth)
  114. }
  115. }
  116. }
  117. // No expanded items: checks block horizontal alignment
  118. } else {
  119. // Calculates initial x position which depends
  120. // on the current horizontal alignment.
  121. switch bl.alignH {
  122. case AlignLeft:
  123. posX = 0
  124. case AlignCenter:
  125. posX = (parent.ContentWidth() - twidth) / 2
  126. case AlignRight:
  127. posX = parent.ContentWidth() - twidth
  128. case AlignWidth:
  129. space := parent.ContentWidth() - twidth + 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. posX = spaceMiddle
  135. default:
  136. log.Fatal("HBoxLayout: invalid global horizontal alignment")
  137. }
  138. }
  139. // Calculates the Y position of each item considering its vertical alignment
  140. var posY float32
  141. height := parent.ContentHeight()
  142. for pos, obj := range parent.Children() {
  143. pan := obj.(IPanel).GetPanel()
  144. // Get item layout parameters or use default
  145. params := paramsDef
  146. if pan.layoutParams != nil {
  147. params = *pan.layoutParams.(*HBoxLayoutParams)
  148. }
  149. cheight := pan.Height()
  150. switch params.AlignV {
  151. case AlignTop:
  152. posY = 0
  153. case AlignCenter:
  154. posY = (height - cheight) / 2
  155. case AlignBottom:
  156. posY = height - cheight
  157. case AlignHeight:
  158. posY = 0
  159. pan.SetHeight(height)
  160. default:
  161. log.Fatal("HBoxLayout: invalid item vertical alignment")
  162. }
  163. // Sets the child position
  164. pan.SetPosition(posX, posY)
  165. // Calculates next position
  166. posX += pan.Width()
  167. if pos < len(parent.Children())-1 {
  168. posX += spaceMiddle
  169. }
  170. }
  171. }