summaryrefslogtreecommitdiff
path: root/table_darwin.go
blob: 26418b6be608a47d98640cd8f1090dfcb0864006 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 29 july 2014

package ui

import (
	"fmt"
	"reflect"
	"unsafe"
	"image"
)

// #include "objc_darwin.h"
import "C"

type table struct {
	*tablebase

	*scroller

	selected *event
}

func finishNewTable(b *tablebase, ty reflect.Type) Table {
	id := C.newTable()
	t := &table{
		scroller:  newScroller(id, true), // border on Table
		tablebase: b,
		selected:  newEvent(),
	}
	t.fpreferredSize = t.xpreferredSize
	// also sets the delegate
	C.tableMakeDataSource(t.id, unsafe.Pointer(t))
	for i := 0; i < ty.NumField(); i++ {
		cname := C.CString(ty.Field(i).Name)
		coltype := C.colTypeText
		editable := false
		switch {
		case ty.Field(i).Type == reflect.TypeOf((*image.RGBA)(nil)):
			coltype = C.colTypeImage
		case ty.Field(i).Type.Kind() == reflect.Bool:
			coltype = C.colTypeCheckbox
			editable = true
		}
		C.tableAppendColumn(t.id, C.intptr_t(i), cname, C.int(coltype), toBOOL(editable))
		C.free(unsafe.Pointer(cname)) // free now (not deferred) to conserve memory
	}
	return t
}

func (t *table) Unlock() {
	t.unlock()
	// 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()
			C.tableUpdate(t.id)
		})
	}()
}

func (t *table) Selected() int {
	t.RLock()
	defer t.RUnlock()
	return int(C.tableSelected(t.id))
}

func (t *table) Select(index int) {
	t.RLock()
	defer t.RUnlock()
	C.tableSelect(t.id, C.intptr_t(index))
}

func (t *table) OnSelected(f func()) {
	t.selected.set(f)
}

//export goTableDataSource_getValue
func goTableDataSource_getValue(data unsafe.Pointer, row C.intptr_t, col C.intptr_t, outtype *C.int) unsafe.Pointer {
	t := (*table)(data)
	t.RLock()
	defer t.RUnlock()
	d := reflect.Indirect(reflect.ValueOf(t.data))
	datum := d.Index(int(row)).Field(int(col))
	switch {
	case datum.Type() == reflect.TypeOf((*image.RGBA)(nil)):
		*outtype = C.colTypeImage
		d := datum.Interface().(*image.RGBA)
		img := C.toTableImage(unsafe.Pointer(pixelData(d)), C.intptr_t(d.Rect.Dx()), C.intptr_t(d.Rect.Dy()), C.intptr_t(d.Stride))
		return unsafe.Pointer(img)
	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
	default:
		s := fmt.Sprintf("%v", datum)
		return unsafe.Pointer(C.CString(s))
	}
}

//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())
}

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

//export tableSelectionChanged
func tableSelectionChanged(data unsafe.Pointer) {
	t := (*table)(data)
	t.selected.fire()
}

func (t *table) xpreferredSize(d *sizing) (width, height int) {
	s := C.tablePreferredSize(t.id)
	return int(s.width), int(s.height)
}