From 2c8bb7bc6d3b5614d53de957b4dca025c53a9cee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 14 Aug 2014 11:30:48 -0400 Subject: [PATCH] Fixed Tabs on Windows having undesirable tab stop behavior. This doesn't work correctly in wine, and I can confirm for the first time that it actually is a bug in wine! --- redo/tab_windows.c | 24 ++++++++++++++++++++++++ redo/tab_windows.go | 4 +--- redo/uitask_windows.c | 35 ++++++++++++++++++++++++----------- redo/winapi_windows.h | 2 ++ 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/redo/tab_windows.c b/redo/tab_windows.c index 791e115..4958a68 100644 --- a/redo/tab_windows.c +++ b/redo/tab_windows.c @@ -80,3 +80,27 @@ LONG tabGetTabHeight(HWND hwnd) } return tallest; } + +void tabEnterChildren(HWND hwnd) +{ + DWORD style, xstyle; + + style = (DWORD) GetWindowLongPtrW(hwnd, GWL_STYLE); + xstyle = (DWORD) GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + style &= ~((DWORD) WS_TABSTOP); + xstyle |= WS_EX_CONTROLPARENT; + SetWindowLongPtrW(hwnd, GWL_STYLE, (LONG_PTR) style); + SetWindowLongPtrW(hwnd, GWL_EXSTYLE, (LONG_PTR) xstyle); +} + +void tabLeaveChildren(HWND hwnd) +{ + DWORD style, xstyle; + + style = (DWORD) GetWindowLongPtrW(hwnd, GWL_STYLE); + xstyle = (DWORD) GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + style |= WS_TABSTOP; + xstyle &= ~((DWORD) WS_EX_CONTROLPARENT); + SetWindowLongPtrW(hwnd, GWL_STYLE, (LONG_PTR) style); + SetWindowLongPtrW(hwnd, GWL_EXSTYLE, (LONG_PTR) xstyle); +} diff --git a/redo/tab_windows.go b/redo/tab_windows.go index 43946b7..1d22887 100644 --- a/redo/tab_windows.go +++ b/redo/tab_windows.go @@ -23,9 +23,7 @@ type tab struct { func newTab() Tab { hwnd := C.newControl(C.xWC_TABCONTROL, C.TCS_TOOLTIPS | C.WS_TABSTOP, - // this is needed to have the tab contents be tab stop - // TODO this seems to override WS_TABSTOP; it seems I have to undo making the containers children - http://stackoverflow.com/questions/1153981/tab-order-in-tab-control-with-nested-dialogs-ws-ex-controlparent - C.WS_EX_CONTROLPARENT) + 0) // don't set WS_EX_CONTROLPARENT here; see uitask_windows.c t := &tab{ _hwnd: hwnd, } diff --git a/redo/uitask_windows.c b/redo/uitask_windows.c index 85b077d..3101452 100644 --- a/redo/uitask_windows.c +++ b/redo/uitask_windows.c @@ -4,15 +4,18 @@ #include "_cgo_export.h" // note that this includes the terminating '\0' -#define NAREACLASS (sizeof areaWindowClass / sizeof areaWindowClass[0]) +// this also assumes WC_TABCONTROL is longer than areaWindowClass +#define NCLASSNAME (sizeof WC_TABCONTROL / sizeof WC_TABCONTROL[0]) void uimsgloop(void) { MSG msg; int res; - HWND active; - WCHAR classchk[NAREACLASS]; + HWND active, focus; + WCHAR classchk[NCLASSNAME]; BOOL dodlgmessage; + BOOL istab; + BOOL idm; for (;;) { SetLastError(0); @@ -23,21 +26,31 @@ void uimsgloop(void) break; active = GetActiveWindow(); if (active != NULL) { - HWND focus; - + // bit of logic involved here: + // we don't want dialog messages passed into Areas, so we don't call IsDialogMessageW() there + // as for Tabs, we can't have both WS_TABSTOP and WS_EX_CONTROLPARENT set at the same time, so we hotswap the two styles to get the behavior we want + // theoretically we could use the class atom to avoid a wcscmp() + // however, raymond chen advises against this - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/11/240744.aspx (and we're not in control of the Tab class, before you say anything) + // we could also theoretically just send msgAreaDefocuses directly, but what DefWindowProc() does to a WM_APP message is undocumented dodlgmessage = TRUE; + istab = FALSE; focus = GetFocus(); if (focus != NULL) { - // theoretically we could use the class atom to avoid a wcscmp() - // however, raymond chen advises against this - http://blogs.msdn.com/b/oldnewthing/archive/2004/10/11/240744.aspx - // while I have no idea what could deregister *my* window class from under *me*, better safe than sorry - // we could also theoretically just send msgAreaDefocuses directly, but what DefWindowProc() does to a WM_APP message is undocumented - if (GetClassNameW(focus, classchk, NAREACLASS) == 0) + if (GetClassNameW(focus, classchk, NCLASSNAME) == 0) xpanic("error getting name of focused window class for Area check", GetLastError()); if (wcscmp(classchk, areaWindowClass) == 0) dodlgmessage = FALSE; + else if (wcscmp(classchk, WC_TABCONTROL) == 0) + istab = TRUE; } - if (dodlgmessage && IsDialogMessageW(active, &msg) != 0) + if (istab) + tabEnterChildren(focus); + // TODO this goes into an infinite loop on a blank tab + if (dodlgmessage) + idm = IsDialogMessageW(active, &msg); + if (istab) + tabLeaveChildren(focus); + if (idm != 0) continue; } TranslateMessage(&msg); diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 78c5e39..c063679 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -90,6 +90,8 @@ extern void setTabSubclass(HWND, void *); extern void tabAppend(HWND, LPWSTR); extern void tabGetContentRect(HWND, RECT *); extern LONG tabGetTabHeight(HWND); +extern void tabEnterChildren(HWND); +extern void tabLeaveChildren(HWND); // table_windows.go extern LPWSTR xWC_LISTVIEW;