aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/windows/tab.cpp
diff options
context:
space:
mode:
authorStapleButter <thetotalworm@gmail.com>2017-09-09 02:30:51 +0200
committerStapleButter <thetotalworm@gmail.com>2017-09-09 02:30:51 +0200
commit70e4841d311d68689724768157cc9cbfbde7a9fc (patch)
treeba9499f77d1258530a7e60aa6e1732c41d98161c /src/libui_sdl/libui/windows/tab.cpp
parent81747d6c34eb159481a6ca3f283d065fa3568617 (diff)
another UI attempt, I guess.
sorry.
Diffstat (limited to 'src/libui_sdl/libui/windows/tab.cpp')
-rw-r--r--src/libui_sdl/libui/windows/tab.cpp287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/libui_sdl/libui/windows/tab.cpp b/src/libui_sdl/libui/windows/tab.cpp
new file mode 100644
index 0000000..365f5a1
--- /dev/null
+++ b/src/libui_sdl/libui/windows/tab.cpp
@@ -0,0 +1,287 @@
+// 16 may 2015
+#include "uipriv_windows.hpp"
+
+// You don't add controls directly to a tab control on Windows; instead you make them siblings and swap between them on a TCN_SELCHANGING/TCN_SELCHANGE notification pair.
+// In addition, you use dialogs because they can be textured properly; other controls cannot. (Things will look wrong if the tab background in the current theme is fancy if you just use the tab background by itself; see http://stackoverflow.com/questions/30087540/why-are-my-programss-tab-controls-rendering-their-background-in-a-blocky-way-b.)
+
+struct uiTab {
+ uiWindowsControl c;
+ HWND hwnd; // of the outer container
+ HWND tabHWND; // of the tab control itself
+ std::vector<struct tabPage *> *pages;
+ HWND parent;
+};
+
+// utility functions
+
+static LRESULT curpage(uiTab *t)
+{
+ return SendMessageW(t->tabHWND, TCM_GETCURSEL, 0, 0);
+}
+
+static struct tabPage *tabPage(uiTab *t, int i)
+{
+ return (*(t->pages))[i];
+}
+
+static void tabPageRect(uiTab *t, RECT *r)
+{
+ // this rect needs to be in parent window coordinates, but TCM_ADJUSTRECT wants a window rect, which is screen coordinates
+ // because we have each page as a sibling of the tab, use the tab's own rect as the input rect
+ uiWindowsEnsureGetWindowRect(t->tabHWND, r);
+ SendMessageW(t->tabHWND, TCM_ADJUSTRECT, (WPARAM) FALSE, (LPARAM) r);
+ // and get it in terms of the container instead of the screen
+ mapWindowRect(NULL, t->hwnd, r);
+}
+
+static void tabRelayout(uiTab *t)
+{
+ struct tabPage *page;
+ RECT r;
+
+ // first move the tab control itself
+ uiWindowsEnsureGetClientRect(t->hwnd, &r);
+ uiWindowsEnsureMoveWindowDuringResize(t->tabHWND, r.left, r.top, r.right - r.left, r.bottom - r.top);
+
+ // then the current page
+ if (t->pages->size() == 0)
+ return;
+ page = tabPage(t, curpage(t));
+ tabPageRect(t, &r);
+ uiWindowsEnsureMoveWindowDuringResize(page->hwnd, r.left, r.top, r.right - r.left, r.bottom - r.top);
+}
+
+static void showHidePage(uiTab *t, LRESULT which, int hide)
+{
+ struct tabPage *page;
+
+ if (which == (LRESULT) (-1))
+ return;
+ page = tabPage(t, which);
+ if (hide)
+ ShowWindow(page->hwnd, SW_HIDE);
+ else {
+ ShowWindow(page->hwnd, SW_SHOW);
+ // we only resize the current page, so we have to resize it; before we can do that, we need to make sure we are of the right size
+ uiWindowsControlMinimumSizeChanged(uiWindowsControl(t));
+ }
+}
+
+// control implementation
+
+static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult)
+{
+ uiTab *t = uiTab(c);
+
+ if (nm->code != TCN_SELCHANGING && nm->code != TCN_SELCHANGE)
+ return FALSE;
+ showHidePage(t, curpage(t), nm->code == TCN_SELCHANGING);
+ *lResult = 0;
+ if (nm->code == TCN_SELCHANGING)
+ *lResult = FALSE;
+ return TRUE;
+}
+
+static void uiTabDestroy(uiControl *c)
+{
+ uiTab *t = uiTab(c);
+ uiControl *child;
+
+ for (struct tabPage *&page : *(t->pages)) {
+ child = page->child;
+ tabPageDestroy(page);
+ if (child != NULL) {
+ uiControlSetParent(child, NULL);
+ uiControlDestroy(child);
+ }
+ }
+ delete t->pages;
+ uiWindowsUnregisterWM_NOTIFYHandler(t->tabHWND);
+ uiWindowsEnsureDestroyWindow(t->tabHWND);
+ uiWindowsEnsureDestroyWindow(t->hwnd);
+ uiFreeControl(uiControl(t));
+}
+
+uiWindowsControlDefaultHandle(uiTab)
+uiWindowsControlDefaultParent(uiTab)
+uiWindowsControlDefaultSetParent(uiTab)
+uiWindowsControlDefaultToplevel(uiTab)
+uiWindowsControlDefaultVisible(uiTab)
+uiWindowsControlDefaultShow(uiTab)
+uiWindowsControlDefaultHide(uiTab)
+uiWindowsControlDefaultEnabled(uiTab)
+uiWindowsControlDefaultEnable(uiTab)
+uiWindowsControlDefaultDisable(uiTab)
+
+static void uiTabSyncEnableState(uiWindowsControl *c, int enabled)
+{
+ uiTab *t = uiTab(c);
+
+ if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(t), enabled))
+ return;
+ EnableWindow(t->tabHWND, enabled);
+ for (struct tabPage *&page : *(t->pages))
+ if (page->child != NULL)
+ uiWindowsControlSyncEnableState(uiWindowsControl(page->child), enabled);
+}
+
+uiWindowsControlDefaultSetParentHWND(uiTab)
+
+static void uiTabMinimumSize(uiWindowsControl *c, int *width, int *height)
+{
+ uiTab *t = uiTab(c);
+ int pagewid, pageht;
+ struct tabPage *page;
+ RECT r;
+
+ // only consider the current page
+ pagewid = 0;
+ pageht = 0;
+ if (t->pages->size() != 0) {
+ page = tabPage(t, curpage(t));
+ tabPageMinimumSize(page, &pagewid, &pageht);
+ }
+
+ r.left = 0;
+ r.top = 0;
+ r.right = pagewid;
+ r.bottom = pageht;
+ // this also includes the tabs themselves
+ SendMessageW(t->tabHWND, TCM_ADJUSTRECT, (WPARAM) TRUE, (LPARAM) (&r));
+ *width = r.right - r.left;
+ *height = r.bottom - r.top;
+}
+
+static void uiTabMinimumSizeChanged(uiWindowsControl *c)
+{
+ uiTab *t = uiTab(c);
+
+ if (uiWindowsControlTooSmall(uiWindowsControl(t))) {
+ uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(t));
+ return;
+ }
+ tabRelayout(t);
+}
+
+uiWindowsControlDefaultLayoutRect(uiTab)
+uiWindowsControlDefaultAssignControlIDZOrder(uiTab)
+
+static void uiTabChildVisibilityChanged(uiWindowsControl *c)
+{
+ // TODO eliminate the redundancy
+ uiWindowsControlMinimumSizeChanged(c);
+}
+
+static void tabArrangePages(uiTab *t)
+{
+ LONG_PTR controlID = 100;
+ HWND insertAfter = NULL;
+
+ // TODO is this first or last?
+ uiWindowsEnsureAssignControlIDZOrder(t->tabHWND, &controlID, &insertAfter);
+ for (struct tabPage *&page : *(t->pages))
+ uiWindowsEnsureAssignControlIDZOrder(page->hwnd, &controlID, &insertAfter);
+}
+
+void uiTabAppend(uiTab *t, const char *name, uiControl *child)
+{
+ uiTabInsertAt(t, name, t->pages->size(), child);
+}
+
+void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child)
+{
+ struct tabPage *page;
+ LRESULT hide, show;
+ TCITEMW item;
+ WCHAR *wname;
+
+ // see below
+ hide = curpage(t);
+
+ if (child != NULL)
+ uiControlSetParent(child, uiControl(t));
+
+ page = newTabPage(child);
+ uiWindowsEnsureSetParentHWND(page->hwnd, t->hwnd);
+ t->pages->insert(t->pages->begin() + n, page);
+ tabArrangePages(t);
+
+ ZeroMemory(&item, sizeof (TCITEMW));
+ item.mask = TCIF_TEXT;
+ wname = toUTF16(name);
+ item.pszText = wname;
+ if (SendMessageW(t->tabHWND, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item)) == (LRESULT) -1)
+ logLastError(L"error adding tab to uiTab");
+ uiFree(wname);
+
+ // we need to do this because adding the first tab doesn't send a TCN_SELCHANGE; it just shows the page
+ show = curpage(t);
+ if (show != hide) {
+ showHidePage(t, hide, 1);
+ showHidePage(t, show, 0);
+ }
+}
+
+void uiTabDelete(uiTab *t, int n)
+{
+ struct tabPage *page;
+
+ // first delete the tab from the tab control
+ // if this is the current tab, no tab will be selected, which is good
+ if (SendMessageW(t->tabHWND, TCM_DELETEITEM, (WPARAM) n, 0) == FALSE)
+ logLastError(L"error deleting uiTab tab");
+
+ // now delete the page itself
+ page = tabPage(t, n);
+ if (page->child != NULL)
+ uiControlSetParent(page->child, NULL);
+ tabPageDestroy(page);
+ t->pages->erase(t->pages->begin() + n);
+}
+
+int uiTabNumPages(uiTab *t)
+{
+ return t->pages->size();
+}
+
+int uiTabMargined(uiTab *t, int n)
+{
+ return tabPage(t, n)->margined;
+}
+
+void uiTabSetMargined(uiTab *t, int n, int margined)
+{
+ struct tabPage *page;
+
+ page = tabPage(t, n);
+ page->margined = margined;
+ // even if the page doesn't have a child it might still have a new minimum size with margins; this is the easiest way to verify it
+ uiWindowsControlMinimumSizeChanged(uiWindowsControl(t));
+}
+
+static void onResize(uiWindowsControl *c)
+{
+ tabRelayout(uiTab(c));
+}
+
+uiTab *uiNewTab(void)
+{
+ uiTab *t;
+
+ uiWindowsNewControl(uiTab, t);
+
+ t->hwnd = uiWindowsMakeContainer(uiWindowsControl(t), onResize);
+
+ t->tabHWND = uiWindowsEnsureCreateControlHWND(0,
+ WC_TABCONTROLW, L"",
+ TCS_TOOLTIPS | WS_TABSTOP,
+ hInstance, NULL,
+ TRUE);
+ uiWindowsEnsureSetParentHWND(t->tabHWND, t->hwnd);
+
+ uiWindowsRegisterWM_NOTIFYHandler(t->tabHWND, onWM_NOTIFY, uiControl(t));
+
+ t->pages = new std::vector<struct tabPage *>;
+
+ return t;
+}