diff --git a/redo/windows/child.c b/redo/windows/child.c index 4090ce01..8f871234 100644 --- a/redo/windows/child.c +++ b/redo/windows/child.c @@ -7,11 +7,13 @@ struct child { uiControl *c; HWND hwnd; - HWND parent; + // This is a helper for uiTab pages. + // For visual accuracy of tab page backgrounds, margins are also handled here, applied to the child only (rather than applied to the whole tab page). + HWND tabpage; + int margined; // This flag is for users of these functions. // For uiBox, this is "spaced". - // For uiTab, this is "margined". (uiGroup and uiWindow have to maintain their margined state themselves, since the margined state is independent of whether there is a child for those two.) int flag; }; @@ -28,15 +30,27 @@ struct child *newChild(uiControl *child, uiControl *parent, HWND parentHWND) uiControlSetParent(c->c, parent); uiWindowsEnsureSetParent(c->hwnd, parentHWND); - c->parent = parentHWND; return c; } +struct child *newChildWithTabPage(uiControl *child, uiControl *parent, HWND parentHWND) +{ + struct child *c; + HWND tabpage; + + tabpage = newTabPage(); + c = newChild(child, parent, tabpage); + uiWindowsEnsureSetParent(tabpage, parentHWND); + return c; +} + void childRemove(struct child *c) { uiWindowsEnsureSetParent(c->hwnd, utilwin); uiControlSetParent(c->c, NULL); + if (c->tabpage != NULL) + uiWindowsEnsureDestroyWindow(c->tabpage); uiFree(c); } @@ -57,15 +71,34 @@ HWND childHWND(struct child *c) void childMinimumSize(struct child *c, uiWindowsSizing *d, intmax_t *width, intmax_t *height) { uiWindowsControl *wc; + intmax_t left, top, right, bottom wc = uiWindowsControl(c->c); (*(wc->MinimumSize))(wc, d, width, height); + if (c->tabpage != NULL && c->margined) { + tabPageMargins(c->tabpage, &left, &top, &right, &bottom); + *width += left + right; + *height += top + bottom; + } } void childRelayout(struct child *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height) { uiWindowsControl *wc; + intmax_t left, top, right, bottom; + if (c->tabpage != NULL) { + uiWindowsEnsureMoveWindow(c->tabpage, x, y, width, height); + x = 0; // and make relative to the client rect of the tab page + y = 0; + if (c->margined) { + tabPageMargins(c->tabpage, &left, &top, &right, &bottom); + x += left; + y += top; + width -= left + right; + height -= top + bottom; + } + } wc = uiWindowsControl(c->c); (*(wc->Relayout))(wc, x, y, width, height); } @@ -75,6 +108,22 @@ void childUpdateState(struct child *c) controlUpdateState(c->c); } +HWND childTabPage(struct child *c) +{ + return c->tabpage; +} + +int childMargined(struct child *c) +{ + return c->margined; +} + +void childSetMargined(struct child *c) +{ + c->margined = margined; + uiControlQueueResize(c->c); +} + int childFlag(struct child *c) { return c->flag; diff --git a/redo/windows/tabpage.c b/redo/windows/tabpage.c new file mode 100644 index 00000000..c3675f12 --- /dev/null +++ b/redo/windows/tabpage.c @@ -0,0 +1,62 @@ +// 30 may 2015 +#include "uipriv_windows.h" + +// This just defines the implementation of the tab page HWND itself. +// The actual work of hosting a tab page's control is in child.c. + +// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx +#define tabMargin 7 + +static void tabPageMargins(HWND hwnd, intmax_t *left, intmax_t *right, intmax_t *right, intmax_t *bottom) +{ + uiWindowsSizing *d; + + d = uiWindowsNewSizing(hwnd); + *left = uiWindowsDlgUnitsToX(tabMargin, d->BaseX); + *top = uiWindowsDlgUnitsToY(tabMargin, d->BaseY); + *right = *left; + *bottom = *top; + uiWindowsFreeSizing(d); +} + +// dummy dialog procedure; see below for details +// let's handle parent messages here to avoid needing to subclass +static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT lResult; + + if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) { + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONG_PTR) lResult); + return TRUE; + } + // unthemed dialogs don't respond to WM_PRINTCLIENT + // fortunately they don't have any special painting + if (uMsg == WM_PRINTCLIENT) { + // don't worry about the return value; hopefully DefWindowProcW() caught it (if not the dialog procedure itself) + // we COULD paint the dialog background brush ourselves but meh, it works + SendMessageW(hwnd, WM_ERASEBKGND, wParam, lParam); + // and pretend we did nothing just so the themed dialog can still paint its content + return FALSE; + } + if (uMsg == WM_INITDIALOG) + return TRUE; + return FALSE; +} + +HWND newTabPage(void) +{ + HWND hwnd; + HRESULT hr; + + // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice + hwnd = CreateDialogW(hInstance, MAKEINTRESOURCE(rcTabPageDialog), + utilWindow, dlgproc); + if (t->hwnd == NULL) + logLastError("error creating tab page in newTabPage()"); + + hr = EnableThemeDialogTexture(t->hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE | ETDT_ENABLETAB); + if (hr != S_OK) + logHRESULT("error setting tab page background in newTabPage()", hr); + + return hwnd; +} diff --git a/windows/tabpage.c b/windows/tabpage.c deleted file mode 100644 index 47f8ed11..00000000 --- a/windows/tabpage.c +++ /dev/null @@ -1,169 +0,0 @@ -// 30 may 2015 -#include "uipriv_windows.h" - -// This is a special internal control type that handles tab pages. -// This doesn't use the container class, but rather a subclassed WC_DIALOG, as that supports tab textures properly. -// The standard property sheet control does the same thing. - -struct tabPage { - uiControl c; - HWND hwnd; - uiControl *child; - int margined; - void (*baseResize)(uiControl *, intmax_t, intmax_t, intmax_t, intmax_t, uiSizing *); -}; - -uiDefineControlType(tabPage, tabPageType, struct tabPage) - -// don't override CommitDestroy(); we destroy the child with a separate function - -static uintptr_t tabPageHandle(uiControl *c) -{ - struct tabPage *t = (struct tabPage *) c; - - return (uintptr_t) (t->hwnd); -} - -// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx -#define tabMargin 7 - -static void tabPagePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height) -{ - struct tabPage *t = (struct tabPage *) c; - - *width = 0; - *height = 0; - if (t->child != NULL) - uiControlPreferredSize(t->child, d, width, height); - if (t->margined) { - *width += 2 * uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX); - *height += 2 * uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY); - } -} - -static void tabPageResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d) -{ - struct tabPage *t = (struct tabPage *) c; - RECT r; - uiSizing *dchild; - - (*(t->baseResize))(uiControl(t), x, y, width, height, d); - - if (t->child == NULL) - return; - - dchild = uiControlSizing(uiControl(t)); - - if (GetClientRect(t->hwnd, &r) == 0) - logLastError("error getting tab page client rect in tabPageResize()"); - if (t->margined) { - r.left += uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX); - r.top += uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY); - r.right -= uiWindowsDlgUnitsToX(tabMargin, d->Sys->BaseX); - r.bottom -= uiWindowsDlgUnitsToY(tabMargin, d->Sys->BaseY); - } - // this rect is in client coordinates; we need toplevel window coordinates - mapWindowRect(t->hwnd, dchild->Sys->CoordFrom, &r); - - uiControlResize(t->child, r.left, r.top, r.right - r.left, r.bottom - r.top, dchild); - - uiFreeSizing(dchild); -} - -static void tabPageContainerUpdateState(uiControl *c) -{ - struct tabPage *t = (struct tabPage *) c; - - if (t->child != NULL) - uiControlUpdateState(t->child); -} - -// dummy dialog procedure; see below for details -// let's handle parent messages here to avoid needing to subclass -static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - LRESULT lResult; - - if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) { - SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONG_PTR) lResult); - return TRUE; - } - // unthemed dialogs don't respond to WM_PRINTCLIENT - // fortunately they don't have any special painting - if (uMsg == WM_PRINTCLIENT) { - // don't worry about the return value; hopefully DefWindowProcW() caught it (if not the dialog procedure itself) - // we COULD paint the dialog background brush ourselves but meh, it works - SendMessageW(hwnd, WM_ERASEBKGND, wParam, lParam); - // and pretend we did nothing just so the themed dialog can still paint its content - return FALSE; - } - if (uMsg == WM_INITDIALOG) - return TRUE; - return FALSE; -} - -uiControl *newTabPage(void) -{ - struct tabPage *t; - HRESULT hr; - - t = (struct tabPage *) uiWindowsNewSingleHWNDControl(tabPageType()); - - // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice - t->hwnd = CreateDialogW(hInstance, MAKEINTRESOURCE(rcTabPageDialog), - utilWindow, dlgproc); - if (t->hwnd == NULL) - logLastError("error creating tab page in newTabPage()"); - - hr = EnableThemeDialogTexture(t->hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE | ETDT_ENABLETAB); - if (hr != S_OK) - logHRESULT("error setting tab page background in newTabPage()", hr); - - uiControl(t)->Handle = tabPageHandle; - uiControl(t)->PreferredSize = tabPagePreferredSize; - t->baseResize = uiControl(t)->Resize; - uiControl(t)->Resize = tabPageResize; - uiControl(t)->ContainerUpdateState = tabPageContainerUpdateState; - - return uiControl(t); -} - -int tabPageMargined(uiControl *c) -{ - struct tabPage *t = (struct tabPage *) c; - - return t->margined; -} - -void tabPageSetMargined(uiControl *c, int margined) -{ - struct tabPage *t = (struct tabPage *) c; - - t->margined = margined; -} - -void tabPageDestroyChild(uiControl *c) -{ - struct tabPage *t = (struct tabPage *) c; - - uiControlSetParent(t->child, NULL); - uiControlDestroy(t->child); - t->child = NULL; -} - -void tabPagePreserveChild(uiControl *c) -{ - struct tabPage *t = (struct tabPage *) c; - - uiControlSetParent(t->child, NULL); - t->child = NULL; -} - -void tabPageSetChild(uiControl *c, uiControl *child) -{ - struct tabPage *t = (struct tabPage *) c; - - t->child = child; - t->child = child; - uiControlSetParent(t->child, uiControl(t)); -}