2015-05-17 12:01:42 -05:00
// 16 may 2015
2015-05-16 19:28:35 -05:00
# include "uipriv_windows.h"
2015-05-18 11:17:35 -05:00
// TODO
2015-05-18 11:57:57 -05:00
// - comctl5 on real windows: tabs get drawn behind checkbox
2015-05-18 11:17:35 -05:00
2015-05-16 19:28:35 -05:00
struct tab {
uiTab t ;
HWND hwnd ;
2015-05-18 10:41:09 -05:00
struct ptrArray * pages ;
void ( * baseResize ) ( uiControl * , intmax_t , intmax_t , intmax_t , intmax_t , uiSizing * ) ;
2015-05-16 19:28:35 -05:00
} ;
2015-05-18 10:41:09 -05:00
struct tabPage {
uiControl * control ;
int margined ;
} ;
// utility functions
static LRESULT curpage ( struct tab * t )
{
return SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
}
static void showHidePage ( struct tab * t , LRESULT which , int hide )
{
struct tabPage * page ;
if ( which = = ( LRESULT ) ( - 1 ) )
return ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , which ) ;
if ( hide )
uiControlContainerHide ( page - > control ) ;
else {
uiControlContainerShow ( page - > control ) ;
// we only resize the current page, so we have to do this here
uiControlQueueResize ( page - > control ) ;
}
}
// control implementation
2015-05-21 13:52:21 -05:00
static BOOL onWM_NOTIFY ( uiControl * c , HWND hwnd , NMHDR * nm , LRESULT * lResult )
2015-05-16 19:28:35 -05:00
{
2015-05-18 11:10:54 -05:00
struct tab * t = ( struct tab * ) c ;
if ( nm - > code ! = TCN_SELCHANGING & & nm - > code ! = TCN_SELCHANGE )
return FALSE ;
showHidePage ( t , curpage ( t ) , nm - > code = = TCN_SELCHANGING ) ;
* lResult = 0 ;
if ( nm - > code = = TCN_SELCHANGING )
* lResult = FALSE ;
return TRUE ;
2015-05-16 19:28:35 -05:00
}
static void onDestroy ( void * data )
{
2015-05-17 12:01:42 -05:00
// TODO
2015-05-21 11:39:19 -05:00
//TODO uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd);
2015-05-16 19:28:35 -05:00
}
2015-05-18 12:03:22 -05:00
// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
# define tabMargin 7
2015-05-16 19:28:35 -05:00
static void tabPreferredSize ( uiControl * c , uiSizing * d , intmax_t * width , intmax_t * height )
{
2015-05-17 12:01:42 -05:00
// TODO
2015-05-16 19:28:35 -05:00
}
2015-05-18 10:41:09 -05:00
static void tabResize ( uiControl * c , intmax_t x , intmax_t y , intmax_t width , intmax_t height , uiSizing * d )
{
struct tab * t = ( struct tab * ) c ;
LRESULT n ;
struct tabPage * page ;
RECT r ;
2015-05-18 11:04:52 -05:00
uiSizing * dchild ;
2015-05-18 10:41:09 -05:00
( * ( t - > baseResize ) ) ( uiControl ( t ) , x , y , width , height , d ) ;
n = curpage ( t ) ;
if ( n = = ( LRESULT ) ( - 1 ) )
return ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
2015-05-18 11:04:52 -05:00
dchild = uiControlSizing ( uiControl ( t ) ) ;
// now we need to figure out what rect the child goes
// this rect needs to be in toplevel window coordinates, but TCM_ADJUSTRECT wants a window rect, which is screen coordinates
2015-05-18 10:41:09 -05:00
r . left = x ;
r . top = y ;
r . right = x + width ;
r . bottom = y + height ;
2015-05-18 11:04:52 -05:00
mapWindowRect ( dchild - > Sys - > CoordFrom , NULL , & r ) ;
2015-05-18 10:41:09 -05:00
SendMessageW ( t - > hwnd , TCM_ADJUSTRECT , ( WPARAM ) FALSE , ( LPARAM ) ( & r ) ) ;
2015-05-18 11:04:52 -05:00
mapWindowRect ( NULL , dchild - > Sys - > CoordFrom , & r ) ;
2015-05-18 10:41:09 -05:00
if ( page - > margined ) {
2015-05-18 12:03:22 -05:00
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 ) ;
2015-05-18 10:41:09 -05:00
}
2015-05-18 11:04:52 -05:00
uiControlResize ( page - > control , r . left , r . top , r . right - r . left , r . bottom - r . top , dchild ) ;
uiFreeSizing ( dchild ) ;
2015-05-18 10:41:09 -05:00
}
2015-05-18 09:20:11 -05:00
static void tabAppend ( uiTab * tt , const char * name , uiControl * child )
2015-05-16 19:28:35 -05:00
{
2015-05-18 10:41:09 -05:00
struct tab * t = ( struct tab * ) tt ;
uiTabInsertAt ( tt , name , t - > pages - > len , child ) ;
2015-05-16 19:28:35 -05:00
}
2015-05-18 09:32:08 -05:00
static void tabInsertAt ( uiTab * tt , const char * name , uintmax_t n , uiControl * child )
2015-05-16 19:28:35 -05:00
{
2015-05-18 10:41:09 -05:00
struct tab * t = ( struct tab * ) tt ;
struct tabPage * page ;
LRESULT hide , show ;
TCITEMW item ;
WCHAR * wname ;
// see below
hide = curpage ( t ) ;
page = uiNew ( struct tabPage ) ;
page - > control = child ;
uiControlSetParent ( page - > control , uiControl ( t ) ) ;
2015-05-18 11:04:52 -05:00
// and make it invisible at first; we show it later if needed
uiControlContainerHide ( page - > control ) ;
2015-05-18 10:41:09 -05:00
ptrArrayInsertAt ( t - > pages , n , page ) ;
ZeroMemory ( & item , sizeof ( TCITEMW ) ) ;
item . mask = TCIF_TEXT ;
wname = toUTF16 ( name ) ;
item . pszText = wname ;
if ( SendMessageW ( t - > hwnd , TCM_INSERTITEM , ( WPARAM ) n , ( LPARAM ) ( & item ) ) = = ( LRESULT ) - 1 )
logLastError ( " error adding tab to uiTab in uiTabInsertAt() " ) ;
uiFree ( wname ) ;
// we need to do this because adding the first tab doesn't send a TCN_SELCHANGE; it just shows the page
show = curpage ( t ) ;
if ( show ! = hide ) {
showHidePage ( t , hide , 1 ) ;
showHidePage ( t , show , 0 ) ;
}
2015-05-16 19:28:35 -05:00
}
2015-05-18 09:20:11 -05:00
static void tabDelete ( uiTab * tt , uintmax_t n )
2015-05-16 19:28:35 -05:00
{
2015-05-18 14:11:59 -05:00
struct tab * t = ( struct tab * ) tt ;
struct tabPage * page ;
// first delete the tab from the tab control
// if this is the current tab, no tab will be selected, which is good
if ( SendMessageW ( t - > hwnd , TCM_DELETEITEM , ( WPARAM ) n , 0 ) = = FALSE )
logLastError ( " error deleting uiTab tab in tabDelete() " ) ;
// now delete the page itself
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
ptrArrayDelete ( t - > pages , n ) ;
// and keep the page control alive
uiControlSetParent ( page - > control , NULL ) ;
2015-05-18 14:27:56 -05:00
// and show it again, as we don't know where it will go next
uiControlContainerShow ( page - > control ) ;
2015-05-18 14:11:59 -05:00
uiFree ( page ) ;
2015-05-16 19:28:35 -05:00
}
static uintmax_t tabNumPages ( uiTab * tt )
{
2015-05-18 12:03:22 -05:00
struct tab * t = ( struct tab * ) tt ;
return t - > pages - > len ;
2015-05-16 19:28:35 -05:00
}
static int tabMargined ( uiTab * tt , uintmax_t n )
{
2015-05-18 12:03:22 -05:00
struct tab * t = ( struct tab * ) tt ;
struct tabPage * page ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
return page - > margined ;
2015-05-16 19:28:35 -05:00
}
static void tabSetMargined ( uiTab * tt , uintmax_t n , int margined )
{
2015-05-18 12:03:22 -05:00
struct tab * t = ( struct tab * ) tt ;
struct tabPage * page ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
page - > margined = margined ;
uiControlQueueResize ( page - > control ) ;
2015-05-16 19:28:35 -05:00
}
uiTab * uiNewTab ( void )
{
struct tab * t ;
uiWindowsMakeControlParams p ;
t = uiNew ( struct tab ) ;
2015-05-17 18:07:07 -05:00
uiTyped ( t ) - > Type = uiTypeTab ( ) ;
2015-05-16 19:28:35 -05:00
p . dwExStyle = 0 ; // don't set WS_EX_CONTROLPARENT yet; we do that dynamically in the message loop (see main_windows.c)
p . lpClassName = WC_TABCONTROLW ;
p . lpWindowName = L " " ;
p . dwStyle = TCS_TOOLTIPS | WS_TABSTOP ; // start with this; we will alternate between this and WS_EX_CONTROLPARENT as needed (see main.c and msgHasTabStops above and the toggling functions below)
p . hInstance = hInstance ;
p . lpParam = NULL ;
p . useStandardControlFont = TRUE ;
p . onDestroy = onDestroy ;
p . onDestroyData = t ;
uiWindowsMakeControl ( uiControl ( t ) , & p ) ;
t - > hwnd = ( HWND ) uiControlHandle ( uiControl ( t ) ) ;
2015-05-21 11:39:19 -05:00
uiWindowsRegisterWM_NOTIFYHandler ( t - > hwnd , onWM_NOTIFY , uiControl ( t ) ) ;
2015-05-16 19:28:35 -05:00
2015-05-18 10:41:09 -05:00
t - > pages = newPtrArray ( ) ;
2015-05-16 19:28:35 -05:00
uiControl ( t ) - > PreferredSize = tabPreferredSize ;
2015-05-18 10:41:09 -05:00
t - > baseResize = uiControl ( t ) - > Resize ;
uiControl ( t ) - > Resize = tabResize ;
2015-05-16 19:28:35 -05:00
2015-05-18 09:20:11 -05:00
uiTab ( t ) - > Append = tabAppend ;
2015-05-18 09:32:08 -05:00
uiTab ( t ) - > InsertAt = tabInsertAt ;
2015-05-18 09:20:11 -05:00
uiTab ( t ) - > Delete = tabDelete ;
2015-05-16 19:28:35 -05:00
uiTab ( t ) - > NumPages = tabNumPages ;
uiTab ( t ) - > Margined = tabMargined ;
uiTab ( t ) - > SetMargined = tabSetMargined ;
return uiTab ( t ) ;
}
2015-05-16 19:33:20 -05:00
// unfortunately WS_TABSTOP and WS_EX_CONTROLPARENT are mutually exclusive, so we have to toggle between them
// see main.c for more details
void tabEnterTabNavigation ( HWND hwnd )
{
setStyle ( hwnd , getStyle ( hwnd ) & ~ WS_TABSTOP ) ;
setExStyle ( hwnd , getExStyle ( hwnd ) | WS_EX_CONTROLPARENT ) ;
}
void tabLeaveTabNavigation ( HWND hwnd )
{
setExStyle ( hwnd , getExStyle ( hwnd ) & ~ WS_EX_CONTROLPARENT ) ;
setStyle ( hwnd , getStyle ( hwnd ) | WS_TABSTOP ) ;
}