summaryrefslogtreecommitdiff
path: root/new/tab_windows.c
diff options
context:
space:
mode:
authorPietro Gagliardi <[email protected]>2015-04-12 03:16:11 -0400
committerPietro Gagliardi <[email protected]>2015-04-12 03:16:11 -0400
commit0ee55d2d2da1a47fb4bbe5f62144e823d07efcd1 (patch)
tree9c23838e37a436e87996c095ce6871446e344db7 /new/tab_windows.c
parenta001448e63963ac0a70932abbf0fdc3738d972c9 (diff)
Implemented uiTab on Windows.
Diffstat (limited to 'new/tab_windows.c')
-rw-r--r--new/tab_windows.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/new/tab_windows.c b/new/tab_windows.c
new file mode 100644
index 0000000..a4696d2
--- /dev/null
+++ b/new/tab_windows.c
@@ -0,0 +1,189 @@
+// 12 april 2015
+#include "uipriv_windows.h"
+
+struct tab {
+ struct tabPage *pages;
+ uintmax_t len;
+ uintmax_t cap;
+};
+
+struct tabPage {
+ uiControl *child;
+};
+
+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((HWND) uiControlHandle(c), TCM_GETCURSEL, 0, 0);
+ if (n != (LRESULT) (-1)) // if we're changing to a real tab
+ uiControlContainerHide(t->pages[n].child);
+ *lResult = FALSE; // and allow the change
+ return TRUE;
+ case TCN_SELCHANGE:
+ n = SendMessageW((HWND) uiControlHandle(c), TCM_GETCURSEL, 0, 0);
+ if (n != (LRESULT) (-1)) { // if we're changing to a real tab
+ uiControlContainerShow(t->pages[n].child);
+ // because we only resize the current child on resize, we'll need to trigger an update here
+ updateParent(uiControlHandle(c));
+ }
+ *lResult = 0;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void onWM_DESTROY(uiControl *c)
+{
+ struct tab *t = (struct tab *) (c->data);
+
+ 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, margin;
+
+ hwnd = (HWND) uiControlHandle(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));
+
+ margin.left = 0;
+ margin.top = 0;
+ margin.right = 0;
+ margin.bottom = 0;
+/*TODO if (w->margined) {
+ margin.left = windowMargin;
+ margin.top = windowMargin;
+ margin.right = windowMargin;
+ margin.bottom = windowMargin;
+ }
+*/ resize(t->pages[n].child, hwnd, r, margin);
+}
+
+// and finally, because we are a container, 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;
+ LRESULT lResult;
+ WINDOWPOS *wp = (WINDOWPOS *) lParam;
+ RECT r;
+
+ if (sharedWndProc(hwnd, uMsg, wParam, lParam, &lResult) != FALSE)
+ return lResult;
+ 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((HWND) uiControlHandle(c), &r) == 0)
+ logLastError("error getting window rect for Tab resize in tabSubProc()");
+ // though these are in screen coordinates, we only need the width and height
+ 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 = (HWND) uiControlHandle(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;
+ WCHAR *wname;
+
+ if (t->len >= t->cap) {
+ t->cap += tabCapGrow;
+ t->pages = (struct tabPage *) uiRealloc(t->pages, t->cap * sizeof (struct tabPage), "struct tabPage[]");
+ }
+
+ hwnd = (HWND) uiControlHandle(c);
+ n = SendMessageW(hwnd, TCM_GETITEMCOUNT, 0, 0);
+
+ t->pages[t->len].child = child;
+ uiControlSetParent(t->pages[t->len].child, (uintptr_t) hwnd);
+ if (n != 0) // if this isn't the first page, we have to hide the other controls
+ uiControlContainerHide(t->pages[t->len].child);
+ 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);
+}