Wrote up the boilerplate needed for tab pages.
This commit is contained in:
parent
411afe4480
commit
99c37b9697
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue