Migrated controls_windows.go to C.

This commit is contained in:
Pietro Gagliardi 2014-07-17 22:16:32 -04:00
parent 8fee588a1d
commit 7e40e9fe28
3 changed files with 96 additions and 68 deletions

71
redo/controls_windows.c Normal file
View File

@ -0,0 +1,71 @@
/* 17 july 2014 */
#include "winapi_windows.h"
HWND newWidget(LPCWSTR class, DWORD style, DWORD extstyle)
{
HWND hwnd;
hwnd = CreateWindowExW(
extstyle,
class, L"",
style | WS_CHILD | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
/*
the following has the consequence of making the control message-only at first
this shouldn't cause any problems... hopefully not
but see the msgwndproc() for caveat info
also don't use low control IDs as they will conflict with dialog boxes (IDCANCEL, etc.)
*/
msgwin, (HMENU) 100, hInstance, NULL);
if (hwnd == NULL) {
xpanic("error creating control", GetLastError());
return hwnd;
}
void controlSetParent(HWND control, HWND parent)
{
if (SetParent(control, parent) == NULL)
xpanic("error changing control parent", GetLastError());
}
/*
all controls that have events receive the events themselves through subclasses
to do this, all windows (including the message-only window; see http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q104069) forward WM_COMMAND to each control with this function
*/
LRESULT forwardCommand(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND control = (HWND) lParam;
/* don't generate an event if the control (if there is one) is unparented (a child of the message-only window) */
if (control != NULL && IsChild(msgwin, control) == 0)
return SendMessageW(control, msgCOMMAND, wParam, lParam);
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK buttonSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR data)
{
switch (uMsg) {
case msgCOMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
buttonClicked(data);
return 0;
}
return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
case WM_NCDESTROY:
if ((*fv_RemoveWindowSubclass)(hwnd, buttonSubProc, id) == FALSE)
xpanic("error removing Button 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", "buttonSubProc()", uMsg);
return 0; /* unreached */
}
void setButtonSubclass(HWND hwnd, void *data)
{
if ((*fv_SetWindowSubclass)(hwnd, buttonSubProc, 0, (DWORD_PTR) data) == FALSE)
xpanic("error subclassing Button to give it its own event handler", GetLastError());
}

View File

@ -8,47 +8,27 @@ import (
"unsafe"
)
// #include "winapi_windows.h"
import "C"
type widgetbase struct {
hwnd uintptr
hwnd C.HWND
}
var emptystr = syscall.StringToUTF16Ptr("")
func newWidget(class *uint16, style uintptr, extstyle uintptr) *widgetbase {
hwnd, err := f_CreateWindowExW(
extstyle,
class, emptystr,
style | c_WS_CHILD | c_WS_VISIBLE,
c_CW_USEDEFAULT, c_CW_USEDEFAULT,
// c_CW_USEDEFAULT, c_CW_USEDEFAULT,
100,100,
// the following has the consequence of making the control message-only at first
// this shouldn't cause any problems... hopefully not
// but see the msgwndproc() for caveat info
// also don't use low control IDs as they will conflict with dialog boxes (IDCANCEL, etc.)
msgwin, 100, hInstance, nil)
if hwnd == hNULL {
panic(fmt.Errorf("creating control of class %q failed: %v", class, err))
}
func newWidget(class C.LPCWSTR, style uintptr, extstyle uintptr) *widgetbase {
return &widgetbase{
hwnd: hwnd,
hwnd: C.newWidget(class, style, extstyle),
}
}
// these few methods are embedded by all the various Controls since they all will do the same thing
func (w *widgetbase) unparent() {
res, err := f_SetParent(w.hwnd, msgwin)
if res == hNULL { // result type is HWND
panic(fmt.Errorf("error unparenting control: %v", err))
}
C.controlSetParent(w.hwnd, C.msgwin)
}
func (w *widgetbase) parent(win *window) {
res, err := f_SetParent(w.hwnd, win.hwnd)
if res == hNULL { // result type is HWND
panic(fmt.Errorf("error parenting control: %v", err))
}
C.controlSetParent(w.hwnd, win.hwnd)
}
// don't embed these as exported; let each Control decide if it should
@ -57,7 +37,7 @@ func (w *widgetbase) text() *Request {
c := make(chan interface{})
return &Request{
op: func() {
c <- getWindowText(w.hwnd)
c <- C.GoString(C.getWindowText(w.hwnd))
},
resp: c,
}
@ -67,47 +47,33 @@ func (w *widgetbase) settext(text string, results ...t_LRESULT) *Request {
c := make(chan interface{})
return &Request{
op: func() {
setWindowText(w.hwnd, text, append([]t_LRESULT{c_FALSE}, results...))
C.setWindowText(w.hwnd, toUTF16(text))
c <- struct{}{}
},
resp: c,
}
}
// all controls that have events receive the events themselves through subclasses
// to do this, all windows (including the message-only window; see http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q104069) forward WM_COMMAND to each control with this function
func forwardCommand(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT {
control := uintptr(lParam)
// don't generate an event if the control (if there is one) is unparented (a child of the message-only window)
if control != hNULL && f_IsChild(msgwin, control) == 0 {
return f_SendMessageW(control, msgCOMMAND, wParam, lParam)
}
return f_DefWindowProcW(hwnd, uMsg, wParam, lParam)
}
type button struct {
*widgetbase
clicked *event
}
var buttonclass = syscall.StringToUTF16Ptr("BUTTON")
var buttonclass = toUTF16("BUTTON")
func newButton(text string) *Request {
c := make(chan interface{})
return &Request{
op: func() {
w := newWidget(buttonclass,
c_BS_PUSHBUTTON | c_WS_TABSTOP,
C.BS_PUSHBUTTON | C.WS_TABSTOP,
0)
setWindowText(w.hwnd, text, []t_LRESULT{c_FALSE})
C.setWindowText(w.hwnd, toUTF16(text))
b := &button{
widgetbase: w,
clicked: newEvent(),
}
res, err := f_SetWindowSubclass(w.hwnd, buttonsubprocptr, 0, t_DWORD_PTR(uintptr(unsafe.Pointer(b))))
if res == c_FALSE {
panic(fmt.Errorf("error subclassing Button to give it its own event handler: %v", err))
}
C.setButtonSubclass(w.hwnd, unsafe.Pointer(b))
c <- b
},
resp: c,
@ -140,24 +106,9 @@ func init() {
buttonsubprocptr = syscall.NewCallback(buttonSubProc)
}
func buttonSubProc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM, id t_UINT_PTR, data t_DWORD_PTR) t_LRESULT {
b := (*button)(unsafe.Pointer(uintptr(data)))
switch uMsg {
case msgCOMMAND:
if wParam.HIWORD() == c_BN_CLICKED {
b.clicked.fire()
println("button clicked")
return 0
}
return f_DefSubclassProc(hwnd, uMsg, wParam, lParam)
case c_WM_NCDESTROY:
res, err := f_RemoveWindowSubclass(b.hwnd, buttonsubprocptr, id)
if res == c_FALSE {
panic(fmt.Errorf("error removing Button subclass (which was for its own event handler): %v", err))
}
return f_DefSubclassProc(hwnd, uMsg, wParam, lParam)
default:
return f_DefSubclassProc(hwnd, uMsg, wParam, lParam)
}
panic(fmt.Errorf("Button message %d does not return a value (bug in buttonSubProc())", uMsg))
//export buttonClicked
func buttonClicked(data unsafe.Pointer) {
b := (*button)(data)
b.clicked.fire()
println("button clicked")
}

View File

@ -32,3 +32,9 @@ extern DWORD initCommonControls(LPCWSTR, char **);
extern BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
extern BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
extern LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
/* controls_windows.c */
extern HWND newWidget(LPCWSTR, DWORD, DWORD);
extern void controlSetParent(HWND, HWND);
extern LRESULT forwardCommand(HWND, UINT, WPARAM, LPARAM);
extern void setButtonSubclass(HWND, void *);