Implemented tab navigation across tabs on Windows.
This commit is contained in:
parent
c5e5fbd909
commit
329b4f29dd
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,6 +118,7 @@ static void singleSysFunc(uiControl *c, uiControlSysFuncParams *p)
|
|||
EnableWindow(s->hwnd, FALSE);
|
||||
return;
|
||||
case uiWindowsSysFuncHasTabStops:
|
||||
if (IsWindowEnabled(s->hwnd) != 0)
|
||||
if ((getStyle(s->hwnd) & WS_TABSTOP) != 0)
|
||||
p->HasTabStops = TRUE;
|
||||
return;
|
||||
|
|
|
@ -133,6 +133,8 @@ static void tabSysFunc(uiControl *c, uiControlSysFuncParams *p)
|
|||
|
||||
// we handle tab stops specially
|
||||
if (p->Func == uiWindowsSysFuncHasTabStops) {
|
||||
// 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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue