summaryrefslogtreecommitdiff
path: root/new/windows/tab.c
diff options
context:
space:
mode:
Diffstat (limited to 'new/windows/tab.c')
-rw-r--r--new/windows/tab.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/new/windows/tab.c b/new/windows/tab.c
new file mode 100644
index 0000000..00fccb0
--- /dev/null
+++ b/new/windows/tab.c
@@ -0,0 +1,189 @@
+// 12 april 2015
+#include "uipriv_windows.h"
+
+// TODO
+// - tab change notifications aren't being sent on wine (anymore...? TODO)
+// - tell wine developers that tab controls do respond to parent changes on real windows (at least comctl6 tab controls do)
+
+struct tab {
+ uiParent **pages;
+ uintmax_t len;
+ uintmax_t cap;
+};
+
+static BOOL onWM_COMMAND(uiControl *c, WORD code, LRESULT *lResult)
+{
+ return FALSE;
+}
+
+// we have to handle hiding and showing of tab pages ourselves
+static BOOL onWM_NOTIFY(uiControl *c, NMHDR *nm, LRESULT *lResult)
+{
+ struct tab *t = (struct tab *) (c->data);
+ LRESULT n;
+
+ switch (nm->code) {
+ case TCN_SELCHANGING:
+ n = SendMessageW(uiControlHWND(c), TCM_GETCURSEL, 0, 0);
+ if (n != (LRESULT) (-1)) // if we're changing to a real tab
+ ShowWindow(uiParentHWND(t->pages[n]), SW_HIDE);
+ *lResult = FALSE; // and allow the change
+ return TRUE;
+ case TCN_SELCHANGE:
+ n = SendMessageW(uiControlHWND(c), TCM_GETCURSEL, 0, 0);
+ if (n != (LRESULT) (-1)) { // if we're changing to a real tab
+ ShowWindow(uiParentHWND(t->pages[n]), SW_SHOW);
+ // because we only resize the current child on resize, we'll need to trigger an update here
+ // don't call uiParentUpdate(); doing that won't size the content area (so we'll still have a 0x0 content area, for instance)
+ SendMessageW(uiControlHWND(c), msgUpdateChild, 0, 0);
+ }
+ *lResult = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void onWM_DESTROY(uiControl *c)
+{
+ struct tab *t = (struct tab *) (c->data);
+
+ // no need to worry about freeing the pages themselves; they'll destroy themselves after we return
+ uiFree(t->pages);
+ uiFree(t);
+}
+
+static void preferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height)
+{
+ // TODO
+}
+
+// common code for resizes
+static void resizeTab(uiControl *c, LONG width, LONG height)
+{
+ struct tab *t = (struct tab *) (c->data);
+ HWND hwnd;
+ LRESULT n;
+ RECT r;
+
+ hwnd = uiControlHWND(c);
+
+ n = SendMessageW(hwnd, TCM_GETCURSEL, 0, 0);
+ if (n == (LRESULT) (-1)) // no child selected; do nothing
+ return;
+
+ // make a rect at (0, 0) of the given window size
+ // this should give us the correct client coordinates
+ r.left = 0;
+ r.top = 0;
+ r.right = width;
+ r.bottom = height;
+ // convert to the display rectangle
+ SendMessageW(hwnd, TCM_ADJUSTRECT, FALSE, (LPARAM) (&r));
+
+ if (MoveWindow(uiParentHWND(t->pages[n]), r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE) == 0)
+ logLastError("error resizing current tab page in resizeTab()");
+}
+
+// and finally, because we have to resize parents, we have to handle resizes and updates
+static LRESULT CALLBACK tabSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
+{
+ uiControl *c = (uiControl *) dwRefData;
+ WINDOWPOS *wp = (WINDOWPOS *) lParam;
+ LRESULT lResult;
+ RECT r;
+
+ switch (uMsg) {
+ case WM_WINDOWPOSCHANGED:
+ if ((wp->flags & SWP_NOSIZE) != 0)
+ break;
+ // first, let the tab control handle it
+ lResult = (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
+ // we have the window rect width as part of the WINDOWPOS; resize
+ resizeTab(c, wp->cx, wp->cy);
+ return lResult;
+ case msgUpdateChild:
+ if (GetWindowRect(uiControlHWND(c), &r) == 0)
+ logLastError("error getting Tab window rect for synthesized resize message in tabSubProc()");
+ // these are in screen coordinates, which match what WM_WINDOWPOSCHANGED gave us (thanks TODOTODOTODOTODOTODOTODOTODO)
+ resizeTab(c, r.right - r.left, r.bottom - r.top);
+ return 0;
+ case WM_NCDESTROY:
+ if ((*fv_RemoveWindowSubclass)(hwnd, tabSubProc, uIdSubclass) == FALSE)
+ logLastError("error removing Tab resize handling subclass in tabSubProc()");
+ break;
+ }
+ return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam);
+}
+
+uiControl *uiNewTab(void)
+{
+ uiControl *c;
+ struct tab *t;
+ uiWindowsNewControlParams p;
+ HWND hwnd;
+
+ 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;
+ p.hInstance = hInstance;
+ p.useStandardControlFont = TRUE;
+ p.onWM_COMMAND = onWM_COMMAND;
+ p.onWM_NOTIFY = onWM_NOTIFY;
+ p.onWM_DESTROY = onWM_DESTROY;
+ c = uiWindowsNewControl(&p);
+
+ c->preferredSize = preferredSize;
+
+ t = uiNew(struct tab);
+ c->data = t;
+
+ hwnd = uiControlHWND(c);
+ if ((*fv_SetWindowSubclass)(hwnd, tabSubProc, 0, (DWORD_PTR) c) == FALSE)
+ logLastError("error subclassing Tab to give it its own resize handler in uiNewTab()");
+
+ return c;
+}
+
+#define tabCapGrow 32
+
+void uiTabAddPage(uiControl *c, const char *name, uiControl *child)
+{
+ struct tab *t = (struct tab *) (c->data);
+ HWND hwnd;
+ TCITEMW item;
+ LRESULT n;
+ uiParent *parent;
+ WCHAR *wname;
+
+ if (t->len >= t->cap) {
+ t->cap += tabCapGrow;
+ t->pages = (uiParent **) uiRealloc(t->pages, t->cap * sizeof (uiParent *), "uiParent *[]");
+ }
+
+ hwnd = uiControlHWND(c);
+ n = SendMessageW(hwnd, TCM_GETITEMCOUNT, 0, 0);
+
+ parent = uiNewParent((uintptr_t) hwnd);
+ uiParentSetChild(parent, child);
+ uiParentUpdate(parent);
+ if (n != 0) // if this isn't the first page, we have to hide the other controls
+ ShowWindow(uiParentHWND(parent), SW_HIDE);
+ t->pages[t->len] = parent;
+ t->len++;
+
+ ZeroMemory(&item, sizeof (TCITEMW));
+ item.mask = TCIF_TEXT;
+ wname = toUTF16(name);
+ item.pszText = wname;
+ // MSDN's example code uses the first invalid index directly for this
+ if (SendMessageW(hwnd, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item)) == (LRESULT) -1)
+ logLastError("error adding tab to Tab in uiTabAddPage()");
+ uiFree(wname);
+
+ // if this is the first tab, Windows will automatically show it /without/ sending a TCN_SELCHANGE notification
+ // (TODO verify that)
+ // so we need to manually resize the tab ourselves
+ // don't use uiUpdateParent() for the same reason as in the TCN_SELCHANGE handler
+ SendMessageW(uiControlHWND(c), msgUpdateChild, 0, 0);
+}