Began the implementation of Table on Windows.
This commit is contained in:
parent
e2ef204b70
commit
7cb6ca1243
|
@ -10,6 +10,12 @@ BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
||||||
BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
||||||
LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
|
#define wantedICCClasses ( \
|
||||||
|
ICC_PROGRESS_CLASS | /* progress bars */ \
|
||||||
|
ICC_TAB_CLASSES | /* tabs */ \
|
||||||
|
ICC_LISTVIEW_CLASSES | /* list views */ \
|
||||||
|
0)
|
||||||
|
|
||||||
DWORD initCommonControls(LPCWSTR manifest, char **errmsg)
|
DWORD initCommonControls(LPCWSTR manifest, char **errmsg)
|
||||||
{
|
{
|
||||||
ACTCTX actctx;
|
ACTCTX actctx;
|
||||||
|
@ -35,7 +41,7 @@ DWORD initCommonControls(LPCWSTR manifest, char **errmsg)
|
||||||
|
|
||||||
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX));
|
||||||
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
icc.dwSize = sizeof (INITCOMMONCONTROLSEX);
|
||||||
icc.dwICC = ICC_PROGRESS_CLASS | ICC_TAB_CLASSES;
|
icc.dwICC = wantedICCClasses;
|
||||||
|
|
||||||
comctl32 = LoadLibraryW(L"comctl32.dll");
|
comctl32 = LoadLibraryW(L"comctl32.dll");
|
||||||
if (comctl32 == NULL) {
|
if (comctl32 == NULL) {
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
// 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.
|
// 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
|
// TODO headers
|
||||||
type Table interface {
|
type Table interface {
|
||||||
|
Control
|
||||||
|
|
||||||
// Lock and Unlock lock and unlock Data for reading or writing.
|
// Lock and Unlock lock and unlock Data for reading or writing.
|
||||||
// RLock and RUnlock lock and unlock Data for reading only.
|
// RLock and RUnlock lock and unlock Data for reading only.
|
||||||
// These methods have identical semantics to the analogous methods of sync.RWMutex.
|
// These methods have identical semantics to the analogous methods of sync.RWMutex.
|
||||||
|
@ -41,16 +43,18 @@ func NewTable(ty reflect.Type) Table {
|
||||||
panic(fmt.Errorf("unknown or unsupported type %v given to NewTable()", ty))
|
panic(fmt.Errorf("unknown or unsupported type %v given to NewTable()", ty))
|
||||||
}
|
}
|
||||||
b := new(tablebase)
|
b := new(tablebase)
|
||||||
// arbitrary starting capacity
|
// we want a pointer to a slice
|
||||||
b.data = reflect.NewSlice(ty, 0, 512).Addr().Interface()
|
b.data = reflect.New(reflect.SliceOf(ty)).Interface()
|
||||||
return finishNewTable(b)
|
return finishNewTable(b, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *tablebase) Lock() {
|
func (b *tablebase) Lock() {
|
||||||
b.lock.Lock()
|
b.lock.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *tablebase) Unlock() {
|
// Unlock() is defined on each backend implementation of Table
|
||||||
|
// they should all call this, however
|
||||||
|
func (b *tablebase) unlock() {
|
||||||
b.lock.Unlock()
|
b.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +66,6 @@ func (b *tablebase) RUnlock() {
|
||||||
b.lock.RUnlock()
|
b.lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *tablebase) Data() {
|
func (b *tablebase) Data() interface{} {
|
||||||
return b.data
|
return b.data
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* 28 july 2014 */
|
||||||
|
|
||||||
|
#include "winapi_windows.h"
|
||||||
|
|
||||||
|
/* provided for cgo's benefit */
|
||||||
|
LPCWSTR xWC_LISTVIEW = WC_LISTVIEW;
|
||||||
|
|
||||||
|
static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR data)
|
||||||
|
{
|
||||||
|
switch (uMsg) {
|
||||||
|
case msgCOMMAND:
|
||||||
|
/* TODO */
|
||||||
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
||||||
|
case msgNOTIFY:
|
||||||
|
/* TODO */
|
||||||
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
||||||
|
case WM_NCDESTROY:
|
||||||
|
if ((*fv_RemoveWindowSubclass)(hwnd, tableSubProc, id) == FALSE)
|
||||||
|
xpanic("error removing Table subclass (which was for its own event handler)", GetLastError());
|
||||||
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
||||||
|
default:
|
||||||
|
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
xmissedmsg("Button", "tableSubProc()", uMsg);
|
||||||
|
return 0; /* unreached */
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTableSubclass(HWND hwnd, void *data)
|
||||||
|
{
|
||||||
|
if ((*fv_SetWindowSubclass)(hwnd, tableSubProc, 0, (DWORD_PTR) data) == FALSE)
|
||||||
|
xpanic("error subclassing Table to give it its own event handler", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tableAppendColumn(HWND hwnd, int index, LPCWSTR name)
|
||||||
|
{
|
||||||
|
LVCOLUMNW col;
|
||||||
|
|
||||||
|
ZeroMemory(&col, sizeof (LVCOLUMNW));
|
||||||
|
col.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER;
|
||||||
|
col.fmt = LVCFMT_LEFT;
|
||||||
|
col.pszText = name;
|
||||||
|
col.iSubItem = index;
|
||||||
|
col.iOrder = index;
|
||||||
|
if (SendMessageW(hwnd, LVM_INSERTCOLUMN, (WPARAM) index, (LPARAM) (&col)) == (LRESULT) -1)
|
||||||
|
xpanic("error adding column to Table", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tableUpdate(HWND table, int nItems)
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 28 july 2014
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #include "winapi_windows.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type table struct {
|
||||||
|
*widgetbase
|
||||||
|
*tablebase
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishNewTable(b *tablebase, ty reflect.Type) Table {
|
||||||
|
t := &table{
|
||||||
|
widgetbase: newWidget(C.xWC_LISTVIEW,
|
||||||
|
C.LVS_REPORT | C.LVS_OWNERDATA | C.LVS_NOSORTHEADER | C.LVS_SHOWSELALWAYS | C.WS_HSCROLL | C.WS_VSCROLL,
|
||||||
|
0), // TODO WS_EX_CLIENTEDGE?
|
||||||
|
tablebase: b,
|
||||||
|
}
|
||||||
|
C.setTableSubclass(t.hwnd, unsafe.Pointer(&t))
|
||||||
|
for i := 0; i < ty.NumField(); i++ {
|
||||||
|
C.tableAppendColumn(t.hwnd, C.int(i), toUTF16(ty.Field(i).Name))
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *table) Unlock() {
|
||||||
|
t.unlock()
|
||||||
|
// TODO RACE CONDITION HERE
|
||||||
|
// I think there's a way to set the item count without causing a refetch of data that works around this...
|
||||||
|
t.RLock()
|
||||||
|
defer t.RUnlock()
|
||||||
|
C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
|
||||||
|
}
|
|
@ -87,4 +87,10 @@ extern void setTabSubclass(HWND, void *);
|
||||||
extern void tabAppend(HWND, LPCWSTR);
|
extern void tabAppend(HWND, LPCWSTR);
|
||||||
extern void tabGetContentRect(HWND, RECT *);
|
extern void tabGetContentRect(HWND, RECT *);
|
||||||
|
|
||||||
|
/* table_windows.go */
|
||||||
|
extern LPCWSTR xWC_LISTVIEW;
|
||||||
|
extern void setTableSubclass(HWND, void *);
|
||||||
|
extern void tableAppendColumn(HWND, int, LPCWSTR);
|
||||||
|
extern void tableUpdate(HWND, int);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,6 +7,7 @@ package ui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"flag"
|
"flag"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue