| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // 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
- import (
- "fmt"
- "github.com/g3n/engine/math32"
- )
- //
- // Table implements a panel which can contains child panels
- // organized in rows and columns.
- //
- type Table struct {
- Panel // Embedded panel
- styles *TableStyles // pointer to current styles
- cols []TableColumn // array of columns descriptors
- colmap map[string]*TableColumn // maps column id to column descriptor
- firstRow int // index of the first row of data to show
- rows []tableRow // array of table rows
- headerHeight float32
- }
- // TableColumn describes a table column
- type TableColumn struct {
- Id string // column id used to reference the column
- Name string // column name shown in the header
- Width float32 // column preferable width in pixels
- Hidden bool // hidden flag
- Format string // format string for numbers and strings
- Align int // cell align
- order int // show order
- header *Panel // header panel
- label *Label // header label
- }
- // TableHeaderStyle describes the style of the table header
- type TableHeaderStyle struct {
- Border BorderSizes
- Paddings BorderSizes
- BorderColor math32.Color4
- BgColor math32.Color
- FgColor math32.Color
- }
- // TableRowStyle describes the style of the table row
- type TableRowStyle struct {
- Border BorderSizes
- Paddings BorderSizes
- BorderColor math32.Color4
- BgColor math32.Color
- FgColor math32.Color
- }
- // TableRowStyles describes all styles for the table row
- type TableRowStyles struct {
- Normal TableRowStyle
- Selected TableRowStyle
- }
- // TableStyles describes all styles of the table header and rows
- type TableStyles struct {
- Header *TableHeaderStyle
- Row *TableRowStyles
- }
- type tableRow struct {
- height float32 // row height
- cells []*tableCell // array of row cells
- }
- type tableCell struct {
- Panel // embedded panel
- label Label // cell label
- value interface{} // cell current value
- }
- // NewTable creates and returns a pointer to a new Table with the
- // specified width, height and columns
- func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
- t := new(Table)
- t.Panel.Initialize(width, height)
- t.styles = &StyleDefault.Table
- // Checks columns descriptors
- t.colmap = make(map[string]*TableColumn)
- t.cols = make([]TableColumn, len(cols))
- copy(t.cols, cols)
- for i := 0; i < len(t.cols); i++ {
- c := &t.cols[i]
- if c.Format == "" {
- c.Format = "%v"
- }
- c.order = i
- if c.Id == "" {
- return nil, fmt.Errorf("Column with empty id")
- }
- if t.colmap[c.Id] != nil {
- return nil, fmt.Errorf("Column with duplicate id")
- }
- t.colmap[c.Id] = c
- }
- // Create header panels
- for i := 0; i < len(t.cols); i++ {
- c := &t.cols[i]
- c.header = NewPanel(0, 0)
- t.applyHeaderStyle(c.header)
- c.label = NewLabel(c.Name)
- c.header.Add(c.label)
- width := c.Width
- if width < c.label.Width()+c.header.MinWidth() {
- width = c.label.Width() + c.header.MinWidth()
- }
- c.header.SetContentSize(width, c.label.Height())
- t.headerHeight = c.header.Height()
- t.Panel.Add(c.header)
- }
- t.recalcHeader()
- return t, nil
- }
- // SetRows clears all current rows of the table and
- // sets new rows from the specifying parameter.
- // Each row is a map keyed by the colum id.
- // The map value currently can be a string or any number type
- // If a row column is not found it is ignored
- func (t *Table) SetRows(rows []map[string]interface{}) {
- // Create rows if necessary
- if len(rows) > len(t.rows) {
- count := len(rows) - len(t.rows)
- for ri := 0; ri < count; ri++ {
- var trow tableRow
- trow.cells = make([]*tableCell, 0)
- for coli := 0; coli < len(t.cols); coli++ {
- cell := new(tableCell)
- cell.Initialize(10, 10)
- cell.label.initialize("", StyleDefault.Font)
- cell.Add(&cell.label)
- cell.SetBorders(1, 1, 1, 1)
- trow.cells = append(trow.cells, cell)
- t.Panel.Add(cell)
- }
- t.rows = append(t.rows, trow)
- }
- }
- for ri := 0; ri < len(rows); ri++ {
- t.SetRow(ri, rows[ri])
- }
- t.firstRow = 0
- t.recalc()
- }
- func (t *Table) SetRow(row int, values map[string]interface{}) {
- if row < 0 || row >= len(t.rows) {
- panic("Invalid row index")
- }
- for ci := 0; ci < len(t.cols); ci++ {
- c := t.cols[ci]
- cv := values[c.Id]
- if cv == nil {
- continue
- }
- t.SetCell(row, c.Id, values[c.Id])
- }
- }
- func (t *Table) SetCell(row int, colid string, value interface{}) {
- if row < 0 || row >= len(t.rows) {
- panic("Invalid row index")
- }
- c := t.colmap[colid]
- if c == nil {
- return
- }
- cell := t.rows[row].cells[c.order]
- cell.label.SetText(fmt.Sprintf(c.Format, value))
- }
- // SetColFormat sets the formatting string (Printf) for the specified column
- // Update must be called to update the table.
- func (t *Table) SetColFormat(id, format string) error {
- c := t.colmap[id]
- if c == nil {
- return fmt.Errorf("No column with id:%s", id)
- }
- c.Format = format
- return nil
- }
- // recalcHeader recalculates and sets the position and size of the header panels
- func (t *Table) recalcHeader() {
- posx := float32(0)
- for i := 0; i < len(t.cols); i++ {
- c := t.cols[i]
- if c.Hidden {
- continue
- }
- c.header.SetPosition(posx, 0)
- posx += c.header.Width()
- }
- }
- // recalc calculates the positions...
- func (t *Table) recalc() {
- // Assumes that the TableColum array is sorted in show order
- py := t.headerHeight
- for ri := t.firstRow; ri < len(t.rows); ri++ {
- row := t.rows[ri]
- px := float32(0)
- // Get maximum height for row
- for ci := 0; ci < len(t.cols); ci++ {
- // If column is hidden, ignore
- c := t.cols[ci]
- if c.Hidden {
- continue
- }
- cell := row.cells[c.order]
- cellHeight := cell.MinHeight() + cell.label.Height()
- if cellHeight > row.height {
- row.height = cellHeight
- }
- }
- // Sets position and size of row cells
- for ci := 0; ci < len(t.cols); ci++ {
- // If column is hidden, ignore
- c := t.cols[ci]
- if c.Hidden {
- continue
- }
- // Sets cell position and size
- cell := row.cells[c.order]
- cell.SetPosition(px, py)
- cell.SetSize(c.header.Width(), row.height)
- log.Error("Cell(%v,%v)(%p) size:%v/%v pos:%v/%v", ri, c.Id, &cell, cell.Width(), cell.Height(), cell.Position().X, cell.Position().Y)
- px += cell.Width()
- }
- py += row.height
- }
- }
- // colAtOrder returns the TableColumn at the specified view order.
- // Returns nil if not found
- func (t *Table) colAtOrder(order int) *TableColumn {
- for ci := 0; ci < len(t.cols); ci++ {
- if t.cols[ci].order == order {
- return &t.cols[ci]
- }
- }
- return nil
- }
- // applyStyle applies the specified menu body style
- func (t *Table) applyHeaderStyle(hp *Panel) {
- s := t.styles.Header
- hp.SetBordersFrom(&s.Border)
- hp.SetBordersColor4(&s.BorderColor)
- hp.SetPaddingsFrom(&s.Paddings)
- hp.SetColor(&s.BgColor)
- }
|