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-30 18:54:01 -05:00
// - container update state
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-29 17:03:24 -05:00
void ( * baseCommitDestroy ) ( uiControl * ) ;
2015-05-16 19:28:35 -05:00
} ;
2015-05-29 17:03:24 -05:00
uiDefineControlType ( uiTab , uiTypeTab , struct tab )
2015-05-18 10:41:09 -05:00
// 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 )
{
2015-05-30 11:28:52 -05:00
uiControl * page ;
2015-05-18 10:41:09 -05:00
if ( which = = ( LRESULT ) ( - 1 ) )
return ;
2015-05-30 11:28:52 -05:00
page = ptrArrayIndex ( t - > pages , uiControl * , which ) ;
2015-05-18 10:41:09 -05:00
if ( hide )
2015-05-30 11:28:52 -05:00
uiControlHide ( page ) ;
2015-05-18 10:41:09 -05:00
else {
2015-05-30 11:28:52 -05:00
uiControlShow ( page ) ;
2015-05-18 10:41:09 -05:00
// we only resize the current page, so we have to do this here
2015-05-30 11:28:52 -05:00
uiControlQueueResize ( page ) ;
2015-05-18 10:41:09 -05:00
}
}
// 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
}
2015-05-29 17:03:24 -05:00
static void tabCommitDestroy ( uiControl * c )
2015-05-16 19:28:35 -05:00
{
2015-05-29 17:03:24 -05:00
struct tab * t = ( struct tab * ) c ;
2015-06-01 16:21:21 -05:00
uiControl * page ;
2015-05-29 17:03:24 -05:00
2015-06-01 16:21:21 -05:00
while ( t - > pages - > len ! = 0 ) {
page = ptrArrayIndex ( t - > pages , uiControl * , 0 ) ;
ptrArrayDelete ( t - > pages , 0 ) ;
2015-06-01 16:37:43 -05:00
tabPageDestroyChild ( page ) ;
2015-06-01 16:21:21 -05:00
uiControlSetParent ( page , NULL ) ;
uiControlDestroy ( page ) ;
}
ptrArrayDestroy ( t - > pages ) ;
2015-05-29 17:03:24 -05:00
uiWindowsUnregisterWM_NOTIFYHandler ( t - > hwnd ) ;
( * ( t - > baseCommitDestroy ) ) ( uiControl ( t ) ) ;
2015-05-16 19:28:35 -05:00
}
2015-05-29 18:48:27 -05:00
static uintptr_t tabHandle ( uiControl * c )
{
struct tab * t = ( struct tab * ) c ;
return ( uintptr_t ) ( t - > hwnd ) ;
}
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 ;
2015-05-30 11:34:39 -05:00
uiControl * page ;
2015-05-18 10:41:09 -05:00
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 ;
2015-05-30 11:34:39 -05:00
page = ptrArrayIndex ( t - > pages , uiControl * , 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-30 11:34:39 -05:00
uiControlResize ( page , r . left , r . top , r . right - r . left , r . bottom - r . top , dchild ) ;
2015-05-18 11:04:52 -05:00
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 ;
2015-05-30 11:28:52 -05:00
uiControl * page ;
2015-05-18 10:41:09 -05:00
LRESULT hide , show ;
TCITEMW item ;
WCHAR * wname ;
// see below
hide = curpage ( t ) ;
2015-05-30 11:28:52 -05:00
page = newTabPage ( child ) ;
uiControlSetParent ( page , uiControl ( t ) ) ;
2015-05-18 11:04:52 -05:00
// and make it invisible at first; we show it later if needed
2015-05-30 11:28:52 -05:00
uiControlHide ( page ) ;
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 ;
2015-05-30 11:28:52 -05:00
uiControl * page ;
2015-05-18 14:11:59 -05:00
// 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
2015-05-30 11:28:52 -05:00
page = ptrArrayIndex ( t - > pages , uiControl * , n ) ;
2015-05-18 14:11:59 -05:00
ptrArrayDelete ( t - > pages , n ) ;
2015-05-30 11:28:52 -05:00
// and free the page
// this will keep the control alive
uiControlSetParent ( page , NULL ) ;
uiControlDestroy ( 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 ;
2015-05-30 11:28:52 -05:00
uiControl * page ;
2015-05-18 12:03:22 -05:00
2015-05-30 11:28:52 -05:00
page = ptrArrayIndex ( t - > pages , uiControl * , n ) ;
2015-05-31 09:53:20 -05:00
return tabPageMargined ( page ) ;
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 ;
2015-05-30 11:28:52 -05:00
uiControl * page ;
2015-05-18 12:03:22 -05:00
2015-05-30 11:28:52 -05:00
page = ptrArrayIndex ( t - > pages , uiControl * , n ) ;
2015-05-31 09:53:20 -05:00
tabPageSetMargined ( page , margined ) ;
uiControlQueueResize ( page ) ;
2015-05-16 19:28:35 -05:00
}
uiTab * uiNewTab ( void )
{
struct tab * t ;
2015-05-29 17:03:24 -05:00
t = ( struct tab * ) uiWindowsNewSingleHWNDControl ( uiTypeTab ( ) ) ;
2015-05-29 19:53:12 -05:00
t - > hwnd = uiWindowsUtilCreateControlHWND ( 0 , // don't set WS_EX_CONTROLPARENT yet; we do that dynamically in the message loop (see main_windows.c)
2015-05-29 17:03:24 -05:00
WC_TABCONTROLW , L " " ,
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)
hInstance , NULL ,
TRUE ) ;
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-29 18:48:27 -05:00
uiControl ( t ) - > Handle = tabHandle ;
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-29 17:03:24 -05:00
t - > baseCommitDestroy = uiControl ( t ) - > CommitDestroy ;
uiControl ( t ) - > CommitDestroy = tabCommitDestroy ;
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 ) ;
}