2015-08-31 10:31:42 -05:00
// 16 may 2015
2016-04-23 10:22:46 -05:00
# include "uipriv_windows.hpp"
2015-08-31 10:31:42 -05:00
2016-05-06 15:06:55 -05: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.)
2015-08-31 10:31:42 -05:00
struct uiTab {
uiWindowsControl c ;
2016-04-27 13:21:05 -05:00
HWND hwnd ; // of the outer container
HWND tabHWND ; // of the tab control itself
std : : vector < struct tabPage * > * pages ;
2015-09-02 12:19:34 -05:00
HWND parent ;
2015-08-31 10:31:42 -05:00
} ;
// utility functions
static LRESULT curpage ( uiTab * t )
{
2016-04-27 13:21:05 -05:00
return SendMessageW ( t - > tabHWND , TCM_GETCURSEL , 0 , 0 ) ;
}
static struct tabPage * tabPage ( uiTab * t , intmax_t i )
{
return ( * ( t - > pages ) ) [ i ] ;
}
static void tabPageRect ( uiTab * t , RECT * r )
{
// 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
2016-04-29 16:08:31 -05:00
uiWindowsEnsureGetWindowRect ( t - > tabHWND , r ) ;
SendMessageW ( t - > tabHWND , TCM_ADJUSTRECT , ( WPARAM ) FALSE , ( LPARAM ) r ) ;
2016-04-27 13:21:05 -05:00
// and get it in terms of the container instead of the screen
2016-04-29 16:08:31 -05:00
mapWindowRect ( NULL , t - > hwnd , r ) ;
2016-04-27 13:21:05 -05:00
}
static void tabRelayout ( uiTab * t )
{
struct tabPage * page ;
RECT r ;
2016-04-28 16:36:07 -05:00
// first move the tab control itself
uiWindowsEnsureGetClientRect ( t - > hwnd , & r ) ;
uiWindowsEnsureMoveWindowDuringResize ( t - > tabHWND , r . left , r . top , r . right - r . left , r . bottom - r . top ) ;
// then the current page
2016-04-27 13:21:05 -05: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 ) ;
2015-08-31 10:31:42 -05:00
}
static void showHidePage ( uiTab * t , LRESULT which , int hide )
{
2016-04-27 13:21:05 -05:00
struct tabPage * page ;
2015-08-31 10:31:42 -05:00
if ( which = = ( LRESULT ) ( - 1 ) )
return ;
2016-04-27 13:21:05 -05:00
page = tabPage ( t , which ) ;
2015-08-31 10:31:42 -05:00
if ( hide )
2016-04-27 13:21:05 -05:00
ShowWindow ( page - > hwnd , SW_HIDE ) ;
2015-08-31 10:31:42 -05:00
else {
2016-04-27 13:21:05 -05:00
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
2016-04-28 16:36:07 -05:00
uiWindowsControlMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
2015-08-31 10:31:42 -05:00
}
}
// control implementation
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 ;
}
2016-04-27 13:21:05 -05:00
static void uiTabDestroy ( uiControl * c )
2015-08-31 10:31:42 -05:00
{
2016-04-27 13:21:05 -05:00
uiTab * t = uiTab ( c ) ;
uiControl * child ;
2016-04-29 16:08:31 -05:00
for ( struct tabPage * & page : * ( t - > pages ) ) {
2016-04-27 13:21:05 -05:00
child = page - > child ;
tabPageDestroy ( page ) ;
if ( child ! = NULL ) {
uiControlSetParent ( child , NULL ) ;
uiControlDestroy ( child ) ;
}
2015-08-31 10:31:42 -05:00
}
2016-04-27 13:21:05 -05:00
delete t - > pages ;
uiWindowsUnregisterWM_NOTIFYHandler ( t - > tabHWND ) ;
uiWindowsEnsureDestroyWindow ( t - > tabHWND ) ;
uiWindowsEnsureDestroyWindow ( t - > hwnd ) ;
uiFreeControl ( uiControl ( t ) ) ;
2015-08-31 10:31:42 -05:00
}
2016-04-27 13:21:05 -05:00
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 )
2015-09-02 12:19:34 -05:00
{
uiTab * t = uiTab ( c ) ;
2016-04-27 13:21:05 -05:00
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 ) ;
2015-09-02 12:19:34 -05:00
}
2016-04-27 13:21:05 -05:00
uiWindowsControlDefaultSetParentHWND ( uiTab )
static void uiTabMinimumSize ( uiWindowsControl * c , intmax_t * width , intmax_t * height )
2015-08-31 10:31:42 -05:00
{
uiTab * t = uiTab ( c ) ;
intmax_t pagewid , pageht ;
2016-04-27 13:21:05 -05:00
struct tabPage * page ;
2015-08-31 10:31:42 -05:00
RECT r ;
2015-09-02 15:02:06 -05:00
// only consider the current page
pagewid = 0 ;
pageht = 0 ;
2016-04-27 13:21:05 -05:00
if ( t - > pages - > size ( ) ! = 0 ) {
page = tabPage ( t , curpage ( t ) ) ;
2016-04-29 16:08:31 -05:00
tabPageMinimumSize ( page , & pagewid , & pageht ) ;
2015-08-31 10:31:42 -05:00
}
r . left = 0 ;
r . top = 0 ;
2015-09-02 15:02:06 -05:00
r . right = pagewid ;
r . bottom = pageht ;
2015-08-31 10:31:42 -05:00
// this also includes the tabs themselves
2016-04-29 16:33:56 -05:00
SendMessageW ( t - > tabHWND , TCM_ADJUSTRECT , ( WPARAM ) TRUE , ( LPARAM ) ( & r ) ) ;
2015-08-31 10:31:42 -05:00
* width = r . right - r . left ;
* height = r . bottom - r . top ;
}
2016-04-28 21:33:32 -05:00
static void uiTabMinimumSizeChanged ( uiWindowsControl * c )
2015-08-31 10:31:42 -05:00
{
uiTab * t = uiTab ( c ) ;
2015-09-01 10:46:53 -05:00
2016-04-28 16:36:07 -05:00
if ( uiWindowsControlTooSmall ( uiWindowsControl ( t ) ) ) {
uiWindowsControlContinueMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
return ;
}
tabRelayout ( t ) ;
2015-08-31 10:31:42 -05:00
}
2016-04-28 16:36:07 -05:00
uiWindowsControlDefaultLayoutRect ( uiTab )
2016-04-29 16:08:31 -05:00
uiWindowsControlDefaultAssignControlIDZOrder ( uiTab )
2015-08-31 10:31:42 -05:00
2016-04-27 13:21:05 -05:00
static void tabArrangePages ( uiTab * t )
2015-09-02 11:59:57 -05:00
{
2016-04-27 16:51:33 -05:00
LONG_PTR controlID = 100 ;
HWND insertAfter = NULL ;
2015-09-02 11:59:57 -05:00
2016-04-27 13:55:36 -05:00
// TODO is this first or last?
2016-04-29 16:08:31 -05:00
uiWindowsEnsureAssignControlIDZOrder ( t - > tabHWND , & controlID , & insertAfter ) ;
2016-04-27 13:21:05 -05:00
for ( struct tabPage * & page : * ( t - > pages ) )
2016-04-29 16:08:31 -05:00
uiWindowsEnsureAssignControlIDZOrder ( page - > hwnd , & controlID , & insertAfter ) ;
2015-09-02 11:59:57 -05:00
}
2015-08-31 10:31:42 -05:00
void uiTabAppend ( uiTab * t , const char * name , uiControl * child )
{
2016-04-27 13:21:05 -05:00
uiTabInsertAt ( t , name , t - > pages - > size ( ) , child ) ;
2015-08-31 10:31:42 -05:00
}
void uiTabInsertAt ( uiTab * t , const char * name , uintmax_t n , uiControl * child )
{
2016-04-27 13:21:05 -05:00
struct tabPage * page ;
2015-08-31 10:31:42 -05:00
LRESULT hide , show ;
TCITEMW item ;
WCHAR * wname ;
// see below
hide = curpage ( t ) ;
2016-04-27 13:21:05 -05:00
if ( child ! = NULL )
uiControlSetParent ( child , uiControl ( t ) ) ;
page = newTabPage ( child ) ;
2016-04-29 16:08:31 -05:00
uiWindowsEnsureSetParentHWND ( page - > hwnd , t - > hwnd ) ;
2016-04-27 13:21:05 -05:00
t - > pages - > insert ( t - > pages - > begin ( ) + n , page ) ;
tabArrangePages ( t ) ;
2015-08-31 10:31:42 -05:00
ZeroMemory ( & item , sizeof ( TCITEMW ) ) ;
item . mask = TCIF_TEXT ;
wname = toUTF16 ( name ) ;
item . pszText = wname ;
2016-04-29 16:33:56 -05:00
if ( SendMessageW ( t - > tabHWND , TCM_INSERTITEM , ( WPARAM ) n , ( LPARAM ) ( & item ) ) = = ( LRESULT ) - 1 )
2016-04-23 10:22:46 -05:00
logLastError ( L " error adding tab to uiTab " ) ;
2015-08-31 10:31:42 -05:00
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 ) ;
}
}
void uiTabDelete ( uiTab * t , uintmax_t n )
{
2016-04-29 16:08:31 -05:00
struct tabPage * page ;
2015-08-31 10:31:42 -05:00
// first delete the tab from the tab control
// if this is the current tab, no tab will be selected, which is good
2016-04-29 16:33:56 -05:00
if ( SendMessageW ( t - > tabHWND , TCM_DELETEITEM , ( WPARAM ) n , 0 ) = = FALSE )
2016-04-23 10:22:46 -05:00
logLastError ( L " error deleting uiTab tab " ) ;
2015-08-31 10:31:42 -05:00
// now delete the page itself
2016-04-27 13:21:05 -05:00
page = tabPage ( t , n ) ;
if ( page - > child ! = NULL )
uiControlSetParent ( page - > child , NULL ) ;
tabPageDestroy ( page ) ;
t - > pages - > erase ( t - > pages - > begin ( ) + n ) ;
2015-08-31 10:31:42 -05:00
}
uintmax_t uiTabNumPages ( uiTab * t )
{
2016-04-27 13:21:05 -05:00
return t - > pages - > size ( ) ;
2015-08-31 10:31:42 -05:00
}
int uiTabMargined ( uiTab * t , uintmax_t n )
{
2016-04-27 13:21:05 -05:00
return tabPage ( t , n ) - > margined ;
2015-08-31 10:31:42 -05:00
}
void uiTabSetMargined ( uiTab * t , uintmax_t n , int margined )
{
2016-04-29 16:08:31 -05:00
struct tabPage * page ;
2016-04-27 13:21:05 -05:00
page = tabPage ( t , n ) ;
page - > margined = margined ;
2016-04-27 13:25:52 -05: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
2016-04-29 16:08:31 -05:00
uiWindowsControlMinimumSizeChanged ( uiWindowsControl ( t ) ) ;
2016-04-27 13:21:05 -05:00
}
2015-08-31 10:31:42 -05:00
2016-04-28 16:36:07 -05:00
static void onResize ( uiWindowsControl * c )
2016-04-27 13:21:05 -05:00
{
2016-04-28 16:36:07 -05:00
tabRelayout ( uiTab ( c ) ) ;
2015-08-31 10:31:42 -05:00
}
uiTab * uiNewTab ( void )
{
2015-08-31 16:50:23 -05:00
uiTab * t ;
2015-08-31 10:31:42 -05:00
2016-04-27 13:21:05 -05:00
uiWindowsNewControl ( uiTab , t ) ;
2015-08-31 10:31:42 -05:00
2016-04-28 16:36:07 -05:00
t - > hwnd = uiWindowsMakeContainer ( uiWindowsControl ( t ) , onResize ) ;
2016-04-27 13:21:05 -05:00
t - > tabHWND = uiWindowsEnsureCreateControlHWND ( 0 ,
2015-08-31 10:31:42 -05:00
WC_TABCONTROLW , L " " ,
2015-09-02 08:41:32 -05:00
TCS_TOOLTIPS | WS_TABSTOP ,
2015-08-31 10:31:42 -05:00
hInstance , NULL ,
TRUE ) ;
2016-04-29 16:08:31 -05:00
uiWindowsEnsureSetParentHWND ( t - > tabHWND , t - > hwnd ) ;
2015-08-31 10:31:42 -05:00
2016-04-27 13:21:05 -05:00
uiWindowsRegisterWM_NOTIFYHandler ( t - > tabHWND , onWM_NOTIFY , uiControl ( t ) ) ;
2015-09-02 12:19:34 -05:00
2016-04-27 13:21:05 -05:00
t - > pages = new std : : vector < struct tabPage * > ;
2015-08-31 10:31:42 -05:00
return t ;
}