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-04-27 09:37:37 -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-04-27 09:37:37 -05:00
static void 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 ;
// TODO clean this up a bit
size . cx = 0 ;
size . cy = 0 ;
ZeroMemory ( & tm , sizeof ( TEXTMETRICW ) ) ;
2015-04-27 09:37:37 -05:00
dc = GetDC ( c - > hwnd ) ;
2015-04-27 00:27:07 -05:00
if ( dc = = NULL )
logLastError ( " error getting DC in resize() " ) ;
prevfont = ( HFONT ) SelectObject ( dc , hMessageFont ) ;
if ( prevfont = = NULL )
logLastError ( " error loading control font into device context in resize() " ) ;
if ( GetTextMetricsW ( dc , & tm ) = = 0 )
logLastError ( " error getting text metrics in resize() " ) ;
if ( GetTextExtentPoint32W ( dc , L " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " , 52 , & size ) = = 0 )
logLastError ( " error getting text extent point in resize() " ) ;
sys . baseX = ( int ) ( ( size . cx / 26 + 1 ) / 2 ) ;
sys . baseY = ( int ) tm . tmHeight ;
sys . internalLeading = tm . tmInternalLeading ;
if ( SelectObject ( dc , prevfont ) ! = hMessageFont )
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-04-27 00:27:07 -05:00
logLastError ( " error releasing DC in resize() " ) ;
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-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-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-04-27 00:07:46 -05:00
// these are only run if c is not NULL
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-04-27 00:27:07 -05:00
logLastError ( " error getting client rect for resize in parentWndProc() " ) ;
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 ) ;
if ( MoveWindow ( c - > hwnd , x , y , width , height , TRUE ) = = 0 )
logLastError ( " error resizing uiContainer in containerResize() " ) ;
}
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 ) ;
EnableWindow ( c - > hwnd , TRUE ) ;
}
static void containerDisable ( uiControl * cc )
{
struct container * c = ( struct container * ) ( cc - > Internal ) ;
EnableWindow ( c - > hwnd , FALSE ) ;
}
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 ) ;
c - > hwnd = CreateWindowExW ( 0 ,
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
}