2015-04-26 22:33:08 -05:00
// 26 april 2015
# include "uipriv_windows.h"
# define containerClass L"libui_uiContainerClass"
HWND initialParent ;
2015-04-27 09:37:37 -05:00
struct container {
HWND hwnd ;
uiContainer * parent ;
2015-04-27 09:41:19 -05:00
int hidden ;
2015-05-04 22:06:16 -05:00
HBRUSH brush ;
2015-04-27 09:37:37 -05:00
} ;
2015-05-04 22:06:16 -05:00
// see http://www.codeproject.com/Articles/5978/Correctly-drawn-themed-dialogs-in-WinXP
2015-05-05 13:37:25 -05:00
static HBRUSH getControlBackgroundBrush ( HWND hwnd , HDC dc , RECT * hwndScreenRect )
2015-05-04 22:06:16 -05:00
{
HWND parent ;
2015-05-05 13:37:25 -05:00
RECT parentRect ;
2015-05-04 22:06:16 -05:00
int class ;
HDC cdc ;
HBITMAP bitmap , prevbitmap ;
HBRUSH brush ;
parent = hwnd ;
for ( ; ; ) {
parent = GetAncestor ( parent , GA_PARENT ) ;
// skip groupboxes; they're (supposed to be) transparent
// skip uiContainers; they don't draw anything
class = windowClassOf ( parent , L " button " , containerClass , NULL ) ;
if ( class ! = 0 & & class ! = 1 )
break ;
}
2015-05-05 12:01:20 -05:00
// TODO get client rect instead?
2015-05-05 13:37:25 -05:00
if ( GetWindowRect ( parent , & parentRect ) = = 0 )
2015-05-05 12:01:20 -05:00
logLastError ( " error getting parent's window rect in getControlBackgroundBrush() " ) ;
2015-05-04 22:06:16 -05:00
cdc = CreateCompatibleDC ( dc ) ;
2015-05-05 12:01:20 -05:00
if ( cdc = = NULL )
logLastError ( " error creating compatible DC in getControlBackgroundBrush() " ) ;
2015-05-05 13:37:25 -05:00
bitmap = CreateCompatibleBitmap ( dc , parentRect . right - parentRect . left , parentRect . bottom - parentRect . top ) ;
2015-05-05 12:01:20 -05:00
if ( bitmap = = NULL )
logLastError ( " error creating compatible bitmap in getControlBackgroundBrush() " ) ;
2015-05-04 22:06:16 -05:00
prevbitmap = SelectObject ( cdc , bitmap ) ;
2015-05-05 12:01:20 -05:00
if ( prevbitmap = = NULL )
logLastError ( " error selecting bitmap into compatible DC in getControlBackgroundBrush() " ) ;
2015-05-04 22:06:16 -05:00
SendMessageW ( parent , WM_PRINTCLIENT , ( WPARAM ) cdc , PRF_CLIENT ) ;
2015-05-05 12:01:20 -05:00
// create it now, just to be safe
2015-05-04 22:06:16 -05:00
brush = CreatePatternBrush ( bitmap ) ;
2015-05-05 12:01:20 -05:00
if ( brush = = NULL )
logLastError ( " error creating pattern brush in getControlBackgroundBrush() " ) ;
if ( SelectObject ( cdc , prevbitmap ) ! = bitmap )
logLastError ( " error selecting previous bitmap back into compatible DC in getControlBackgroundBrush() " ) ;
if ( DeleteObject ( bitmap ) = = 0 )
logLastError ( " error deleting compatible bitmap in getControlBackgroundBrush() " ) ;
if ( DeleteDC ( cdc ) = = 0 )
logLastError ( " error deleting compatible DC in getControlBackgroundBrush() " ) ;
2015-05-04 23:16:25 -05:00
2015-05-05 13:37:25 -05:00
// the given control rect is in screen coordinates; convert to parent coordinates
mapWindowRect ( NULL , parent , hwndScreenRect ) ;
if ( SetBrushOrgEx ( dc , - hwndScreenRect - > left , - hwndScreenRect - > top , NULL ) = = 0 )
2015-05-05 12:01:20 -05:00
logLastError ( " error setting brush origin in getControlBackgroundBrush() " ) ;
2015-05-04 22:06:16 -05:00
return brush ;
}
2015-05-05 13:44:10 -05:00
// TODO this doesn't work right for partial redraws
2015-05-05 13:00:55 -05:00
static void paintContainerBackground ( HWND hwnd , HDC dc , RECT * paintRect )
{
RECT screenRect ;
HBRUSH brush , prevbrush ;
// getControlBackgroundBrush() needs a screen rectangle
screenRect = * paintRect ;
mapWindowRect ( hwnd , NULL , & screenRect ) ;
2015-05-05 13:37:25 -05:00
brush = getControlBackgroundBrush ( hwnd , dc , & screenRect ) ;
2015-05-05 13:00:55 -05:00
prevbrush = SelectObject ( dc , brush ) ;
if ( prevbrush = = NULL )
logLastError ( " error selecting background brush into DC in paintContainerBackground() " ) ;
if ( PatBlt ( dc , paintRect - > left , paintRect - > top , paintRect - > right - paintRect - > left , paintRect - > bottom - paintRect - > top , PATCOPY ) = = 0 )
logLastError ( " error drawing container background in paintContainerBackground() " ) ;
if ( SelectObject ( dc , prevbrush ) ! = brush )
logLastError ( " error selecting previous brush back into DC in paintContainerBackground() " ) ;
if ( DeleteObject ( brush ) = = 0 )
logLastError ( " error deleting background brush in paintContainerBackground() " ) ;
}
2015-05-04 22:06:16 -05:00
2015-04-27 00:27:07 -05:00
// from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing and https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
// this X value is really only for buttons but I don't see a better one :/
# define winXPadding 4
# define winYPadding 4
2015-05-03 10:55:34 -05:00
// abort the resize if something fails and we don't have what we need to do a resize
static HRESULT resize ( uiContainer * cc , RECT * r )
2015-04-27 00:27:07 -05:00
{
2015-04-27 21:17:15 -05:00
struct container * c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
2015-04-27 00:27:07 -05:00
uiSizing d ;
uiSizingSys sys ;
HDC dc ;
HFONT prevfont ;
TEXTMETRICW tm ;
SIZE size ;
2015-04-27 09:37:37 -05:00
dc = GetDC ( c - > hwnd ) ;
2015-04-27 00:27:07 -05:00
if ( dc = = NULL )
2015-05-03 10:55:34 -05:00
return logLastError ( " error getting DC in resize() " ) ;
2015-04-27 00:27:07 -05:00
prevfont = ( HFONT ) SelectObject ( dc , hMessageFont ) ;
if ( prevfont = = NULL )
2015-05-03 10:55:34 -05:00
return logLastError ( " error loading control font into device context in resize() " ) ;
ZeroMemory ( & tm , sizeof ( TEXTMETRICW ) ) ;
2015-04-27 00:27:07 -05:00
if ( GetTextMetricsW ( dc , & tm ) = = 0 )
2015-05-03 10:55:34 -05:00
return logLastError ( " error getting text metrics in resize() " ) ;
2015-04-27 00:27:07 -05:00
if ( GetTextExtentPoint32W ( dc , L " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " , 52 , & size ) = = 0 )
2015-05-03 10:55:34 -05:00
return logLastError ( " error getting text extent point in resize() " ) ;
2015-04-27 00:27:07 -05:00
sys . baseX = ( int ) ( ( size . cx / 26 + 1 ) / 2 ) ;
sys . baseY = ( int ) tm . tmHeight ;
sys . internalLeading = tm . tmInternalLeading ;
2015-05-03 10:55:34 -05:00
2015-04-27 00:27:07 -05:00
if ( SelectObject ( dc , prevfont ) ! = hMessageFont )
2015-05-03 10:55:34 -05:00
return logLastError ( " error restoring previous font into device context in resize() " ) ;
2015-04-27 09:37:37 -05:00
if ( ReleaseDC ( c - > hwnd , dc ) = = 0 )
2015-05-03 10:55:34 -05:00
return logLastError ( " error releasing DC in resize() " ) ;
2015-04-27 00:27:07 -05:00
d . xPadding = uiDlgUnitsToX ( winXPadding , sys . baseX ) ;
d . yPadding = uiDlgUnitsToY ( winYPadding , sys . baseY ) ;
d . sys = & sys ;
2015-04-27 21:17:15 -05:00
uiContainerResizeChildren ( cc , r - > left , r - > top , r - > right - r - > left , r - > bottom - r - > top , & d ) ;
2015-05-03 18:58:37 -05:00
return S_OK ;
2015-04-27 00:27:07 -05:00
}
2015-04-26 22:33:08 -05:00
static LRESULT CALLBACK containerWndProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
2015-04-27 21:17:15 -05:00
uiContainer * cc ;
struct container * c ;
2015-04-27 00:07:46 -05:00
CREATESTRUCTW * cs = ( CREATESTRUCTW * ) lParam ;
2015-04-29 18:17:26 -05:00
HWND control ;
NMHDR * nm = ( NMHDR * ) lParam ;
2015-04-27 21:17:15 -05:00
WINDOWPOS * wp = ( WINDOWPOS * ) lParam ;
2015-04-27 00:27:07 -05:00
RECT r ;
2015-05-05 08:10:58 -05:00
HDC dc ;
PAINTSTRUCT ps ;
2015-04-27 00:07:46 -05:00
2015-04-27 21:17:15 -05:00
cc = uiContainer ( GetWindowLongPtrW ( hwnd , GWLP_USERDATA ) ) ;
if ( cc = = NULL )
2015-04-27 00:07:46 -05:00
if ( uMsg = = WM_NCCREATE )
SetWindowLongPtrW ( hwnd , GWLP_USERDATA , ( LONG_PTR ) ( cs - > lpCreateParams ) ) ;
// DO NOT RETURN DEFWINDOWPROC() HERE
// see the next block of comments as to why
// instead, we simply check if c == NULL again later
switch ( uMsg ) {
2015-04-26 22:33:08 -05:00
// these must always be run, even on the initial parent
// why? http://blogs.msdn.com/b/oldnewthing/archive/2010/03/16/9979112.aspx
2015-04-29 18:17:26 -05:00
case WM_COMMAND :
// bounce back to the control in question
// except if to the initial parent, in which case act as if the message was ignored
control = ( HWND ) lParam ;
if ( control ! = NULL & & IsChild ( initialParent , control ) = = 0 )
return SendMessageW ( control , msgCOMMAND , wParam , lParam ) ;
break ; // fall through to DefWindowProcW()
case WM_NOTIFY :
// same as WM_COMMAND
control = nm - > hwndFrom ;
if ( control ! = NULL & & IsChild ( initialParent , control ) = = 0 )
return SendMessageW ( control , msgNOTIFY , wParam , lParam ) ;
break ;
2015-05-04 22:06:16 -05:00
// these are only run if c is not NULL
case WM_CTLCOLORSTATIC :
2015-05-04 19:36:33 -05:00
case WM_CTLCOLORBTN :
2015-05-04 22:06:16 -05:00
if ( cc = = NULL )
break ;
c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
if ( c - > brush ! = NULL )
if ( DeleteObject ( c - > brush ) = = 0 )
logLastError ( " error deleting old background brush in containerWndProc() " ) ;
2015-05-04 19:36:33 -05:00
/*TODO // read-only TextFields and Textboxes are exempt
// this is because read-only edit controls count under WM_CTLCOLORSTATIC
if ( windowClassOf ( ( HWND ) lParam , L " edit " , NULL ) = = 0 )
if ( textfieldReadOnly ( ( HWND ) lParam ) )
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2015-05-04 22:06:16 -05:00
*/ if ( SetBkMode ( ( HDC ) wParam , TRANSPARENT ) = = 0 )
2015-05-05 13:37:25 -05:00
logLastError ( " error setting transparent background mode to controls in containerWndProc() " ) ;
if ( GetWindowRect ( ( HWND ) lParam , & r ) = = 0 )
logLastError ( " error getting control's window rect in containerWndProc() " ) ;
c - > brush = getControlBackgroundBrush ( ( HWND ) lParam , ( HDC ) wParam , & r ) ;
2015-05-04 22:06:16 -05:00
return ( LRESULT ) ( c - > brush ) ;
2015-05-05 08:10:58 -05:00
case WM_PAINT :
if ( cc = = NULL )
break ;
c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
dc = BeginPaint ( c - > hwnd , & ps ) ;
2015-05-05 12:01:20 -05:00
if ( dc = = NULL )
logLastError ( " error beginning container paint in containerWndProc() " ) ;
2015-05-05 13:00:55 -05:00
r = ps . rcPaint ;
paintContainerBackground ( c - > hwnd , dc , & r ) ;
EndPaint ( c - > hwnd , & ps ) ;
return 0 ;
2015-05-05 13:37:25 -05:00
// TODO the tab control uses this to draw the tab background
// but we have no idea where the tab is, so...
// case WM_PRINTCLIENT:
2015-05-05 13:00:55 -05:00
if ( cc = = NULL )
break ;
c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
2015-05-05 12:01:20 -05:00
if ( GetClientRect ( c - > hwnd , & r ) = = 0 )
logLastError ( " error getting client rect in containerWndProc() " ) ;
2015-05-05 13:00:55 -05:00
paintContainerBackground ( c - > hwnd , ( HDC ) wParam , & r ) ;
2015-05-05 08:10:58 -05:00
return 0 ;
2015-05-05 13:44:10 -05:00
case WM_ERASEBKGND :
// avoid some flicker
// we draw the whole update area anyway
return 1 ;
2015-04-27 00:07:46 -05:00
case WM_WINDOWPOSCHANGED :
2015-04-27 00:27:07 -05:00
if ( ( wp - > flags & SWP_NOSIZE ) ! = 0 )
break ;
// fall through
2015-04-27 00:07:46 -05:00
case msgUpdateChild :
2015-04-27 21:17:15 -05:00
if ( cc = = NULL )
2015-04-27 00:07:46 -05:00
break ;
2015-04-27 21:17:15 -05:00
c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
if ( GetClientRect ( c - > hwnd , & r ) = = 0 )
2015-05-05 12:01:20 -05:00
logLastError ( " error getting client rect for resize in containerWndProc() " ) ;
2015-04-27 21:17:15 -05:00
resize ( cc , & r ) ;
2015-04-27 00:27:07 -05:00
return 0 ;
2015-04-27 00:07:46 -05:00
}
2015-04-26 22:33:08 -05:00
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
2015-04-27 21:17:15 -05:00
const char * initContainer ( HICON hDefaultIcon , HCURSOR hDefaultCursor )
2015-04-26 22:33:08 -05:00
{
WNDCLASSW wc ;
ZeroMemory ( & wc , sizeof ( WNDCLASSW ) ) ;
wc . lpszClassName = containerClass ;
wc . lpfnWndProc = containerWndProc ;
wc . hInstance = hInstance ;
wc . hIcon = hDefaultIcon ;
wc . hCursor = hDefaultCursor ;
wc . hbrBackground = ( HBRUSH ) ( COLOR_BTNFACE + 1 ) ;
if ( RegisterClassW ( & wc ) = = 0 )
return " registering uiContainer window class " ;
initialParent = CreateWindowExW ( 0 ,
2015-04-27 21:17:15 -05:00
containerClass , L " " ,
2015-04-26 22:33:08 -05:00
WS_OVERLAPPEDWINDOW ,
0 , 0 ,
100 , 100 ,
NULL , NULL , hInstance , NULL ) ;
if ( initialParent = = NULL )
return " creating initial parent window " ;
// just to be safe, disable the initial parent so it can't be interacted with accidentally
// if this causes issues for our controls, we can remove it
EnableWindow ( initialParent , FALSE ) ;
return NULL ;
}
2015-04-27 09:37:37 -05:00
// subclasses override this and call back here when all children are destroyed
static void containerDestroy ( uiControl * cc )
2015-04-26 22:33:08 -05:00
{
2015-04-27 09:37:37 -05:00
struct container * c = ( struct container * ) ( cc - > Internal ) ;
if ( c - > parent ! = NULL )
complain ( " attempt to destroy uiContainer %p while it has a parent " , cc ) ;
if ( DestroyWindow ( c - > hwnd ) = = 0 )
logLastError ( " error destroying uiContainer window in containerDestroy() " ) ;
uiFree ( c ) ;
}
static uintptr_t containerHandle ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
2015-04-27 21:17:15 -05:00
return ( uintptr_t ) ( c - > hwnd ) ;
2015-04-27 09:37:37 -05:00
}
static void containerSetParent ( uiControl * cc , uiContainer * parent )
{
2015-04-29 00:53:39 -05:00
struct container * c = ( struct container * ) ( cc - > Internal ) ;
2015-04-27 09:37:37 -05:00
uiContainer * oldparent ;
HWND newparent ;
oldparent = c - > parent ;
c - > parent = parent ;
newparent = initialParent ;
if ( c - > parent ! = NULL )
newparent = ( HWND ) uiControlHandle ( uiControl ( c - > parent ) ) ;
if ( SetParent ( c - > hwnd , newparent ) = = 0 )
logLastError ( " error changing uiContainer parent in containerSetParent() " ) ;
if ( oldparent ! = NULL )
uiContainerUpdate ( oldparent ) ;
if ( c - > parent ! = NULL )
uiContainerUpdate ( c - > parent ) ;
}
static void containerResize ( uiControl * cc , intmax_t x , intmax_t y , intmax_t width , intmax_t height , uiSizing * d )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
2015-05-05 14:19:55 -05:00
if ( MoveWindow ( c - > hwnd , x , y , width , height , TRUE ) = = 0 )
logLastError ( " error resizing uiContainer in containerResize() " ) ;
2015-04-27 09:37:37 -05:00
}
2015-04-27 09:41:19 -05:00
static int containerVisible ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
return ! c - > hidden ;
}
2015-04-27 09:37:37 -05:00
static void containerShow ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
ShowWindow ( c - > hwnd , SW_SHOW ) ;
2015-04-28 20:35:29 -05:00
// hidden controls don't count in boxes and grids
2015-04-29 13:25:34 -05:00
c - > hidden = 0 ;
2015-04-28 20:35:29 -05:00
if ( c - > parent ! = NULL )
uiContainerUpdate ( c - > parent ) ;
2015-04-27 09:37:37 -05:00
}
static void containerHide ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
ShowWindow ( c - > hwnd , SW_HIDE ) ;
2015-04-29 13:25:34 -05:00
c - > hidden = 1 ;
2015-04-28 20:35:29 -05:00
if ( c - > parent ! = NULL )
uiContainerUpdate ( c - > parent ) ;
2015-04-27 09:37:37 -05:00
}
static void containerEnable ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
2015-05-04 13:05:36 -05:00
uiControlSysFuncParams p ;
2015-04-27 09:37:37 -05:00
EnableWindow ( c - > hwnd , TRUE ) ;
2015-05-04 13:05:36 -05:00
p . Func = uiWindowsSysFuncContainerEnable ;
uiControlSysFunc ( cc , & p ) ;
2015-04-27 09:37:37 -05:00
}
static void containerDisable ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
2015-05-04 13:05:36 -05:00
uiControlSysFuncParams p ;
2015-04-27 09:37:37 -05:00
EnableWindow ( c - > hwnd , FALSE ) ;
2015-05-04 13:05:36 -05:00
p . Func = uiWindowsSysFuncContainerDisable ;
uiControlSysFunc ( cc , & p ) ;
2015-04-27 09:37:37 -05:00
}
static void containerUpdate ( uiContainer * cc )
{
struct container * c = ( struct container * ) ( uiControl ( cc ) - > Internal ) ;
SendMessageW ( c - > hwnd , msgUpdateChild , 0 , 0 ) ;
}
void uiMakeContainer ( uiContainer * cc )
{
struct container * c ;
c = uiNew ( struct container ) ;
2015-05-03 13:12:45 -05:00
c - > hwnd = CreateWindowExW ( WS_EX_CONTROLPARENT ,
2015-04-27 09:37:37 -05:00
containerClass , L " " ,
WS_CHILD | WS_VISIBLE ,
0 , 0 ,
100 , 100 ,
initialParent , NULL , hInstance , cc ) ;
if ( c - > hwnd = = NULL )
logLastError ( " error creating uiContainer window in uiMakeContainer() " ) ;
uiControl ( cc ) - > Internal = c ;
uiControl ( cc ) - > Destroy = containerDestroy ;
uiControl ( cc ) - > Handle = containerHandle ;
uiControl ( cc ) - > SetParent = containerSetParent ;
// PreferredSize() is provided by subclasses
uiControl ( cc ) - > Resize = containerResize ;
2015-04-27 09:41:19 -05:00
uiControl ( cc ) - > Visible = containerVisible ;
2015-04-27 09:37:37 -05:00
uiControl ( cc ) - > Show = containerShow ;
uiControl ( cc ) - > Hide = containerHide ;
uiControl ( cc ) - > Enable = containerEnable ;
uiControl ( cc ) - > Disable = containerDisable ;
// ResizeChildren() is provided by subclasses
uiContainer ( cc ) - > Update = containerUpdate ;
2015-04-26 22:33:08 -05:00
}