Implemented tab navigation across tabs on Windows.

This commit is contained in:
Pietro Gagliardi 2015-05-06 18:37:21 -04:00
parent c5e5fbd909
commit 329b4f29dd
5 changed files with 68 additions and 17 deletions

View File

@ -66,7 +66,7 @@ enum {
uiWindowsSysFuncContainerDisable,
// This is interpreted by controls that are tab stops; the control should set HasTabStops to TRUE if so, and *LEAVE IT ALONE* if not.
// You only need this if implementing your own uiControl.
// Controls created with uiWindowsMakeControl() check for the presence of WS_TABSTOP.
// Controls created with uiWindowsMakeControl() check for the window being enabled and the presence of WS_TABSTOP.
// The name is "has tab stops" because it is used by uiTabs to say "does the current tab page have tab stops?".
uiWindowsSysFuncHasTabStops,
};

View File

@ -3,7 +3,25 @@
// #qo LDFLAGS: -luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 -lcomdlg32 -lole32 -loleaut32 -loleacc -luuid
static void uimsgloop_else(MSG *msg)
static void msgloop_tab(HWND active, HWND focus, MSG *msg)
{
BOOL hasChildren;
BOOL idm;
// THIS BIT IS IMPORTANT: if the current tab has no children, then there will be no children left in the dialog to tab to, and IsDialogMessageW() will loop forever
hasChildren = SendMessageW(focus, msgHasTabStops, 0, 0);
if (hasChildren)
tabEnterTabNavigation(focus);
idm = IsDialogMessageW(active, msg);
if (hasChildren)
tabLeaveTabNavigation(focus);
if (idm != 0)
return;
TranslateMessage(msg);
DispatchMessage(msg);
}
static void msgloop_else(MSG *msg)
{
TranslateMessage(msg);
DispatchMessage(msg);
@ -24,7 +42,7 @@ void uiMain(void)
break;
active = GetActiveWindow();
if (active == NULL) {
uimsgloop_else(&msg);
msgloop_else(&msg);
continue;
}
@ -33,20 +51,20 @@ void uiMain(void)
// 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
focus = GetFocus();
if (focus != NULL) {
/*TODO switch (windowClassOf(focus, areaWindowClass, WC_TABCONTROLW, NULL)) {
case 0: // areaWindowClass
uimsgloop_area(active, focus, &msg);
switch (windowClassOf(focus, L"TODO Area not yet implemented", WC_TABCONTROLW, NULL)) {
case 0: // uiArea
// msgloop_area(active, focus, &msg);
continue;
case 1: // WC_TABCONTROLW
uimsgloop_tab(active, focus, &msg);
msgloop_tab(active, focus, &msg);
continue;
}
// else fall through
*/ }
}
if (IsDialogMessage(active, &msg) != 0)
continue;
uimsgloop_else(&msg);
msgloop_else(&msg);
}
}

View File

@ -19,7 +19,6 @@ static void singleDestroy(uiControl *c)
if (s->parent != NULL)
complain("attempt to destroy a uiControl at %p while it still has a parent", c);
SendMessageW(s->hwnd, msgCanDestroyNow, 0, 0);
(*(s->onDestroy))(s->onDestroyData);
if (DestroyWindow(s->hwnd) == 0)
logLastError("error destroying control in singleDestroy()");
@ -119,8 +118,9 @@ static void singleSysFunc(uiControl *c, uiControlSysFuncParams *p)
EnableWindow(s->hwnd, FALSE);
return;
case uiWindowsSysFuncHasTabStops:
if ((getStyle(s->hwnd) & WS_TABSTOP) != 0)
p->HasTabStops = TRUE;
if (IsWindowEnabled(s->hwnd) != 0)
if ((getStyle(s->hwnd) & WS_TABSTOP) != 0)
p->HasTabStops = TRUE;
return;
}
complain("unknown p->Func %d in singleSysFunc()", p->Func);

View File

