2015-04-12 02:16:11 -05:00
// 12 april 2015
# include "uipriv_windows.h"
struct tab {
2015-04-16 08:32:34 -05:00
uiTab t ;
2015-04-17 10:18:45 -05:00
HWND hwnd ;
2015-05-06 12:37:23 -05:00
struct ptrArray * pages ;
2015-05-10 11:54:11 -05:00
void ( * baseResize ) ( uiControl * , intmax_t , intmax_t , intmax_t , intmax_t , uiSizing * ) ;
2015-05-04 13:42:23 -05:00
void ( * baseEnable ) ( uiControl * ) ;
void ( * baseDisable ) ( uiControl * ) ;
void ( * baseSysFunc ) ( uiControl * , uiControlSysFuncParams * ) ;
2015-04-12 02:16:11 -05:00
} ;
2015-05-06 12:37:23 -05:00
struct tabPage {
2015-05-15 17:54:13 -05:00
uiControl * control ;
2015-05-06 12:37:23 -05:00
int margined ;
} ;
2015-04-12 02:16:11 -05:00
static BOOL onWM_COMMAND ( uiControl * c , WORD code , LRESULT * lResult )
{
return FALSE ;
}
// we have to handle hiding and showing of tab pages ourselves
static BOOL onWM_NOTIFY ( uiControl * c , NMHDR * nm , LRESULT * lResult )
{
2015-04-16 08:32:34 -05:00
struct tab * t = ( struct tab * ) c ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-04-12 02:16:11 -05:00
LRESULT n ;
2015-05-06 12:37:23 -05:00
if ( nm - > code ! = TCN_SELCHANGING & & nm - > code ! = TCN_SELCHANGE )
return FALSE ;
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( n = = ( LRESULT ) ( - 1 ) ) // not changing from/to a page; nothing to do
return FALSE ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
if ( nm - > code = = TCN_SELCHANGING ) {
// changing from a real page
2015-05-15 17:54:13 -05:00
uiControlContainerHide ( page - > control ) ;
2015-04-12 02:16:11 -05:00
* lResult = FALSE ; // and allow the change
return TRUE ;
}
2015-05-06 12:37:23 -05:00
// otherwise it's TCN_SELCHANGE
// and we're changing to a real page
2015-05-15 17:54:13 -05:00
uiControlContainerShow ( page - > control ) ;
uiControlQueueResize ( page - > control ) ;
2015-05-06 12:37:23 -05:00
* lResult = 0 ;
return TRUE ;
2015-04-12 02:16:11 -05:00
}
2015-04-18 17:02:16 -05:00
static void onDestroy ( void * data )
2015-04-12 02:16:11 -05:00
{
2015-04-18 17:02:16 -05:00
struct tab * t = ( struct tab * ) data ;
2015-05-10 20:22:22 -05:00
struct tabPage * page ;
2015-04-12 02:16:11 -05:00
2015-04-29 00:53:39 -05:00
// first, hide the widget to avoid flicker
ShowWindow ( t - > hwnd , SW_HIDE ) ;
// because the pages don't have by a libui paent, we can simply destroy them
// we don't have to worry about the Windows tab control holding a reference to our bin; there is no reference holding anyway
2015-05-06 12:37:23 -05:00
while ( t - > pages - > len ! = 0 ) {
2015-05-10 20:22:22 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , 0 ) ;
2015-05-05 23:52:24 -05:00
// we do have to remove the page from the tab control, though
2015-05-15 17:54:13 -05:00
uiControlSetOSParent ( page - > control , NULL ) ;
uiControlDestroy ( page - > control , NULL ) ;
2015-05-06 12:37:23 -05:00
ptrArrayDelete ( t - > pages , 0 ) ;
2015-05-10 20:22:22 -05:00
uiFree ( page ) ;
2015-05-05 23:52:24 -05:00
}
2015-04-29 00:53:39 -05:00
// and finally destroy ourselves
2015-05-06 12:37:23 -05:00
ptrArrayDestroy ( t - > pages ) ;
2015-04-12 02:16:11 -05:00
uiFree ( t ) ;
}
2015-05-15 17:54:13 -05:00
// from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx
# define tabMargin 7
2015-05-04 13:42:23 -05:00
static void tabPreferredSize ( uiControl * c , uiSizing * d , intmax_t * width , intmax_t * height )
2015-04-12 02:16:11 -05:00
{
2015-05-03 16:13:40 -05:00
struct tab * t = ( struct tab * ) c ;
LRESULT current ;
2015-05-06 12:37:23 -05:00
struct tabPage * curpage ;
2015-05-03 16:13:40 -05:00
intmax_t curwid , curht ;
RECT r ;
r . left = 0 ;
r . top = 0 ;
r . right = 0 ;
r . bottom = 0 ;
2015-05-06 12:37:23 -05:00
if ( t - > pages - > len ! = 0 ) {
2015-05-03 16:13:40 -05:00
current = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( current ! = ( LRESULT ) ( - 1 ) ) {
2015-05-06 12:37:23 -05:00
curpage = ptrArrayIndex ( t - > pages , struct tabPage * , current ) ;
2015-05-15 17:54:13 -05:00
uiControlPreferredSize ( curpage - > control , d , & curwid , & curht ) ;
2015-05-03 16:13:40 -05:00
r . right = curwid ;
r . bottom = curht ;
2015-05-15 17:54:13 -05:00
// TODO add margins
2015-05-03 16:13:40 -05:00
}
}
// otherwise just use the rect [0 0 0 0]
2015-05-03 16:21:48 -05:00
// the following will take the tabs themselves into account
2015-05-03 16:13:40 -05:00
SendMessageW ( t - > hwnd , TCM_ADJUSTRECT , ( WPARAM ) TRUE , ( LPARAM ) ( & r ) ) ;
* width = r . right - r . left ;
* height = r . bottom - r . top ;
2015-04-12 02:16:11 -05:00
}
2015-05-15 17:54:13 -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 current ;
struct tabPage * curpage ;
( * ( t - > baseResize ) ) ( uiControl ( t ) , x , y , width , height , d ) ;
current = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( current ! = ( LRESULT ) ( - 1 ) ) {
curpage = ptrArrayIndex ( t - > pages , struct tabPage * , current ) ;
uiControlQueueResize ( curpage - > control ) ;
}
}
static void tabComputeChildSize ( uiControl * c , intmax_t * x , intmax_t * y , intmax_t * width , intmax_t * height , uiSizing * d )
2015-05-10 11:54:11 -05:00
{
2015-05-15 17:54:13 -05:00
struct tab * t = ( struct tab * ) c ;
2015-05-10 11:54:11 -05:00
LRESULT n ;
RECT r ;
struct tabPage * page ;
2015-05-15 17:54:13 -05:00
// first figure out what the content area for pages is
if ( GetWindowRect ( t - > hwnd , & r ) = = 0 )
logLastError ( " error getting tab window rect in tabComputeChildSize() " ) ;
// convert to the display rectangle
SendMessageW ( t - > hwnd , TCM_ADJUSTRECT , ( WPARAM ) TRUE , ( LPARAM ) ( & r ) ) ;
* x = r . left ;
* y = r . top ;
* width = r . right - r . left ;
* height = r . bottom - r . top ;
2015-05-10 11:54:11 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( n = = ( LRESULT ) ( - 1 ) ) // no child selected; do nothing
return ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
2015-05-15 17:54:13 -05:00
if ( page - > margined ) {
// TODO
}
2015-05-10 11:54:11 -05:00
}
2015-05-04 13:42:23 -05:00
static void tabEnable ( uiControl * c )
{
struct tab * t = ( struct tab * ) c ;
2015-05-10 20:22:22 -05:00
struct tabPage * page ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
( * ( t - > baseEnable ) ) ( uiControl ( t ) ) ;
2015-05-06 12:37:23 -05:00
for ( i = 0 ; i < t - > pages - > len ; i + + ) {
2015-05-10 20:22:22 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , i ) ;
2015-05-15 17:54:13 -05:00
uiControlContainerEnable ( uiControl ( page - > bin ) ) ;
2015-05-06 12:37:23 -05:00
}
2015-05-04 13:42:23 -05:00
}
static void tabDisable ( uiControl * c )
{
struct tab * t = ( struct tab * ) c ;
2015-05-10 20:22:22 -05:00
struct tabPage * page ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
( * ( t - > baseDisable ) ) ( uiControl ( t ) ) ;
2015-05-06 12:37:23 -05:00
for ( i = 0 ; i < t - > pages - > len ; i + + ) {
2015-05-10 20:22:22 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , i ) ;
2015-05-15 17:54:13 -05:00
uiControlContainerDisable ( uiControl ( page - > bin ) ) ;
2015-05-06 12:37:23 -05:00
}
2015-05-04 13:42:23 -05:00
}
static void tabSysFunc ( uiControl * c , uiControlSysFuncParams * p )
{
struct tab * t = ( struct tab * ) c ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-05-04 13:45:18 -05:00
uintmax_t i ;
2015-05-04 13:42:23 -05:00
2015-05-06 17:05:07 -05:00
// we handle tab stops specially
if ( p - > Func = = uiWindowsSysFuncHasTabStops ) {
2015-05-06 17:37:21 -05:00
// if disabled, not a tab stop
if ( IsWindowEnabled ( t - > hwnd ) ! = 0 )
// if there are no tabs, it is not a tab stop
if ( t - > pages - > len ! = 0 )
p - > HasTabStops = TRUE ;
2015-05-06 17:05:07 -05:00
return ;
}
// otherwise distribute it throughout all pages
2015-05-04 13:42:23 -05:00
( * ( t - > baseSysFunc ) ) ( uiControl ( t ) , p ) ;
2015-05-06 12:37:23 -05:00
for ( i = 0 ; i < t - > pages - > len ; i + + ) {
page = ptrArrayIndex ( t - > pages , struct tabPage * , i ) ;
uiControlSysFunc ( uiControl ( page - > bin ) , p ) ;
}
2015-05-04 13:42:23 -05:00
}
2015-05-07 09:14:49 -05:00
// this is also partially where tab navigation is handled
2015-04-12 02:16:11 -05:00
static LRESULT CALLBACK tabSubProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam , UINT_PTR uIdSubclass , DWORD_PTR dwRefData )
{
2015-04-17 10:18:45 -05:00
struct tab * t = ( struct tab * ) dwRefData ;
2015-04-13 09:15:36 -05:00
RECT r ;
2015-05-06 17:37:21 -05:00
LRESULT n ;
uiControlSysFuncParams p ;
struct tabPage * page ;
2015-04-12 02:16:11 -05:00
switch ( uMsg ) {
2015-05-06 17:37:21 -05:00
case msgHasTabStops :
n = SendMessageW ( t - > hwnd , TCM_GETCURSEL , 0 , 0 ) ;
if ( n = = ( LRESULT ) ( - 1 ) ) // no current selection == no tab stops
return FALSE ;
p . Func = uiWindowsSysFuncHasTabStops ;
p . HasTabStops = FALSE ;
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
uiControlSysFunc ( uiControl ( page - > bin ) , & p ) ;
return p . HasTabStops ;
2015-04-12 02:16:11 -05:00
case WM_NCDESTROY :
2015-05-15 17:54:13 -05:00
if ( RemoveWindowSubclass ( hwnd , tabSubProc , uIdSubclass ) = = FALSE )
2015-04-12 02:16:11 -05:00
logLastError ( " error removing Tab resize handling subclass in tabSubProc() " ) ;
break ;
}
2015-05-15 17:54:13 -05:00
return DefSubclassProc ( hwnd , uMsg , wParam , lParam ) ;
2015-04-12 02:16:11 -05:00
}
# define tabCapGrow 32
2015-04-29 00:53:39 -05:00
static void tabAppendPage ( uiTab * tt , const char * name , uiControl * child )
2015-04-12 02:16:11 -05:00
{
2015-04-16 08:32:34 -05:00
struct tab * t = ( struct tab * ) tt ;
2015-04-12 02:16:11 -05:00
TCITEMW item ;
LRESULT n ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-04-12 02:16:11 -05:00
WCHAR * wname ;
2015-05-06 12:37:23 -05:00
page = uiNew ( struct tabPage ) ;
2015-04-17 10:18:45 -05:00
n = SendMessageW ( t - > hwnd , TCM_GETITEMCOUNT , 0 , 0 ) ;
2015-04-12 02:16:11 -05:00
2015-05-15 17:54:13 -05:00
page - > control = child ;
2015-04-12 02:16:11 -05:00
if ( n ! = 0 ) // if this isn't the first page, we have to hide the other controls
2015-05-15 17:54:13 -05:00
uiControlHide ( page - > control ) ;
2015-05-06 12:37:23 -05:00
ptrArrayAppend ( t - > pages , page ) ;
2015-04-12 02:16:11 -05:00
ZeroMemory ( & item , sizeof ( TCITEMW ) ) ;
item . mask = TCIF_TEXT ;
wname = toUTF16 ( name ) ;
item . pszText = wname ;
// MSDN's example code uses the first invalid index directly for this
2015-04-17 10:18:45 -05:00
if ( SendMessageW ( t - > hwnd , TCM_INSERTITEM , ( WPARAM ) n , ( LPARAM ) ( & item ) ) = = ( LRESULT ) - 1 )
2015-04-12 02:16:11 -05:00
logLastError ( " error adding tab to Tab in uiTabAddPage() " ) ;
uiFree ( wname ) ;
2015-04-13 09:15:36 -05:00
// if this is the first tab, Windows will automatically show it /without/ sending a TCN_SELCHANGE notification
// so we need to manually resize the tab ourselves
2015-05-07 09:14:49 -05:00
// don't use uiContainerUpdate() for the same reason as in the TCN_SELCHANGE handler
2015-05-15 17:54:13 -05:00
uiControlQueueResize ( page - > control ) ;
2015-04-12 02:16:11 -05:00
}
2015-04-16 08:32:34 -05:00
2015-05-06 13:09:20 -05:00
static void tabInsertPageBefore ( uiTab * tt , const char * name , uintmax_t n , uiControl * child )
{
struct tab * t = ( struct tab * ) tt ;
TCITEMW item ;
struct tabPage * page ;
WCHAR * wname ;
page = uiNew ( struct tabPage ) ;
2015-05-15 17:54:13 -05:00
page - > control = child ;
2015-05-06 13:09:20 -05:00
// always hide; the current tab doesn't change
2015-05-15 17:54:13 -05:00
uiControlHide ( page - > control ) ;
2015-05-06 13:09:20 -05:00
ptrArrayInsertBefore ( 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 Tab in uiTabInsertPageBefore() " ) ;
uiFree ( wname ) ;
}
2015-04-18 13:41:28 -05:00
static void tabDeletePage ( uiTab * tt , uintmax_t n )
{
struct tab * t = ( struct tab * ) tt ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-04-18 13:41:28 -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 Tab page in tabDeletePage() " ) ;
// now delete the page itself
2015-05-06 12:37:23 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
ptrArrayDelete ( t - > pages , n ) ;
2015-04-18 13:41:28 -05:00
2015-05-15 17:54:13 -05:00
// and keep the page control alive
uiControlSetParent ( page - > control , NULL ) ;
2015-04-29 00:53:39 -05:00
2015-05-06 12:37:23 -05:00
uiFree ( page ) ;
2015-04-18 13:41:28 -05:00
}
2015-04-30 14:50:03 -05:00
static uintmax_t tabNumPages ( uiTab * tt )
{
struct tab * t = ( struct tab * ) tt ;
2015-05-06 12:37:23 -05:00
return t - > pages - > len ;
2015-04-30 14:50:03 -05:00
}
static int tabMargined ( uiTab * tt , uintmax_t n )
{
struct tab * t = ( struct tab * ) tt ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-04-30 14:50:03 -05:00
2015-05-06 12:37:23 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
return page - > margined ;
2015-04-30 14:50:03 -05:00
}
static void tabSetMargined ( uiTab * tt , uintmax_t n , int margined )
{
struct tab * t = ( struct tab * ) tt ;
2015-05-06 12:37:23 -05:00
struct tabPage * page ;
2015-04-30 14:50:03 -05:00
2015-05-06 12:37:23 -05:00
page = ptrArrayIndex ( t - > pages , struct tabPage * , n ) ;
page - > margined = margined ;
2015-05-15 17:54:13 -05:00
uiControlQueueResize ( page - > control ) ;
2015-04-30 14:50:03 -05:00
}
2015-04-16 08:32:34 -05:00
uiTab * uiNewTab ( void )
{
struct tab * t ;
2015-05-02 19:51:00 -05:00
uiWindowsMakeControlParams p ;
2015-04-16 08:32:34 -05:00
t = uiNew ( struct tab ) ;
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 " " ;
2015-05-06 17:37:21 -05:00
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)
2015-04-16 08:32:34 -05:00
p . hInstance = hInstance ;
2015-05-15 20:06:52 -05:00
p . lpParam = NULL ;
2015-04-16 08:32:34 -05:00
p . useStandardControlFont = TRUE ;
p . onWM_COMMAND = onWM_COMMAND ;
p . onWM_NOTIFY = onWM_NOTIFY ;
2015-04-18 17:02:16 -05:00
p . onDestroy = onDestroy ;
p . onDestroyData = t ;
2015-05-02 19:51:00 -05:00
uiWindowsMakeControl ( uiControl ( t ) , & p ) ;
2015-04-16 08:32:34 -05:00
2015-05-01 09:16:02 -05:00
t - > hwnd = ( HWND ) uiControlHandle ( uiControl ( t ) ) ;
2015-05-06 12:37:23 -05:00
t - > pages = newPtrArray ( ) ;
2015-04-17 10:18:45 -05:00
if ( ( * fv_SetWindowSubclass ) ( t - > hwnd , tabSubProc , 0 , ( DWORD_PTR ) t ) = = FALSE )
2015-04-16 08:32:34 -05:00
logLastError ( " error subclassing Tab to give it its own resize handler in uiNewTab() " ) ;
2015-05-04 13:42:23 -05:00
uiControl ( t ) - > PreferredSize = tabPreferredSize ;
2015-05-10 11:54:11 -05:00
t - > baseResize = uiControl ( t ) - > Resize ;
uiControl ( t ) - > Resize = tabResize ;
2015-05-15 17:54:13 -05:00
uiControl ( t ) - > ComputeChildSize = tabComputeChildSize ;
2015-05-04 13:42:23 -05:00
t - > baseEnable = uiControl ( t ) - > Enable ;
uiControl ( t ) - > Enable = tabEnable ;
t - > baseDisable = uiControl ( t ) - > Disable ;
uiControl ( t ) - > Disable = tabDisable ;
2015-05-15 17:54:13 -05:00
// TODO ContainerEnable/ContainerDisable
2015-05-04 13:42:23 -05:00
t - > baseSysFunc = uiControl ( t ) - > SysFunc ;
uiControl ( t ) - > SysFunc = tabSysFunc ;
2015-04-16 08:32:34 -05:00
2015-04-29 00:53:39 -05:00
uiTab ( t ) - > AppendPage = tabAppendPage ;
2015-05-06 13:09:20 -05:00
uiTab ( t ) - > InsertPageBefore = tabInsertPageBefore ;
2015-04-18 13:41:28 -05:00
uiTab ( t ) - > DeletePage = tabDeletePage ;
2015-04-30 14:50:03 -05:00
uiTab ( t ) - > NumPages = tabNumPages ;
uiTab ( t ) - > Margined = tabMargined ;
uiTab ( t ) - > SetMargined = tabSetMargined ;
2015-04-16 08:32:34 -05:00
return uiTab ( t ) ;
}
2015-05-06 17:37:21 -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 ) ;
}