diff --git a/redo/area.go b/redo/area.go index b4b2f78..75f47b2 100644 --- a/redo/area.go +++ b/redo/area.go @@ -74,10 +74,10 @@ type AreaHandler interface { Mouse(e MouseEvent) // Key is called when the Area receives a keyboard event. - // You are allowed to do nothing in this handler (to ignore keyboard events). + // Return true to indicate that you handled the event; return false to indicate that you did not and let the system handle the event. + // You are allowed to do nothing in this handler (to ignore keyboard events); in this case, return false. // See KeyEvent for details. - // After handling the key event, package ui will decide whether to perform platform-dependent event chain continuation based on that platform's designated action (so it is not possible to override global key events, such as Alt-Tab, this way). - Key(e KeyEvent) + Key(e KeyEvent) (handled bool) } // MouseEvent contains all the information for a mous event sent by Area.Mouse. @@ -140,7 +140,7 @@ func (e MouseEvent) HeldBits() (h uintptr) { // on US English QWERTY keyboards; see Key for details. // // If a key is pressed that is not supported by Key, ExtKey, -// or Modifiers, no KeyEvent will be produced. +// or Modifiers, no KeyEvent will be produced and package ui will behave as if false was returned from the event handler. type KeyEvent struct { // Key is a byte representing a character pressed // in the typewriter section of the keyboard. diff --git a/redo/area_windows.c b/redo/area_windows.c index e9ded14..ce538db 100644 --- a/redo/area_windows.c +++ b/redo/area_windows.c @@ -380,19 +380,10 @@ static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM heldButtons = (uintptr_t) GET_KEYSTATE_WPARAM(wParam); areaMouseEvent(hwnd, data, which, TRUE, heldButtons, lParam); return TRUE; - case WM_KEYDOWN: - areaKeyEvent(data, FALSE, wParam, lParam); - return 0; - case WM_KEYUP: - areaKeyEvent(data, TRUE, wParam, lParam); - return 0; - // Alt+[anything] and F10 send these instead and require us to return to DefWindowProc() so global keystrokes such as Alt+Tab can be processed - case WM_SYSKEYDOWN: - areaKeyEvent(data, FALSE, wParam, lParam); - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - case WM_SYSKEYUP: - areaKeyEvent(data, TRUE, wParam, lParam); - return DefWindowProcW(hwnd, uMsg, wParam, lParam); + case msgAreaKeyDown: + return (LRESULT) areaKeyEvent(data, FALSE, wParam, lParam); + case msgAreaKeyUp: + return (LRESULT) areaKeyEvent(data, TRUE, wParam, lParam); case msgAreaSizeChanged: adjustAreaScrollbars(hwnd, data); repaintArea(hwnd); // this calls for an update diff --git a/redo/area_windows.go b/redo/area_windows.go index 05f0274..877f8b6 100644 --- a/redo/area_windows.go +++ b/redo/area_windows.go @@ -152,7 +152,7 @@ func finishAreaMouseEvent(data unsafe.Pointer, cbutton C.DWORD, up C.BOOL, heldB } //export areaKeyEvent -func areaKeyEvent(data unsafe.Pointer, up C.BOOL, wParam C.WPARAM, lParam C.LPARAM) { +func areaKeyEvent(data unsafe.Pointer, up C.BOOL, wParam C.WPARAM, lParam C.LPARAM) C.BOOL { var ke KeyEvent a := (*area)(data) @@ -180,10 +180,14 @@ func areaKeyEvent(data unsafe.Pointer, up C.BOOL, wParam C.WPARAM, lParam C.LPAR ke.ExtKey = xke.ExtKey } else if ke.Modifiers == 0 { // no key, extkey, or modifiers; do nothing - return + return C.FALSE } ke.Up = up != C.FALSE - a.handler.Key(ke) + handled := a.handler.Key(ke) + if handled { + return C.TRUE + } + return C.FALSE } // all mappings come from GLFW - https://github.com/glfw/glfw/blob/master/src/win32_window.c#L152 diff --git a/redo/uitask_windows.c b/redo/uitask_windows.c index 5dad117..25f7da9 100644 --- a/redo/uitask_windows.c +++ b/redo/uitask_windows.c @@ -8,9 +8,31 @@ // this also assumes WC_TABCONTROL is longer than areaWindowClass #define NCLASSNAME (sizeof WC_TABCONTROL / sizeof WC_TABCONTROL[0]) -void uimsgloop_area(MSG *msg) +void uimsgloop_area(HWND active, HWND focus, MSG *msg) { + MSG copy; + + copy = *msg; + switch (copy.message) { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: // Alt+[anything] and F10 send these instead + copy.message = msgAreaKeyDown; + break; + case WM_KEYUP: + case WM_SYSKEYUP: + copy.message = msgAreaKeyUp; + break; + default: + goto notkey; + } + // if we handled the key, don't do the default behavior // don't call TranslateMessage(); we do our own keyboard handling + if (DispatchMessage(©) != FALSE) + return; + // TODO move this under notkey? + if (IsDialogMessage(active, msg) != 0) + return; +notkey: DispatchMessage(msg); } @@ -70,7 +92,7 @@ void uimsgloop(void) if (GetClassNameW(focus, classchk, NCLASSNAME) == 0) xpanic("error getting name of focused window class for Area check", GetLastError()); if (wcscmp(classchk, areaWindowClass) == 0) { - uimsgloop_area(&msg); + uimsgloop_area(active, focus, &msg); continue; } else if (wcscmp(classchk, WC_TABCONTROL) == 0) { uimsgloop_tab(active, focus, &msg); diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 5787331..c11dc7c 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -34,6 +34,8 @@ enum { msgTabCurrentTabHasChildren, msgBeginModal, msgEndModal, + msgAreaKeyDown, + msgAreaKeyUp, }; // uitask_windows.c diff --git a/redo/zz_test.go b/redo/zz_test.go index f3392ad..e8ccead 100644 --- a/redo/zz_test.go +++ b/redo/zz_test.go @@ -63,14 +63,16 @@ type testwin struct { wsmall Window } -type areaHandler struct{} +type areaHandler struct { + handled bool +} func (a *areaHandler) Paint(r image.Rectangle) *image.RGBA { i := image.NewRGBA(r) draw.Draw(i, r, &image.Uniform{color.RGBA{128,0,128,255}}, image.ZP, draw.Src) return i } func (a *areaHandler) Mouse(me MouseEvent) { fmt.Printf("%#v\n", me) } -func (a *areaHandler) Key(ke KeyEvent) { fmt.Printf("%#v %q\n", ke, ke.Key) } +func (a *areaHandler) Key(ke KeyEvent) bool { fmt.Printf("%#v %q\n", ke, ke.Key); return a.handled } func (tw *testwin) addfe() { tw.festart = NewButton("Start") @@ -169,7 +171,7 @@ func (tw *testwin) make(done chan struct{}) { tw.nt.Append("Tab 2", Space()) tw.t.Append("Tab", tw.nt) tw.t.Append("Space", Space()) - tw.a = NewArea(200, 200, &areaHandler{}) + tw.a = NewArea(200, 200, &areaHandler{false}) tw.t.Append("Area", tw.a) tw.spw = NewHorizontalStack( NewButton("hello"), @@ -233,7 +235,7 @@ func (tw *testwin) make(done chan struct{}) { NewVerticalStack( NewButton("Small"), NewButton("Small 2"), - NewArea(200, 200, &areaHandler{}))) + NewArea(200, 200, &areaHandler{true}))) tw.wsmall.Show() } }