// 30 may 2015 #include "uipriv_windows.hpp" // TODO refine error handling // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx #define tabMargin 7 static void tabPageMargins(struct tabPage *tp, int *mx, int *my) { uiWindowsSizing sizing; *mx = 0; *my = 0; if (!tp->margined) return; uiWindowsGetSizing(tp->hwnd, &sizing); *mx = tabMargin; *my = tabMargin; uiWindowsSizingDlgUnitsToPixels(&sizing, mx, my); } static void tabPageRelayout(struct tabPage *tp) { RECT r; int mx, my; HWND child; if (tp->child == NULL) return; uiWindowsEnsureGetClientRect(tp->hwnd, &r); tabPageMargins(tp, &mx, &my); r.left += mx; r.top += my; r.right -= mx; r.bottom -= my; child = (HWND) uiControlHandle(tp->child); uiWindowsEnsureMoveWindowDuringResize(child, r.left, r.top, r.right - r.left, r.bottom - r.top); } // dummy dialog procedure; see below for details // let's handle parent messages here to avoid needing to subclass // TODO do we need to handle DM_GETDEFID/DM_SETDEFID here too because of the ES_WANTRETURN stuff at http://blogs.msdn.com/b/oldnewthing/archive/2007/08/20/4470527.aspx? what about multiple default buttons (TODO)? // TODO we definitely need to do something about edit message handling; it does a fake close of our parent on pressing escape, causing uiWindow to stop responding to maximizes but still respond to events and then die horribly on destruction static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { struct tabPage *tp; LRESULT lResult; if (uMsg == WM_INITDIALOG) { tp = (struct tabPage *) lParam; tp->hwnd = hwnd; SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR) tp); return TRUE; } if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) { SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONG_PTR) lResult); return TRUE; } if (uMsg == WM_WINDOWPOSCHANGED) { tp = (struct tabPage *) GetWindowLongPtrW(hwnd, DWLP_USER); tabPageRelayout(tp); // pretend the dialog hasn't handled this just in case the system needs to do something special return FALSE; } // 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 // TODO see if w ecan avoid erasing the background in this case in the first place, or if we even need to return FALSE; } return FALSE; } // because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well /* // this is the dialog template used by tab pages; see windows/tabpage.c for details rcTabPageDialog DIALOGEX 0, 0, 100, 100 STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE EXSTYLE WS_EX_CONTROLPARENT BEGIN // nothing END */ static const uint8_t data_rcTabPageDialog[] = { 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static_assert(ARRAYSIZE(data_rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog"); struct tabPage *newTabPage(uiControl *child) { struct tabPage *tp; HRESULT hr; tp = uiprivNew(struct tabPage); // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice if (CreateDialogIndirectParamW(hInstance, (const DLGTEMPLATE *) data_rcTabPageDialog, utilWindow, dlgproc, (LPARAM) tp) == NULL) logLastError(L"error creating tab page"); tp->child = child; if (tp->child != NULL) { uiWindowsEnsureSetParentHWND((HWND) uiControlHandle(tp->child), tp->hwnd); uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl(tp->child)); } hr = EnableThemeDialogTexture(tp->hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE | ETDT_ENABLETAB); if (hr != S_OK) logHRESULT(L"error setting tab page background", hr); // continue anyway; it'll look wrong but eh // and start the tab page hidden ShowWindow(tp->hwnd, SW_HIDE); return tp; } void tabPageDestroy(struct tabPage *tp) { // don't destroy the child with the page if (tp->child != NULL) uiWindowsControlSetParentHWND(uiWindowsControl(tp->child), NULL); // don't call EndDialog(); that's for the DialogBox() family of functions instead of CreateDialog() uiWindowsEnsureDestroyWindow(tp->hwnd); uiprivFree(tp); } void tabPageMinimumSize(struct tabPage *tp, int *width, int *height) { int mx, my; *width = 0; *height = 0; if (tp->child != NULL) uiWindowsControlMinimumSize(uiWindowsControl(tp->child), width, height); tabPageMargins(tp, &mx, &my); *width += 2 * mx; *height += 2 * my; }