|
|
@@ -4,21 +4,271 @@
|
|
|
|
|
|
package gui
|
|
|
|
|
|
-import ()
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "github.com/g3n/engine/math32"
|
|
|
+)
|
|
|
|
|
|
//
|
|
|
-// Table implements a panel which can contains row and columns of child panels
|
|
|
+// Table implements a panel which can contains child panels
|
|
|
+// organized in rows and columns.
|
|
|
//
|
|
|
type Table struct {
|
|
|
- Panel // Embedded panel
|
|
|
+ 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 initial width and height
|
|
|
-func NewTable(width, height float32) *Table {
|
|
|
+// 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) {
|
|
|
|
|
|
- return t
|
|
|
+ s := t.styles.Header
|
|
|
+ hp.SetBordersFrom(&s.Border)
|
|
|
+ hp.SetBordersColor4(&s.BorderColor)
|
|
|
+ hp.SetPaddingsFrom(&s.Paddings)
|
|
|
+ hp.SetColor(&s.BgColor)
|
|
|
}
|