leonsal преди 8 години
родител
ревизия
a59b34967d
променени са 2 файла, в които са добавени 269 реда и са изтрити 6 реда
  1. 13 0
      gui/style.go
  2. 256 6
      gui/table.go

+ 13 - 0
gui/style.go

@@ -36,6 +36,7 @@ type Style struct {
 	Tree          TreeStyles
 	ControlFolder ControlFolderStyles
 	Menu          MenuStyles
+	Table         TableStyles
 }
 
 const (
@@ -742,4 +743,16 @@ func setupDefaultStyle() {
 			},
 		},
 	}
+
+	// Table styles
+	StyleDefault.Table = TableStyles{
+		Header: &TableHeaderStyle{
+			Border:      BorderSizes{0, 1, 1, 0},
+			Paddings:    BorderSizes{2, 2, 2, 2},
+			BorderColor: borderColor,
+			BgColor:     bgColor,
+			FgColor:     fgColor,
+		},
+	}
+
 }

+ 256 - 6
gui/table.go

@@ -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)
 }