| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package gui
- // GridLayout is a panel layout which arranges its children in a rectangular grid.
- // It is necessary to set the number of columns of the grid when the layout is created.
- // The panel's child elements are positioned in the grid cells accordingly to the
- // order they were added to the panel.
- // The height of each row is determined by the height of the heightest child in the row.
- // The width of each column is determined by the width of the widest child in the column
- type GridLayout struct {
- pan IPanel // parent panel
- columns []colInfo // columns alignment info
- alignh Align // global cell horizontal alignment
- alignv Align // global cell vertical alignment
- expandh bool // expand horizontally flag
- expandv bool // expand vertically flag
- }
- // GridLayoutParams describes layout parameter for an specific child
- type GridLayoutParams struct {
- ColSpan int // Number of additional columns to ocuppy to the right
- AlignH Align // Vertical alignment
- AlignV Align // Horizontal alignment
- }
- // colInfo keeps information about each grid column
- type colInfo struct {
- alignh *Align // optional column horizontal alignment
- alignv *Align // optional column vertical alignment
- }
- // NewGridLayout creates and returns a pointer of a new grid layout
- func NewGridLayout(ncols int) *GridLayout {
- if ncols <= 0 {
- panic("Invalid number of columns")
- }
- gl := new(GridLayout)
- gl.columns = make([]colInfo, ncols)
- return gl
- }
- // SetAlignV sets the vertical alignment for all the grid cells
- // The alignment of an individual cell can be set by settings its layout parameters.
- func (g *GridLayout) SetAlignV(align Align) {
- g.alignv = align
- g.Recalc(g.pan)
- }
- // SetAlignH sets the horizontal alignment for all the grid cells
- // The alignment of an individual cell can be set by settings its layout parameters.
- func (g *GridLayout) SetAlignH(align Align) {
- g.alignh = align
- g.Recalc(g.pan)
- }
- // SetExpandH sets it the columns should expand horizontally if possible
- func (g *GridLayout) SetExpandH(expand bool) {
- g.expandh = expand
- g.Recalc(g.pan)
- }
- // SetExpandV sets it the rowss should expand vertically if possible
- func (g *GridLayout) SetExpandV(expand bool) {
- g.expandv = expand
- g.Recalc(g.pan)
- }
- // SetColAlignV sets the vertical alignment for all the cells of
- // the specified column. The function panics if the supplied column is invalid
- func (g *GridLayout) SetColAlignV(col int, align Align) {
- if col < 0 || col >= len(g.columns) {
- panic("Invalid column")
- }
- if g.columns[col].alignv == nil {
- g.columns[col].alignv = new(Align)
- }
- *g.columns[col].alignv = align
- g.Recalc(g.pan)
- }
- // SetColAlignH sets the horizontal alignment for all the cells of
- // the specified column. The function panics if the supplied column is invalid.
- func (g *GridLayout) SetColAlignH(col int, align Align) {
- if col < 0 || col >= len(g.columns) {
- panic("Invalid column")
- }
- if g.columns[col].alignh == nil {
- g.columns[col].alignh = new(Align)
- }
- *g.columns[col].alignh = align
- g.Recalc(g.pan)
- }
- // Recalc sets the position and sizes of all of the panel's children.
- // It is normally called by the parent panel when its size changes or
- // a child is added or removed.
- func (g *GridLayout) Recalc(ipan IPanel) {
- type cell struct {
- panel *Panel // pointer to cell panel
- params GridLayoutParams // copy of params or default
- paramsDef bool // true if parameters are default
- }
- type row struct {
- cells []*cell // array of row cells
- height float32 // row height
- }
- // Saves the received panel
- g.pan = ipan
- if g.pan == nil {
- return
- }
- // Builds array of child rows
- pan := ipan.GetPanel()
- var irow int
- var icol int
- rows := []row{}
- for _, node := range pan.Children() {
- // Ignore invisible child
- child := node.(IPanel).GetPanel()
- if !child.Visible() {
- continue
- }
- // Checks child layout params, if supplied
- ip := child.layoutParams
- var params *GridLayoutParams
- var ok bool
- var paramsDef bool
- if ip != nil {
- params, ok = child.layoutParams.(*GridLayoutParams)
- if !ok {
- panic("layoutParams is not GridLayoutParams")
- }
- paramsDef = false
- } else {
- params = &GridLayoutParams{}
- paramsDef = true
- }
- // If first column, creates row and appends to rows
- if icol == 0 {
- var r row
- r.cells = make([]*cell, len(g.columns))
- rows = append(rows, r)
- }
- // Set current child panel to current cells
- rows[irow].cells[icol] = &cell{child, *params, paramsDef}
- // Updates next cell column and row
- icol += 1 + params.ColSpan
- if icol >= len(g.columns) {
- irow++
- icol = 0
- }
- }
- // Sets the height of each row to the height of the heightest child
- // Sets the width of each column to the width of the widest column
- colWidths := make([]float32, len(g.columns))
- for i := 0; i < len(rows); i++ {
- r := &rows[i]
- r.height = 0
- for ci, cell := range r.cells {
- if cell == nil {
- continue
- }
- if cell.panel.Height() > r.height {
- r.height = cell.panel.Height()
- }
- // If cell span columns, ignore this cell when computing column width
- if cell.params.ColSpan > 0 {
- continue
- }
- if cell.panel.Width() > colWidths[ci] {
- colWidths[ci] = cell.panel.Width()
- }
- }
- }
- // If expand horizontally set, distribute available space between all columns
- if g.expandh {
- var twidth float32
- for i := 0; i < len(colWidths); i++ {
- twidth += colWidths[i]
- }
- space := pan.ContentWidth() - twidth
- if space > 0 {
- colspace := space / float32(len(colWidths))
- for i := 0; i < len(colWidths); i++ {
- colWidths[i] += colspace
- }
- }
- }
- // If expand vertically set, distribute available space between all rows
- if g.expandv {
- // Calculates the sum of all row heights
- var theight float32
- for _, r := range rows {
- theight += r.height
- }
- // If space available distribute between all rows
- space := pan.ContentHeight() - theight
- if space > 0 {
- rowspace := space / float32(len(rows))
- for i := 0; i < len(rows); i++ {
- rows[i].height += rowspace
- }
- }
- }
- // Position each child panel in the parent panel
- var celly float32
- for _, r := range rows {
- var cellx float32
- for ci, cell := range r.cells {
- if cell == nil {
- continue
- }
- colspan := 0
- // Default grid cell alignment
- alignv := g.alignv
- alignh := g.alignh
- // If column has alignment, use them
- if g.columns[ci].alignv != nil {
- alignv = *g.columns[ci].alignv
- }
- if g.columns[ci].alignh != nil {
- alignh = *g.columns[ci].alignh
- }
- // If cell has layout parameters, use them
- if !cell.paramsDef {
- alignh = cell.params.AlignH
- alignv = cell.params.AlignV
- colspan = cell.params.ColSpan
- }
- // Calculates the available width for the cell considering colspan
- var cellWidth float32
- for i := ci; i < ci+colspan+1; i++ {
- if i >= len(colWidths) {
- break
- }
- cellWidth += colWidths[i]
- }
- // Determines child panel horizontal position
- px := cellx
- switch alignh {
- case AlignNone:
- case AlignLeft:
- case AlignRight:
- space := cellWidth - cell.panel.Width()
- if space > 0 {
- px += space
- }
- case AlignCenter:
- space := (cellWidth - cell.panel.Width()) / 2
- if space > 0 {
- px += space
- }
- default:
- panic("Invalid horizontal alignment")
- }
- // Determines child panel vertical position
- py := celly
- switch alignv {
- case AlignNone:
- case AlignTop:
- case AlignBottom:
- space := r.height - cell.panel.Height()
- if space > 0 {
- py += space
- }
- case AlignCenter:
- space := (r.height - cell.panel.Height()) / 2
- if space > 0 {
- py += space
- }
- default:
- panic("Invalid vertical alignment")
- }
- // Sets child panel position
- cell.panel.SetPosition(px, py)
- // Advances to next row cell considering colspan
- cellx += cellWidth
- }
- celly += r.height
- }
- }
|