diff --git a/redo/table.go b/redo/table.go new file mode 100644 index 0000000..f6f78d5 --- /dev/null +++ b/redo/table.go @@ -0,0 +1,66 @@ +// 28 july 2014 + +package ui + +import ( + "fmt" + "reflect" + "sync" +) + +// Table is a Control that displays a list of like-structured data in a grid where each row represents an item and each column represents a bit of data. +// As such, a Table renders a []struct{...} where each field of the struct can be a string, a number, [TODO an image, or a checkbox]. +// Tables maintain their own storage behind a sync.RWMutex-compatible sync.Locker; use Table.Lock()/Table.Unlock() to make changes and Table.RLock()/Table.RUnlock() to merely read values. +// TODO headers +type Table interface { + // Lock and Unlock lock and unlock Data for reading or writing. + // RLock and RUnlock lock and unlock Data for reading only. + // These methods have identical semantics to the analogous methods of sync.RWMutex. + Lock() + Unlock() + RLock() + RUnlock() + + // Data returns the internal data. + // The returned value will contain an object of type pointer to slice of some structure; use a type assertion to get the properly typed object out. + // Do not call this outside a Lock()..Unlock() or RLock()..RUnlock() pair. + Data() interface{} +} + +type tablebase struct { + lock sync.RWMutex + data interface{} +} + +// NewTable creates a new Table. +// Currently, the argument must be a reflect.Type representing the structure that each item in the Table will hold, and the Table will be initially empty. +// This will change in the future. +func NewTable(ty reflect.Type) Table { + if ty.Kind() != reflect.Struct { + panic(fmt.Errorf("unknown or unsupported type %v given to NewTable()", ty)) + } + b := new(tablebase) + // arbitrary starting capacity + b.data = reflect.NewSlice(ty, 0, 512).Addr().Interface() + return finishNewTable(b) +} + +func (b *tablebase) Lock() { + b.lock.Lock() +} + +func (b *tablebase) Unlock() { + b.lock.Unlock() +} + +func (b *tablebase) RLock() { + b.lock.RLock() +} + +func (b *tablebase) RUnlock() { + b.lock.RUnlock() +} + +func (b *tablebase) Data() { + return b.data +} diff --git a/redo/zz_test.go b/redo/zz_test.go index 5fc821e..327aec2 100644 --- a/redo/zz_test.go +++ b/redo/zz_test.go @@ -12,6 +12,18 @@ import ( var closeOnClick = flag.Bool("close", false, "close on click") +type dtype struct { + Name string + Address string +} +var ddata = []dtype{ + { "alpha", "beta" }, + { "gamma", "delta" }, + { "epsilon", "zeta" }, + { "eta", "theta" }, + { "iota", "kappa" }, +} + // because Cocoa hates being run off the main thread, even if it's run exclusively off the main thread func init() { flag.BoolVar(&spaced, "spaced", false, "enable spacing") @@ -30,6 +42,12 @@ func init() { done <- struct{}{} return true }) + table := NewTable(reflect.TypeOf(ddata[0])) + table.Lock() + dq := table.Data().(*[]dtype) + *dq = ddata + table.Unlock() + t.Append("Table", table) b := NewButton("There") if *closeOnClick { b.SetText("Click to Close")