gridlayout.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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. // GridLayout is a panel layout which arranges its children in a rectangular grid.
  6. // It is necessary to set the number of columns of the grid when the layout is created.
  7. // The panel's child elements are positioned in the grid cells accordingly to the
  8. // order they were added to the panel.
  9. // The height of each row is determined by the height of the heightest child in the row.
  10. // The width of each column is determined by the width of the widest child in the column
  11. type GridLayout struct {
  12. columns []colInfo
  13. alignh Align // global cell horizontal alignment
  14. alignv Align // global cell vertical alignment
  15. spaceh float32 // space between rows
  16. specev float32 // space between columns
  17. }
  18. // GridLayoutParams describes layout parameter for an specific child
  19. type GridLayoutParams struct {
  20. ColSpan int // Number of additional columns to ocuppy to the right
  21. AlignH Align // Vertical alignment
  22. AlignV Align // Horizontal alignment
  23. }
  24. // colInfo keeps information about each grid column
  25. type colInfo struct {
  26. width float32 // column width
  27. alignh *Align // optional column horizontal alignment
  28. alignv *Align // optional column vertical alignment
  29. }
  30. // NewGridLayout creates and returns a pointer of a new grid layout
  31. func NewGridLayout(ncols int) *GridLayout {
  32. if ncols <= 0 {
  33. panic("Invalid number of columns")
  34. }
  35. gl := new(GridLayout)
  36. gl.columns = make([]colInfo, ncols)
  37. return gl
  38. }
  39. // SetAlignV sets the vertical alignment for all the grid cells
  40. // The alignment of an individual cell can be set by settings its layout parameters.
  41. func (g *GridLayout) SetAlignV(align Align) {
  42. g.alignv = align
  43. }
  44. // SetAlignH sets the horizontal alignment for all the grid cells
  45. // The alignment of an individual cell can be set by settings its layout parameters.
  46. func (g *GridLayout) SetAlignH(align Align) {
  47. g.alignh = align
  48. }
  49. // SetColAlignV sets the vertical alignment for all the cells of
  50. // the specified column. The function panics if the supplied column is invalid
  51. func (g *GridLayout) SetColAlignV(col int, align Align) {
  52. if col < 0 || col >= len(g.columns) {
  53. panic("Invalid column")
  54. }
  55. if g.columns[col].alignv == nil {
  56. g.columns[col].alignv = new(Align)
  57. }
  58. *g.columns[col].alignv = align
  59. }
  60. // SetColAlignH sets the horizontal alignment for all the cells of
  61. // the specified column. The function panics if the supplied column is invalid.
  62. func (g *GridLayout) SetColAlignH(col int, align Align) {
  63. if col < 0 || col >= len(g.columns) {
  64. panic("Invalid column")
  65. }
  66. if g.columns[col].alignh == nil {
  67. g.columns[col].alignh = new(Align)
  68. }
  69. *g.columns[col].alignh = align
  70. }
  71. // Recalc sets the position and sizes of all of the panel's children.
  72. // It is normally called by the parent panel when its size changes or
  73. // a child is added or removed.
  74. func (g *GridLayout) Recalc(ipan IPanel) {
  75. type cell struct {
  76. panel *Panel
  77. params *GridLayoutParams
  78. }
  79. type row struct {
  80. cells []*cell
  81. height float32
  82. }
  83. // Builds array of child rows
  84. pan := ipan.GetPanel()
  85. var irow int
  86. var icol int
  87. rows := []row{}
  88. for _, node := range pan.Children() {
  89. // Ignore invisible child
  90. child := node.(IPanel).GetPanel()
  91. if !child.Visible() {
  92. continue
  93. }
  94. // Checks child layout params, if supplied
  95. ip := child.layoutParams
  96. var params *GridLayoutParams
  97. var ok bool
  98. if ip != nil {
  99. params, ok = child.layoutParams.(*GridLayoutParams)
  100. if !ok {
  101. panic("layoutParams is not GridLayoutParams")
  102. }
  103. }
  104. // If first column, creates row and appends to rows
  105. if icol == 0 {
  106. var r row
  107. r.cells = make([]*cell, len(g.columns))
  108. rows = append(rows, r)
  109. }
  110. // Set current child panel to current cells
  111. rows[irow].cells[icol] = &cell{child, params}
  112. // Checks child panel colspan layout params
  113. coljump := 1
  114. if params != nil && params.ColSpan > 0 {
  115. coljump += params.ColSpan
  116. }
  117. // Updates next cell column and row
  118. icol += coljump
  119. if icol >= len(g.columns) {
  120. irow++
  121. icol = 0
  122. }
  123. }
  124. // Resets columns widths
  125. for i := 0; i < len(g.columns); i++ {
  126. g.columns[i].width = 0
  127. }
  128. // Sets the height of each row to the height of the heightest child
  129. // Sets the width of each column to the width of the widest column
  130. for i := 0; i < len(rows); i++ {
  131. r := &rows[i]
  132. r.height = 0
  133. for ci, cell := range r.cells {
  134. if cell == nil {
  135. continue
  136. }
  137. if cell.panel.Height() > r.height {
  138. r.height = cell.panel.Height()
  139. }
  140. if cell.panel.Width() > g.columns[ci].width {
  141. g.columns[ci].width = cell.panel.Width()
  142. }
  143. }
  144. }
  145. // Position each child panel in the parent panel
  146. var celly float32
  147. for _, r := range rows {
  148. var cellx float32
  149. for ci, cell := range r.cells {
  150. if cell == nil {
  151. continue
  152. }
  153. colWidth := g.columns[ci].width
  154. colspan := 0
  155. // Default grid cell alignment
  156. alignv := g.alignv
  157. alignh := g.alignh
  158. // If column has aligment:
  159. if g.columns[ci].alignv != nil {
  160. alignv = *g.columns[ci].alignv
  161. }
  162. if g.columns[ci].alignh != nil {
  163. alignh = *g.columns[ci].alignh
  164. }
  165. // If cell has layout parameters:
  166. if cell.params != nil {
  167. alignv = cell.params.AlignV
  168. alignv = cell.params.AlignH
  169. colspan = cell.params.ColSpan
  170. }
  171. // Determines child panel horizontal position
  172. px := cellx
  173. switch alignh {
  174. case AlignNone:
  175. case AlignLeft:
  176. case AlignRight:
  177. px += float32(colWidth) - cell.panel.Width()
  178. case AlignCenter:
  179. px += (float32(colWidth) - cell.panel.Width()) / 2
  180. default:
  181. panic("Invalid horizontal alignment")
  182. }
  183. // Determines child panel vertical position
  184. py := celly
  185. switch alignv {
  186. case AlignNone:
  187. case AlignTop:
  188. case AlignBottom:
  189. py += r.height - cell.panel.Height()
  190. case AlignCenter:
  191. py += (r.height - cell.panel.Height()) / 2
  192. default:
  193. panic("Invalid vertical alignment")
  194. }
  195. // Sets child panel position
  196. cell.panel.SetPosition(px, py)
  197. // Advances to next row cell considering colspan
  198. for i := ci; i < ci+colspan+1; i++ {
  199. if i >= len(g.columns) {
  200. break
  201. }
  202. cellx += g.columns[i].width
  203. }
  204. }
  205. celly += r.height
  206. }
  207. }