diff options
author | StapleButter <thetotalworm@gmail.com> | 2017-09-09 02:30:51 +0200 |
---|---|---|
committer | StapleButter <thetotalworm@gmail.com> | 2017-09-09 02:30:51 +0200 |
commit | 70e4841d311d68689724768157cc9cbfbde7a9fc (patch) | |
tree | ba9499f77d1258530a7e60aa6e1732c41d98161c /src/libui_sdl/libui/windows/tab.cpp | |
parent | 81747d6c34eb159481a6ca3f283d065fa3568617 (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.cpp | 287 |
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; +} |