2017-07-03 09:33:29 +02:00
/* 16 may 2015 */
2017-07-03 06:34:21 +02:00
# include "uipriv_windows.hpp"
2017-07-03 09:33:29 +02:00
/* You don't add controls directly to a tab control on Windows; instead you make them siblings and swap between them on a TCN_SELCHANGING/TCN_SELCHANGE notification pair.
* In addition , you use dialogs because they can be textured properly ; other controls cannot . ( Things will look wrong if the tab background in the current theme is fancy if you just use the tab background by itself ; see http : //stackoverflow.com/questions/30087540/why-are-my-programss-tab-controls-rendering-their-background-in-a-blocky-way-b.) */
2017-07-03 06:34:21 +02:00
struct uiTab {
uiWindowsControl c ;
2017-07-03 09:33:29 +02:00
HWND hwnd ; /* of the outer container */
HWND tabHWND ; /* of the tab control itself */
2017-07-03 06:34:21 +02:00
std : : vector < struct tabPage * > * pages ;
HWND parent ;
} ;
2017-07-03 09:33:29 +02:00
/* utility functions */
2017-07-03 06:34:21 +02:00
static LRESULT curpage ( uiTab * t )
{
return SendMessageW ( t - > tabHWND , TCM_GETCURSEL , 0 , 0 ) ;
}
static struct tabPage * tabPage ( uiTab * t , int i )
{
return ( * ( t - > pages ) ) [ i ] ;
}
static void tabPageRect ( uiTab * t , RECT * r )
{
2017-07-03 09:33:29 +02:00
/* this rect needs to be in parent window coordinates, but TCM_ADJUSTRECT
* wants a window rect , which is screen coordinates
* because we have each page as a sibling of the tab , use the tab ' s
* own rect as the input rect */
2017-07-03 06:34:21 +02:00
uiWindowsEnsureGetWindowRect ( t - > tabHWND , r ) ;
SendMessageW ( t - > tabHWND , TCM_ADJUSTRECT , ( WPARAM ) FALSE , ( LPARAM ) r ) ;
2017-07-03 09:33:29 +02:00
/* and get it in terms of the container instead of the screen */
2017-07-03 06:34:21 +02:00
mapWindowRect ( NULL , t - > hwnd , r ) ;
}
static void tabRelayout ( uiTab * t )
{
struct tabPage * page ;
RECT r ;
2017-07-03 09:33:29 +02:00
/* first move the tab control itself */
2017-07-03 06:34:21 +02:00
uiWindowsEnsureGetClientRect ( t - > hwnd , & r ) ;
uiWindowsEnsureMoveWindowDuringResize ( t - > tabHWND , r . left , r . top , r . right - r . left , r . bottom - r . top ) ;
2017-07-03 09:33:29 +02:00
/* then the current page */
2017-07-03 06:34:21 +02:00
if ( t - > pages - > size ( ) = = 0 )
return ;
page = tabPage ( t , curpage ( t ) ) ;
tabPageRect ( t , & r ) ;
uiWindowsEnsureMoveWindowDuringResize ( page - > hwnd , r . left , r . top , r . right - r . left , r . bottom - r . top ) ;
}
static void showHidePage ( uiTab * t , LRESULT which , int hide )
{
2017-07-03 09:33:29 +02:00
struct tabPage * page = NULL ;
2017-07-03 06:34:21 +02:00
if ( which = = ( LRESULT ) ( - 1 ) )
return ;
page = tabPage ( t , which ) ;
if ( hide )
ShowWindow ( page - > hwnd , SW_HIDE ) ;
2017-07-03 09:33:29 +02:00
else
{
ShowWindow ( page - > hwnd , SW_SHOW ) ;
/* we only resize the current page, so we have to resize it; before we can do that, we need to make sure we are of the right size */
uiWindowsControlMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
}
2017-07-03 06:34:21 +02:00
}
2017-07-03 09:33:29 +02:00
/* control implementation */
2017-07-03 06:34:21 +02:00
static BOOL onWM_NOTIFY ( uiControl * c , HWND hwnd , NMHDR * nm , LRESULT * lResult )
{
uiTab * t = uiTab ( 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 ;
}
static void uiTabDestroy ( uiControl * c )
{
uiTab * t = uiTab ( c ) ;
uiControl * child ;
for ( struct tabPage * & page : * ( t - > pages ) ) {
child = page - > child ;
tabPageDestroy ( page ) ;
if ( child ! = NULL ) {
uiControlSetParent ( child , NULL ) ;
uiControlDestroy ( child ) ;
}
}
delete t - > pages ;
uiWindowsUnregisterWM_NOTIFYHandler ( t - > tabHWND ) ;
uiWindowsEnsureDestroyWindow ( t - > tabHWND ) ;
uiWindowsEnsureDestroyWindow ( t - > hwnd ) ;
uiFreeControl ( uiControl ( t ) ) ;
}
uiWindowsControlDefaultHandle ( uiTab )
uiWindowsControlDefaultParent ( uiTab )
uiWindowsControlDefaultSetParent ( uiTab )
uiWindowsControlDefaultToplevel ( uiTab )
uiWindowsControlDefaultVisible ( uiTab )
uiWindowsControlDefaultShow ( uiTab )
uiWindowsControlDefaultHide ( uiTab )
uiWindowsControlDefaultEnabled ( uiTab )
uiWindowsControlDefaultEnable ( uiTab )
uiWindowsControlDefaultDisable ( uiTab )
static void uiTabSyncEnableState ( uiWindowsControl * c , int enabled )
{
uiTab * t = uiTab ( c ) ;
if ( uiWindowsShouldStopSyncEnableState ( uiWindowsControl ( t ) , enabled ) )
return ;
EnableWindow ( t - > tabHWND , enabled ) ;
for ( struct tabPage * & page : * ( t - > pages ) )
if ( page - > child ! = NULL )
uiWindowsControlSyncEnableState ( uiWindowsControl ( page - > child ) , enabled ) ;
}
uiWindowsControlDefaultSetParentHWND ( uiTab )
static void uiTabMinimumSize ( uiWindowsControl * c , int * width , int * height )
{
RECT r ;
2017-07-03 09:33:29 +02:00
struct tabPage * page = NULL ;
uiTab * t = uiTab ( c ) ;
/* only consider the current page */
int pagewid = 0 ;
int pageht = 0 ;
2017-07-03 06:34:21 +02:00
2017-07-03 09:33:29 +02:00
if ( t - > pages - > size ( ) ! = 0 )
{
2017-07-03 06:34:21 +02:00
page = tabPage ( t , curpage ( t ) ) ;
tabPageMinimumSize ( page , & pagewid , & pageht ) ;
}
2017-07-03 09:33:29 +02:00
r . left = 0 ;
r . top = 0 ;
r . right = pagewid ;
2017-07-03 06:34:21 +02:00
r . bottom = pageht ;
// this also includes the tabs themselves
SendMessageW ( t - > tabHWND , TCM_ADJUSTRECT , ( WPARAM ) TRUE , ( LPARAM ) ( & r ) ) ;
* width = r . right - r . left ;
* height = r . bottom - r . top ;
}
static void uiTabMinimumSizeChanged ( uiWindowsControl * c )
{
2017-07-03 09:33:29 +02:00
uiTab * t = uiTab ( c ) ;
if ( uiWindowsControlTooSmall ( uiWindowsControl ( t ) ) )
{
uiWindowsControlContinueMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
return ;
}
tabRelayout ( t ) ;
2017-07-03 06:34:21 +02:00
}
uiWindowsControlDefaultLayoutRect ( uiTab )
uiWindowsControlDefaultAssignControlIDZOrder ( uiTab )
static void uiTabChildVisibilityChanged ( uiWindowsControl * c )
{
2017-07-03 09:33:29 +02:00
/* TODO eliminate the redundancy */
2017-07-03 06:34:21 +02:00
uiWindowsControlMinimumSizeChanged ( c ) ;
}
static void tabArrangePages ( uiTab * t )
{
LONG_PTR controlID = 100 ;
2017-07-03 09:33:29 +02:00
HWND insertAfter = NULL ;
2017-07-03 06:34:21 +02:00
2017-07-03 09:33:29 +02:00
/* TODO is this first or last? */
2017-07-03 06:34:21 +02:00
uiWindowsEnsureAssignControlIDZOrder ( t - > tabHWND , & controlID , & insertAfter ) ;
for ( struct tabPage * & page : * ( t - > pages ) )
uiWindowsEnsureAssignControlIDZOrder ( page - > hwnd , & controlID , & insertAfter ) ;
}
void uiTabAppend ( uiTab * t , const char * name , uiControl * child )
{
uiTabInsertAt ( t , name , t - > pages - > size ( ) , child ) ;
}
void uiTabInsertAt ( uiTab * t , const char * name , int n , uiControl * child )
{
struct tabPage * page ;
2017-07-03 09:33:29 +02:00
LRESULT show ;
2017-07-03 06:34:21 +02:00
TCITEMW item ;
WCHAR * wname ;
2017-07-03 09:33:29 +02:00
/* see below */
LRESULT hide = curpage ( t ) ;
2017-07-03 06:34:21 +02:00
if ( child ! = NULL )
uiControlSetParent ( child , uiControl ( t ) ) ;
page = newTabPage ( child ) ;
uiWindowsEnsureSetParentHWND ( page - > hwnd , t - > hwnd ) ;
t - > pages - > insert ( t - > pages - > begin ( ) + n , page ) ;
tabArrangePages ( t ) ;
ZeroMemory ( & item , sizeof ( TCITEMW ) ) ;
item . mask = TCIF_TEXT ;
wname = toUTF16 ( name ) ;
item . pszText = wname ;
if ( SendMessageW ( t - > tabHWND , TCM_INSERTITEM , ( WPARAM ) n , ( LPARAM ) ( & item ) ) = = ( LRESULT ) - 1 )
logLastError ( L " error adding tab to uiTab " ) ;
uiFree ( wname ) ;
2017-07-03 09:33:29 +02:00
/* we need to do this because adding the first tab
* doesn ' t send a TCN_SELCHANGE ; it just shows the page */
2017-07-03 06:34:21 +02:00
show = curpage ( t ) ;
2017-07-03 09:33:29 +02:00
if ( show ! = hide )
{
2017-07-03 06:34:21 +02:00
showHidePage ( t , hide , 1 ) ;
showHidePage ( t , show , 0 ) ;
}
}
void uiTabDelete ( uiTab * t , int n )
{
struct tabPage * page ;
2017-07-03 09:33:29 +02:00
/* first delete the tab from the tab control
* if this is the current tab , no tab will be selected , which is good */
2017-07-03 06:34:21 +02:00
if ( SendMessageW ( t - > tabHWND , TCM_DELETEITEM , ( WPARAM ) n , 0 ) = = FALSE )
logLastError ( L " error deleting uiTab tab " ) ;
2017-07-03 09:33:29 +02:00
/* now delete the page itself */
2017-07-03 06:34:21 +02:00
page = tabPage ( t , n ) ;
if ( page - > child ! = NULL )
uiControlSetParent ( page - > child , NULL ) ;
tabPageDestroy ( page ) ;
t - > pages - > erase ( t - > pages - > begin ( ) + n ) ;
}
int uiTabNumPages ( uiTab * t )
{
return t - > pages - > size ( ) ;
}
int uiTabMargined ( uiTab * t , int n )
{
return tabPage ( t , n ) - > margined ;
}
void uiTabSetMargined ( uiTab * t , int n , int margined )
{
2017-07-03 09:33:29 +02:00
struct tabPage * page = tabPage ( t , n ) ;
2017-07-03 06:34:21 +02:00
page - > margined = margined ;
2017-07-03 09:33:29 +02:00
/* even if the page doesn't have a child it might still
* have a new minimum size with margins ; this is the
* easiest way to verify it */
2017-07-03 06:34:21 +02:00
uiWindowsControlMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
}
static void onResize ( uiWindowsControl * c )
{
tabRelayout ( uiTab ( c ) ) ;
}
uiTab * uiNewTab ( void )
{
uiTab * t ;
uiWindowsNewControl ( uiTab , t ) ;
t - > hwnd = uiWindowsMakeContainer ( uiWindowsControl ( t ) , onResize ) ;
t - > tabHWND = uiWindowsEnsureCreateControlHWND ( 0 ,
WC_TABCONTROLW , L " " ,
TCS_TOOLTIPS | WS_TABSTOP ,
hInstance , NULL ,
TRUE ) ;
uiWindowsEnsureSetParentHWND ( t - > tabHWND , t - > hwnd ) ;
uiWindowsRegisterWM_NOTIFYHandler ( t - > tabHWND , onWM_NOTIFY , uiControl ( t ) ) ;
t - > pages = new std : : vector < struct tabPage * > ;
return t ;
}