2014-07-29 09:32:49 -05:00
|
|
|
// 29 july 2014
|
|
|
|
|
|
|
|
package ui
|
|
|
|
|
|
|
|
import (
|
2014-07-29 09:54:52 -05:00
|
|
|
"fmt"
|
2014-07-29 09:32:49 -05:00
|
|
|
"reflect"
|
|
|
|
"unsafe"
|
2015-02-18 22:04:14 -06:00
|
|
|
"image"
|
2014-07-29 09:32:49 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// #include "objc_darwin.h"
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
type table struct {
|
|
|
|
*tablebase
|
2014-08-03 19:08:25 -05:00
|
|
|
|
2014-10-18 16:03:07 -05:00
|
|
|
*scroller
|
2014-08-16 20:49:43 -05:00
|
|
|
|
2014-10-02 09:05:53 -05:00
|
|
|
selected *event
|
2014-07-29 09:32:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func finishNewTable(b *tablebase, ty reflect.Type) Table {
|
|
|
|
id := C.newTable()
|
|
|
|
t := &table{
|
2014-10-02 09:05:53 -05:00
|
|
|
scroller: newScroller(id, true), // border on Table
|
|
|
|
tablebase: b,
|
|
|
|
selected: newEvent(),
|
2014-07-29 09:32:49 -05:00
|
|
|
}
|
2014-10-18 16:03:07 -05:00
|
|
|
t.fpreferredSize = t.xpreferredSize
|
2014-08-20 20:43:28 -05:00
|
|
|
// also sets the delegate
|
2014-10-18 16:03:07 -05:00
|
|
|
C.tableMakeDataSource(t.id, unsafe.Pointer(t))
|
2014-07-29 09:32:49 -05:00
|
|
|
for i := 0; i < ty.NumField(); i++ {
|
2015-02-21 14:53:13 -06:00
|
|
|
colname := ty.Field(i).Tag.Get("uicolumn")
|
|
|
|
if colname == "" {
|
|
|
|
colname = ty.Field(i).Name
|
|
|
|
}
|
|
|
|
cname := C.CString(colname)
|
2014-08-16 20:49:43 -05:00
|
|
|
coltype := C.colTypeText
|
2014-08-17 09:13:28 -05:00
|
|
|
editable := false
|
|
|
|
switch {
|
2015-02-18 22:04:14 -06:00
|
|
|
case ty.Field(i).Type == reflect.TypeOf((*image.RGBA)(nil)):
|
2014-08-16 20:49:43 -05:00
|
|
|
coltype = C.colTypeImage
|
2014-08-17 09:13:28 -05:00
|
|
|
case ty.Field(i).Type.Kind() == reflect.Bool:
|
|
|
|
coltype = C.colTypeCheckbox
|
|
|
|
editable = true
|
2014-08-16 20:49:43 -05:00
|
|
|
}
|
2014-10-18 16:03:07 -05:00
|
|
|
C.tableAppendColumn(t.id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
|
2014-10-02 09:05:53 -05:00
|
|
|
C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory
|
2014-07-29 09:32:49 -05:00
|
|
|
}
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *table) Unlock() {
|
|
|
|
t.unlock()
|
2014-08-11 18:38:21 -05:00
|
|
|
// there's a possibility that user actions can happen at this point, before the view is updated
|
|
|
|
// alas, this is something we have to deal with, because Unlock() can be called from any thread
|
|
|
|
go func() {
|
|
|
|
Do(func() {
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
2014-10-18 16:03:07 -05:00
|
|
|
C.tableUpdate(t.id)
|
2014-08-11 18:38:21 -05:00
|
|
|
})
|
|
|
|
}()
|
2014-07-29 09:32:49 -05:00
|
|
|
}
|
2014-07-29 09:54:52 -05:00
|
|
|
|
2014-08-18 09:41:58 -05:00
|
|
|
func (t *table) Selected() int {
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
2014-10-18 16:03:07 -05:00
|
|
|
return int(C.tableSelected(t.id))
|
2014-08-18 09:41:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *table) Select(index int) {
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
2014-10-18 16:03:07 -05:00
|
|
|
C.tableSelect(t.id, C.intptr_t(index))
|
2014-08-18 09:41:58 -05:00
|
|
|
}
|
|
|
|
|
2014-08-20 20:43:28 -05:00
|
|
|
func (t *table) OnSelected(f func()) {
|
|
|
|
t.selected.set(f)
|
|
|
|
}
|
|
|
|
|
2014-07-29 09:54:52 -05:00
|
|
|
//export goTableDataSource_getValue
|
2014-08-17 09:13:28 -05:00
|
|
|
func goTableDataSource_getValue(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, outtype *C.int) unsafe.Pointer {
|
2014-07-29 09:54:52 -05:00
|
|
|
t := (*table)(data)
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
|
|
|
d := reflect.Indirect(reflect.ValueOf(t.data))
|
|
|
|
datum := d.Index(int(row)).Field(int(col))
|
2014-08-17 09:13:28 -05:00
|
|
|
switch {
|
2015-02-18 22:04:14 -06:00
|
|
|
case datum.Type() == reflect.TypeOf((*image.RGBA)(nil)):
|
2014-08-17 09:13:28 -05:00
|
|
|
*outtype = C.colTypeImage
|
2015-02-18 22:04:14 -06:00
|
|
|
d := datum.Interface().(*image.RGBA)
|
2015-02-19 21:02:58 -06:00
|
|
|
img := C.toTableImage(unsafe.Pointer(pixelData(d)), C.intptr_t(d.Rect.Dx()), C.intptr_t(d.Rect.Dy()), C.intptr_t(d.Stride))
|
2015-02-18 22:04:14 -06:00
|
|
|
return unsafe.Pointer(img)
|
2014-08-17 09:13:28 -05:00
|
|
|
case datum.Kind() == reflect.Bool:
|
|
|
|
*outtype = C.colTypeCheckbox
|
|
|
|
if datum.Bool() == true {
|
|
|
|
// return a non-nil pointer
|
|
|
|
// outtype isn't Go-side so it'll work
|
|
|
|
return unsafe.Pointer(outtype)
|
|
|
|
}
|
|
|
|
return nil
|
2014-08-16 20:49:43 -05:00
|
|
|
default:
|
|
|
|
s := fmt.Sprintf("%v", datum)
|
|
|
|
return unsafe.Pointer(C.CString(s))
|
|
|
|
}
|
2014-07-29 09:54:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
//export goTableDataSource_getRowCount
|
|
|
|
func goTableDataSource_getRowCount(data unsafe.Pointer) C.intptr_t {
|
|
|
|
t := (*table)(data)
|
|
|
|
t.RLock()
|
|
|
|
defer t.RUnlock()
|
|
|
|
d := reflect.Indirect(reflect.ValueOf(t.data))
|
|
|
|
return C.intptr_t(d.Len())
|
|
|
|
}
|
2014-08-03 19:08:25 -05:00
|
|
|
|
2014-08-17 09:13:28 -05:00
|
|
|
//export goTableDataSource_toggled
|
|
|
|
func goTableDataSource_toggled(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, checked C.BOOL) {
|
|
|
|
t := (*table)(data)
|
|
|
|
t.Lock()
|
|
|
|
defer t.Unlock()
|
|
|
|
d := reflect.Indirect(reflect.ValueOf(t.data))
|
|
|
|
datum := d.Index(int(row)).Field(int(col))
|
|
|
|
datum.SetBool(fromBOOL(checked))
|
|
|
|
}
|
|
|
|
|
2014-08-20 20:43:28 -05:00
|
|
|
//export tableSelectionChanged
|
|
|
|
func tableSelectionChanged(data unsafe.Pointer) {
|
|
|
|
t := (*table)(data)
|
|
|
|
t.selected.fire()
|
|
|
|
}
|
|
|
|
|
2014-10-18 16:03:07 -05:00
|
|
|
func (t *table) xpreferredSize(d *sizing) (width, height int) {
|
|
|
|
s := C.tablePreferredSize(t.id)
|
2014-08-10 14:56:59 -05:00
|
|
|
return int(s.width), int(s.height)
|
2014-08-03 19:08:25 -05:00
|
|
|
}
|