diff --git a/redo/common_windows.go b/redo/common_windows.go index d2cae60..30c0d75 100644 --- a/redo/common_windows.go +++ b/redo/common_windows.go @@ -46,3 +46,13 @@ func storelpParam(hwnd uintptr, lParam t_LPARAM) { cs = (*s_CREATESTRUCTW)(unsafe.Pointer(uintptr(lParam))) f_SetWindowLongPtrW(hwnd, c_GWLP_USERDATA, cs.lpCreateParams) } + +func (w t_WPARAM) HIWORD() uint16 { + u := uintptr(w) & 0xFFFF0000 + return uint16(u >> 16) +} + +func (w t_WPARAM) LOWORD() uint16 { + u := uintptr(w) & 0x0000FFFF + return uint16(u) +} diff --git a/redo/controls_windows.go b/redo/controls_windows.go index 3b73b48..dd4b8c9 100644 --- a/redo/controls_windows.go +++ b/redo/controls_windows.go @@ -5,6 +5,7 @@ package ui import ( "fmt" "syscall" + "unsafe" ) type widgetbase struct { @@ -73,8 +74,20 @@ func (w *widgetbase) settext(text string, results ...t_LRESULT) *Request { } } +// 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") @@ -89,6 +102,7 @@ func newButton(text string) *Request { setWindowText(w.hwnd, text, []t_LRESULT{c_FALSE}) c <- &button{ widgetbase: w, + clicked: newEvent(), } }, resp: c, @@ -96,8 +110,14 @@ func newButton(text string) *Request { } func (b *button) OnClicked(e func(c Doer)) *Request { - // TODO - return nil + c := make(chan interface{}) + return &Request{ + op: func() { + b.clicked.set(e) + c <- struct{}{} + }, + resp: c, + } } func (b *button) Text() *Request { @@ -107,3 +127,24 @@ func (b *button) Text() *Request { func (b *button) SetText(text string) *Request { return b.settext(text) } + +var 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 + } + // TODO return + case c_WM_NCDESTROY: + // TODO remove + // TODO return + default: + // TODO return + } + panic(fmt.Errorf("Button message %d does not return a value (bug in buttonSubProc())", uMsg)) +} diff --git a/redo/funcnames_windows.go b/redo/funcnames_windows.go index 4a12430..005607f 100644 --- a/redo/funcnames_windows.go +++ b/redo/funcnames_windows.go @@ -38,3 +38,4 @@ package ui // wfunc user32 GetDC uintptr uintptr // wfunc gdi32 SelectObject uintptr uintptr uintptr // wfunc user32 ReleaseDC uintptr uintptr uintptr +// wfunc user32 IsChild uintptr uintptr uintptr,noerr diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index 6c5b974..8e8187a 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -11,6 +11,7 @@ import ( // global messages unique to everything const ( msgRequest = c_WM_APP + 1 + iota // + 1 just to be safe + msgCOMMAND // WM_COMMAND proxy; see forwardCommand() in controls_windows.go ) var msgwin uintptr @@ -91,6 +92,8 @@ func makemsgwin() error { func msgwinproc(hwnd uintptr, uMsg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT { switch uMsg { + case c_WM_COMMAND: + return forwardCommand(hwnd, uMsg, wParam, lParam) case msgRequest: req := (*Request)(unsafe.Pointer(uintptr(lParam))) perform(req) diff --git a/redo/window_windows.go b/redo/window_windows.go index 9bf0b26..c58d028 100644 --- a/redo/window_windows.go +++ b/redo/window_windows.go @@ -160,6 +160,7 @@ func (w *window) OnClosing(e func(Doer) bool) *Request { } } +// TODO msg -> uMsg func windowWndProc(hwnd uintptr, msg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t_LRESULT { w := (*window)(unsafe.Pointer(f_GetWindowLongPtrW(hwnd, c_GWLP_USERDATA))) if w == nil { @@ -173,6 +174,8 @@ func windowWndProc(hwnd uintptr, msg t_UINT, wParam t_WPARAM, lParam t_LPARAM) t return f_DefWindowProcW(hwnd, msg, wParam, lParam) } switch msg { + case c_WM_COMMAND: + return forwardCommand(hwnd, msg, wParam, lParam) case c_WM_SIZE: var r s_RECT diff --git a/redo/zwinconstgen.go b/redo/zwinconstgen.go index 28ff843..c0e2171 100644 --- a/redo/zwinconstgen.go +++ b/redo/zwinconstgen.go @@ -276,6 +276,8 @@ func main() { fmt.Fprintf(buf, "type t_WPARAM %s\n", winName(reflect.TypeOf(C.WPARAM(0)))) fmt.Fprintf(buf, "type t_LPARAM %s\n", winName(reflect.TypeOf(C.LPARAM(0)))) fmt.Fprintf(buf, "type t_LRESULT %s\n", winName(reflect.TypeOf(C.LRESULT(0)))) + fmt.Fprintf(buf, "type t_UINT_PTR %s\n", winName(reflect.TypeOf(C.UINT_PTR(0)))) + fmt.Fprintf(buf, "type t_DWORD_PTR %s\n", winName(reflect.TypeOf(C.DWORD_PTR(0)))) // and one for GetMessageW() fmt.Fprintf(buf, "type t_BOOL %s\n", winName(reflect.TypeOf(C.BOOL(0))))