浏览代码

gui table dev

leonsal 8 年之前
父节点
当前提交
a76f23d513
共有 1 个文件被更改,包括 123 次插入3 次删除
  1. 123 3
      gui/table.go

+ 123 - 3
gui/table.go

@@ -10,6 +10,7 @@ import (
 	"sort"
 	"strconv"
 
+	"github.com/g3n/engine/gui/assets"
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/window"
 )
@@ -22,9 +23,30 @@ const (
 	OnTableRowCount = "onTableRowCount"
 )
 
+// TableSortType is the type used to specify the sort method for a table column
+type TableSortType int
+
+const (
+	TableSortNone TableSortType = iota
+	TableSortString
+	TableSortNumber
+)
+
+const (
+	tableSortedNoneIcon = assets.SwapVert
+	tableSortedAscIcon  = assets.ArrowDownward
+	tableSortedDescIcon = assets.ArrowUpward
+	tableSortedNone     = 0
+	tableSortedAsc      = 1
+	tableSortedDesc     = 2
+	tableResizerPix     = 4
+)
+
 //
 // Table implements a panel which can contains child panels
 // organized in rows and columns.
+// In this implementation the table data model is keep and
+// mantained by the table itself
 //
 type Table struct {
 	Panel                       // Embedded panel
@@ -37,18 +59,22 @@ type Table struct {
 	statusPanel    Panel        // optional bottom status panel
 	statusLabel    *Label       // status label
 	scrollBarEvent bool         // do not update the scrollbar value in recalc() if true
+	resizeCol      int          // column being resized
+	resizing       bool         // draggin the column resizer
 }
 
 // TableColumn describes a table column
 type TableColumn struct {
 	Id         string          // Column id used to reference the column. Must be unique
-	Name       string          // Column name shown in the header
+	Header     string          // Column name shown in the table header
 	Width      float32         // Inital column width in pixels
 	Hidden     bool            // Hidden flag
 	Align      Align           // Cell content alignment: AlignLeft|AlignCenter|AlignRight
 	Format     string          // Format string for formatting the columns' cells
 	FormatFunc TableFormatFunc // Format function (overrides Format string)
 	Expand     int             // Column width expansion factor (0 no expansion)
+	Sort       TableSortType   // Column sort type
+	Resize     bool            // Allow column to be resized by user
 }
 
 // TableCell describes a table cell.
@@ -126,13 +152,18 @@ type tableHeader struct {
 type tableColHeader struct {
 	Panel                      // header panel
 	label      *Label          // header label
+	ricon      *Label          // header right icon (sort direction)
 	id         string          // column id
 	width      float32         // initial column width
 	format     string          // column format string
 	formatFunc TableFormatFunc // column format function
 	align      Align           // column alignment
 	expand     int             // column expand factor
+	sort       TableSortType   // column sort type
+	resize     bool            // column can be resized by user
 	order      int             // row columns order
+	sorted     int             // current sorted status
+	xr         float32         // right border coordinate in pixels
 }
 
 // tableRow is panel which contains an entire table row of cells
@@ -177,7 +208,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		c := new(tableColHeader)
 		c.Initialize(0, 0)
 		t.applyHeaderStyle(c)
-		c.label = NewLabel(cdesc.Name)
+		c.label = NewLabel(cdesc.Header)
 		c.Add(c.label)
 		c.id = cdesc.Id
 		c.width = cdesc.Width
@@ -185,6 +216,16 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 		c.format = cdesc.Format
 		c.formatFunc = cdesc.FormatFunc
 		c.expand = cdesc.Expand
+		c.sort = cdesc.Sort
+		c.resize = cdesc.Resize
+		// Adds optional sort icon
+		if c.sort != TableSortNone {
+			c.ricon = NewIconLabel(string(tableSortedNoneIcon))
+			c.Add(c.ricon)
+			c.ricon.Subscribe(OnMouseDown, func(evname string, ev interface{}) {
+				t.onRicon(evname, c)
+			})
+		}
 		// Sets default format and order
 		if c.format == "" {
 			c.format = "%v"
@@ -217,6 +258,7 @@ func NewTable(width, height float32, cols []TableColumn) (*Table, error) {
 	// Subscribe to events
 	t.Panel.Subscribe(OnCursorEnter, t.onCursor)
 	t.Panel.Subscribe(OnCursorLeave, t.onCursor)
+	t.Panel.Subscribe(OnCursor, t.onCursorPos)
 	t.Panel.Subscribe(OnScroll, t.onScroll)
 	t.Panel.Subscribe(OnMouseUp, t.onMouse)
 	t.Panel.Subscribe(OnMouseDown, t.onMouse)
@@ -528,6 +570,7 @@ func (t *Table) Cell(col string, ri int) interface{} {
 
 // SortColumn sorts the specified column interpreting its values as strings or numbers
 // and sorting in ascending or descending order.
+// This sorting is independent of the sort configuration of column set when the table was created
 func (t *Table) SortColumn(col string, asString bool, asc bool) {
 
 	c := t.header.cmap[col]
@@ -657,6 +700,40 @@ func (t *Table) onCursor(evname string, ev interface{}) {
 	t.root.StopPropagation(Stop3D)
 }
 
+// onCursorPos process subscribed cursor position events
+func (t *Table) onCursorPos(evname string, ev interface{}) {
+
+	// Convert mouse window coordinates to table content coordinates
+	kev := ev.(*window.CursorEvent)
+	cx, _ := t.ContentCoords(kev.Xpos, kev.Ypos)
+
+	// User is dragging the resizer
+	if t.resizing {
+		return
+	}
+
+	// Find column with right border closest to current position
+	found := false
+	for ci := 0; ci < len(t.header.cols); ci++ {
+		c := t.header.cols[ci]
+		dx := math32.Abs(cx - c.xr)
+		if dx < tableResizerPix {
+			if c.resize {
+				found = true
+				t.resizeCol = ci
+			}
+			break
+		}
+	}
+	if found {
+		t.root.SetCursorHResize()
+	} else {
+		t.root.SetCursorNormal()
+		t.resizeCol = -1
+	}
+	t.root.StopPropagation(Stop3D)
+}
+
 // onMouseEvent process subscribed mouse events
 func (t *Table) onMouse(evname string, ev interface{}) {
 
@@ -664,6 +741,11 @@ func (t *Table) onMouse(evname string, ev interface{}) {
 	t.root.SetKeyFocus(t)
 	switch evname {
 	case OnMouseDown:
+		// If
+		if t.resizeCol >= 0 {
+			t.resizing = true
+			return
+		}
 		// Creates and dispatch TableClickEvent
 		var tce TableClickEvent
 		tce.MouseEvent = *e
@@ -675,6 +757,10 @@ func (t *Table) onMouse(evname string, ev interface{}) {
 			t.recalc()
 		}
 	case OnMouseUp:
+		if t.resizing {
+			t.resizing = false
+			t.root.SetCursorNormal()
+		}
 	default:
 		return
 	}
@@ -700,7 +786,7 @@ func (t *Table) onKeyEvent(evname string, ev interface{}) {
 	}
 }
 
-// onResize receives subscribed resize for this table
+// onResize receives subscribed resize events for this table
 func (t *Table) onResize(evname string, ev interface{}) {
 
 	t.recalc()
@@ -719,6 +805,31 @@ func (t *Table) onScroll(evname string, ev interface{}) {
 	t.root.StopPropagation(Stop3D)
 }
 
+// onRicon receives subscribed events for column header right icon
+func (t *Table) onRicon(evname string, c *tableColHeader) {
+
+	icon := tableSortedNoneIcon
+	var asc bool
+	if c.sorted == tableSortedNone || c.sorted == tableSortedDesc {
+		c.sorted = tableSortedAsc
+		icon = tableSortedAscIcon
+		asc = false
+	} else {
+		c.sorted = tableSortedDesc
+		icon = tableSortedDescIcon
+		asc = true
+	}
+
+	var asString bool
+	if c.sort == TableSortString {
+		asString = true
+	} else {
+		asString = false
+	}
+	t.SortColumn(c.id, asString, asc)
+	c.ricon.SetText(string(icon))
+}
+
 // findClick finds where in the table the specified mouse click event
 // occurred updating the specified TableClickEvent with the click coordinates.
 func (t *Table) findClick(ev *TableClickEvent) {
@@ -904,9 +1015,18 @@ func (t *Table) recalcHeader() {
 		if c.Height() > height {
 			height = c.Height()
 		}
+		// Sets right icon position
+		if c.ricon != nil {
+			ix := c.ContentWidth() - c.ricon.Width()
+			if ix < 0 {
+				ix = 0
+			}
+			c.ricon.SetPosition(ix, 0)
+		}
 		c.SetPosition(posx, 0)
 		c.SetVisible(true)
 		posx += c.Width()
+		c.xr = posx
 	}
 	t.header.SetContentSize(posx, height)
 }