@ -133,9 +133,11 @@ static void tabSysFunc(uiControl *c, uiControlSysFuncParams *p)
// we handle tab stops specially
if (p->Func == uiWindowsSysFuncHasTabStops) {
// if there are no tabs, it is not a tab stop
if (t->pages->len != 0)
p->HasTabStops = TRUE;
// if disabled, not a tab stop
if (IsWindowEnabled(t->hwnd) != 0)
// if there are no tabs, it is not a tab stop
if (t->pages->len != 0)
p->HasTabStops = TRUE;
return;
}
// otherwise distribute it throughout all pages
@ -181,6 +183,9 @@ static LRESULT CALLBACK tabSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
WINDOWPOS *wp = (WINDOWPOS *) lParam;
LRESULT lResult;
RECT r;
LRESULT n;
uiControlSysFuncParams p;
struct tabPage *page;
switch (uMsg) {
case WM_WINDOWPOSCHANGED:
@ -197,6 +202,15 @@ static LRESULT CALLBACK tabSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM l
// these are in screen coordinates, which match what WM_WINDOWPOSCHANGED gave us (see http://stackoverflow.com/questions/29598334/are-the-coordinates-in-windowpos-on-wm-windowposchanged-in-parent-coordinates-or)
resizeTab(t, r.right - r.left, r.bottom - r.top);
return 0;
case msgHasTabStops:
n = SendMessageW(t->hwnd, TCM_GETCURSEL, 0, 0);
if (n == (LRESULT) (-1)) // no current selection == no tab stops
return FALSE;
p.Func = uiWindowsSysFuncHasTabStops;
p.HasTabStops = FALSE;
page = ptrArrayIndex(t->pages, struct tabPage *, n);
uiControlSysFunc(uiControl(page->bin), &p);
return p.HasTabStops;
case WM_NCDESTROY:
if ((*fv_RemoveWindowSubclass)(hwnd, tabSubProc, uIdSubclass) == FALSE)
logLastError("error removing Tab resize handling subclass in tabSubProc()");
@ -332,7 +346,7 @@ uiTab *uiNewTab(void)
p.dwExStyle = 0; // don't set WS_EX_CONTROLPARENT yet; we do that dynamically in the message loop (see main_windows.c)
p.lpClassName = WC_TABCONTROLW;
p.lpWindowName = L"";
p.dwStyle = TCS_TOOLTIPS | WS_TABSTOP; // start with this; we will alternate between this and WS_EX_CONTROLPARENT as needed (see main.c and msgHasTabStops above)
p.dwStyle = TCS_TOOLTIPS | WS_TABSTOP; // start with this; we will alternate between this and WS_EX_CONTROLPARENT as needed (see main.c and msgHasTabStops above and the toggling functions below)
p.hInstance = hInstance;
p.useStandardControlFont = TRUE;
p.onWM_COMMAND = onWM_COMMAND;
@ -364,3 +378,18 @@ uiTab *uiNewTab(void)
return uiTab(t);
}
// unfortunately WS_TABSTOP and WS_EX_CONTROLPARENT are mutually exclusive, so we have to toggle between them
// see main.c for more details
void tabEnterTabNavigation(HWND hwnd)
{
setStyle(hwnd, getStyle(hwnd) & ~WS_TABSTOP);
setExStyle(hwnd, getExStyle(hwnd) | WS_EX_CONTROLPARENT);
}
void tabLeaveTabNavigation(HWND hwnd)
{
setExStyle(hwnd, getExStyle(hwnd) & ~WS_EX_CONTROLPARENT);
setStyle(hwnd, getStyle(hwnd) | WS_TABSTOP);
}

View File

@ -35,7 +35,7 @@ enum {
msgCOMMAND = WM_APP + 0x40, // start offset just to be safe
msgNOTIFY,
msgUpdateChild, // fake because Windows seems to SWP_NOSIZE MoveWindow()s and SetWindowPos()s that don't change the window size (even if SWP_NOSIZE isn't specified)
msgCanDestroyNow,
msgHasTabStops,
};
// debug.c
@ -83,3 +83,7 @@ extern void freeMenubar(HMENU);
// alloc.c
extern int initAlloc(void);
// tab.c
extern void tabEnterTabNavigation(HWND);
extern void tabLeaveTabNavigation(HWND);