896 lines
23 KiB
C++
Executable File
896 lines
23 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
#include "console/console.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "dgl/dgl.h"
|
|
#include "console/simBase.h"
|
|
#include "gui/core/guiCanvas.h"
|
|
#include "gui/containers/guiTabBookCtrl.h"
|
|
#include "platform/event.h"
|
|
#include "core/fileStream.h"
|
|
#include "gui/containers/guiScrollCtrl.h"
|
|
#include "gui/editor/guiEditCtrl.h"
|
|
#include "gui/controls/guiPopUpCtrl.h"
|
|
#include "gui/core/guiDefaultControlRender.h"
|
|
|
|
|
|
// So we can set tab alignment via gui editor
|
|
static EnumTable::Enums tabAlignEnums[] =
|
|
{
|
|
{ GuiTabBookCtrl::AlignTop, "Top" },
|
|
{ GuiTabBookCtrl::AlignLeft, "Left" },
|
|
{ GuiTabBookCtrl::AlignBottom,"Bottom" },
|
|
{ GuiTabBookCtrl::AlignRight, "Right" }
|
|
};
|
|
static EnumTable gTabAlignEnums(4,&tabAlignEnums[0]);
|
|
|
|
|
|
IMPLEMENT_CONOBJECT(GuiTabBookCtrl);
|
|
|
|
GuiTabBookCtrl::GuiTabBookCtrl()
|
|
{
|
|
VECTOR_SET_ASSOCIATION(mPages);
|
|
mTabHeight = 24;
|
|
mLastTabHeight = mTabHeight;
|
|
mTabPosition = GuiTabBookCtrl::AlignTop;
|
|
mLastTabPosition = mTabPosition;
|
|
mActivePage = NULL;
|
|
mHoverTab = NULL;
|
|
mHasTexture = false;
|
|
mBitmapBounds = NULL;
|
|
mBounds.extent.set( 400, 300 );
|
|
mPageRect = RectI(0,0,0,0);
|
|
mTabRect = RectI(0,0,0,0);
|
|
|
|
mPages.reserve(12);
|
|
mTabMargin = 7;
|
|
mMinTabWidth = 64;
|
|
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
addField("TabPosition", TypeEnum, Offset(mTabPosition,GuiTabBookCtrl), 1, &gTabAlignEnums );
|
|
addField("TabHeight", TypeS32, Offset(mTabHeight,GuiTabBookCtrl) );
|
|
addField("TabMargin", TypeS32, Offset(mTabMargin,GuiTabBookCtrl));
|
|
addField("MinTabWidth", TypeS32, Offset(mMinTabWidth,GuiTabBookCtrl));
|
|
}
|
|
|
|
|
|
// Empty for now, will implement for handling design time context menu for manipulating pages
|
|
ConsoleMethod( GuiTabBookCtrl, addPage, void, 2, 2, "(no arguments expected)")
|
|
{
|
|
object->addNewPage();
|
|
}
|
|
|
|
//ConsoleMethod( GuiTabBookCtrl, removePage, void, 2, 2, "()")
|
|
//{
|
|
//}
|
|
|
|
|
|
bool GuiTabBookCtrl::onAdd()
|
|
{
|
|
Parent::onAdd();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void GuiTabBookCtrl::onRemove()
|
|
{
|
|
Parent::onRemove();
|
|
}
|
|
|
|
void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
|
|
{
|
|
for (S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
GuiTabPageCtrl* tab = mPages[i].Page;
|
|
if( tab == child )
|
|
{
|
|
if( tab == mActivePage )
|
|
mActivePage = NULL;
|
|
mPages.erase( i );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( mPages.empty() )
|
|
mActivePage = NULL;
|
|
else if (mActivePage == NULL )
|
|
mActivePage = static_cast<GuiTabPageCtrl*>(mPages[0].Page);
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::onChildAdded( GuiControl *child )
|
|
{
|
|
GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
|
|
if( !page )
|
|
{
|
|
Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
|
|
SimObject *simObj = reinterpret_cast<SimObject*>(child);
|
|
removeObject( simObj );
|
|
if( mActivePage )
|
|
{
|
|
mActivePage->addObject( simObj );
|
|
}
|
|
else
|
|
{
|
|
Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
|
|
GuiControl *rent = getParent();
|
|
if( rent )
|
|
rent->addObject( simObj );
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
TabHeaderInfo newPage;
|
|
|
|
newPage.Page = page;
|
|
newPage.TabRow = -1;
|
|
newPage.TabColumn = -1;
|
|
|
|
mPages.push_back( newPage );
|
|
|
|
// Calculate Page Information
|
|
calculatePageTabs();
|
|
|
|
child->resize( mPageRect.point, mPageRect.extent );
|
|
|
|
|
|
// Select this Page
|
|
selectPage( page );
|
|
}
|
|
|
|
|
|
bool GuiTabBookCtrl::onWake()
|
|
{
|
|
if (! Parent::onWake())
|
|
return false;
|
|
|
|
mHasTexture = mProfile->constructBitmapArray();
|
|
if( mHasTexture )
|
|
mBitmapBounds = mProfile->mBitmapArrayRects.address();
|
|
|
|
return true;
|
|
}
|
|
|
|
void GuiTabBookCtrl::onSleep()
|
|
{
|
|
Parent::onSleep();
|
|
}
|
|
|
|
|
|
void GuiTabBookCtrl::addNewPage()
|
|
{
|
|
char textbuf[1024];
|
|
|
|
GuiTabPageCtrl * page = new GuiTabPageCtrl();
|
|
|
|
page->setField("profile", "GuiTabPageProfile");
|
|
|
|
dSprintf(textbuf, sizeof(textbuf), "TabBookPage%d_%d", getId(), page->getId());
|
|
page->registerObject(textbuf);
|
|
|
|
this->addObject( page );
|
|
}
|
|
|
|
void GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
|
|
{
|
|
Parent::resize( newPosition, newExtent );
|
|
|
|
calculatePageTabs();
|
|
|
|
// Resize Children
|
|
SimSet::iterator i;
|
|
for(i = begin(); i != end(); i++)
|
|
{
|
|
GuiControl *ctrl = static_cast<GuiControl *>(*i);
|
|
ctrl->resize( mPageRect.point, mPageRect.extent );
|
|
}
|
|
}
|
|
|
|
void GuiTabBookCtrl::childResized(GuiControl *child)
|
|
{
|
|
//Parent::childResized( child );
|
|
|
|
child->resize( mPageRect.point, mPageRect.extent );
|
|
}
|
|
|
|
void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
|
|
{
|
|
Point2I localMouse = globalToLocalCoord( event.mousePoint );
|
|
if( mTabRect.pointInRect( localMouse ) )
|
|
{
|
|
GuiTabPageCtrl *tab = findHitTab( localMouse );
|
|
if( tab != NULL )
|
|
selectPage( tab );
|
|
}
|
|
}
|
|
|
|
void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
|
|
{
|
|
|
|
Point2I localMouse = globalToLocalCoord( event.mousePoint );
|
|
if( mTabRect.pointInRect( localMouse ) )
|
|
{
|
|
|
|
GuiTabPageCtrl *tab = findHitTab( localMouse );
|
|
if( tab != NULL && mHoverTab != tab )
|
|
mHoverTab = tab;
|
|
else if ( !tab )
|
|
mHoverTab = NULL;
|
|
}
|
|
Parent::onMouseMove( event );
|
|
}
|
|
|
|
void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
|
|
{
|
|
mHoverTab = NULL;
|
|
}
|
|
|
|
bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
|
|
{
|
|
bool handled = false;
|
|
Point2I localMouse = globalToLocalCoord( event.mousePoint );
|
|
|
|
if( mTabRect.pointInRect( localMouse ) )
|
|
{
|
|
GuiTabPageCtrl *tab = findHitTab( localMouse );
|
|
if( tab != NULL )
|
|
{
|
|
selectPage( tab );
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
// This shouldn't be called if it's not design time, but check just incase
|
|
if ( GuiControl::smDesignTime )
|
|
{
|
|
// If we clicked in the editor and our addset is the tab book
|
|
// ctrl, select the child ctrl so we can edit it's properties
|
|
GuiEditCtrl* edit = GuiControl::smEditorHandle;
|
|
if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL )
|
|
edit->select( mActivePage );
|
|
}
|
|
|
|
// Return whether we handled this or not.
|
|
return handled;
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::onPreRender()
|
|
{
|
|
// sometimes we need to resize because of a changed persistent field
|
|
// that's what this does
|
|
solveDirty();
|
|
}
|
|
|
|
void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
|
|
{
|
|
RectI tabRect = mTabRect;
|
|
tabRect.point += offset;
|
|
RectI pageRect = mPageRect;
|
|
pageRect.point += offset;
|
|
|
|
// We're so nice we'll store the old modulation before we clear it for our rendering! :)
|
|
ColorI oldModulation;
|
|
dglGetBitmapModulation( &oldModulation );
|
|
|
|
// Wipe it out
|
|
dglClearBitmapModulation();
|
|
|
|
// Render background
|
|
renderBackground( offset, updateRect );
|
|
|
|
// Render our tabs
|
|
renderTabs( offset );
|
|
|
|
// Render Children
|
|
renderChildControls( offset, updateRect );
|
|
|
|
// Restore old modulation
|
|
dglSetBitmapModulation( oldModulation );
|
|
}
|
|
|
|
void GuiTabBookCtrl::renderBackground( Point2I offset, const RectI& updateRect )
|
|
{
|
|
RectI winRect;
|
|
winRect.point = offset;
|
|
winRect.extent = mBounds.extent;
|
|
|
|
if( mHasTexture && mProfile->mBitmapArrayRects.size() >= NumBitmaps)
|
|
renderSizableBitmapBordersFilledIndex( winRect, TabBackground, mProfile );
|
|
else
|
|
dglDrawRectFill(winRect, mProfile->mFillColor);
|
|
|
|
}
|
|
|
|
|
|
void GuiTabBookCtrl::renderTabs( const Point2I &offset )
|
|
{
|
|
// If the tab size is zero, don't render tabs,
|
|
// and assume it's a tab-less tab-book - JDD
|
|
if( mPages.empty() || mTabHeight <= 0 )
|
|
return;
|
|
|
|
for( S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
RectI tabBounds = mPages[i].TabRect;
|
|
tabBounds.point += offset;
|
|
GuiTabPageCtrl *tab = mPages[i].Page;
|
|
if( tab != NULL )
|
|
renderTab( tabBounds, tab );
|
|
}
|
|
}
|
|
|
|
void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab )
|
|
{
|
|
StringTableEntry text = tab->getText();
|
|
ColorI oldColor;
|
|
|
|
dglGetBitmapModulation( &oldColor );
|
|
|
|
// Is this a skinned control?
|
|
if( mHasTexture && mProfile->mBitmapArrayRects.size() >= NumBitmaps)
|
|
{
|
|
S32 indexMultiplier = 1;
|
|
switch( mTabPosition )
|
|
{
|
|
case AlignTop:
|
|
case AlignBottom:
|
|
|
|
if ( mActivePage == tab )
|
|
indexMultiplier += TabSelected;
|
|
else if( mHoverTab == tab )
|
|
indexMultiplier += TabHover;
|
|
else
|
|
indexMultiplier += TabNormal;
|
|
|
|
//dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignBottom ) ? GFlip_Y : 0 );
|
|
break;
|
|
case AlignLeft:
|
|
case AlignRight:
|
|
if ( mActivePage == tab )
|
|
indexMultiplier += TabSelectedVertical;
|
|
else if( mHoverTab == tab )
|
|
indexMultiplier += TabHoverVertical;
|
|
else
|
|
indexMultiplier += TabNormalVertical;
|
|
|
|
//dglDrawBitmapStretchSR(mProfile->mTextureHandle,tabRect,stretchRect, ( mTabPosition == AlignRight ) ? GFlip_X : 0 );
|
|
break;
|
|
}
|
|
|
|
renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile );
|
|
}
|
|
else
|
|
{
|
|
// If this isn't a skinned control or the bitmap is simply missing, handle it WELL
|
|
if ( mActivePage == tab )
|
|
dglDrawRectFill(tabRect, mProfile->mFillColor);
|
|
else if( mHoverTab == tab )
|
|
dglDrawRectFill(tabRect, mProfile->mFillColorHL);
|
|
else
|
|
dglDrawRectFill(tabRect, mProfile->mFillColorNA);
|
|
|
|
}
|
|
|
|
|
|
dglSetBitmapModulation(mProfile->mFontColor);
|
|
|
|
switch( mTabPosition )
|
|
{
|
|
case AlignTop:
|
|
case AlignBottom:
|
|
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, 0);
|
|
break;
|
|
case AlignLeft:
|
|
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
|
|
break;
|
|
case AlignRight:
|
|
renderJustifiedTextRot( tabRect.point, tabRect.extent, text, -90 );
|
|
break;
|
|
}
|
|
|
|
dglSetBitmapModulation( oldColor);
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::renderJustifiedTextRot(Point2I offset, Point2I extent, const char *text, F32 rot )
|
|
{
|
|
GFont *font = mProfile->mFont;
|
|
S32 textWidth = font->getStrWidth(text);
|
|
Point2I start;
|
|
|
|
offset += mProfile->mTextOffset;
|
|
|
|
if( mTabPosition == AlignLeft || mTabPosition == AlignRight )
|
|
{
|
|
switch( mProfile->mAlignment )
|
|
{
|
|
case GuiControlProfile::RightJustify:
|
|
start.set( 0, extent.y - textWidth);
|
|
break;
|
|
case GuiControlProfile::CenterJustify:
|
|
start.set( 0, ( extent.y - textWidth) / 2);
|
|
break;
|
|
default:
|
|
// GuiControlProfile::LeftJustify
|
|
start.set( 0, 0 );
|
|
break;
|
|
}
|
|
|
|
if( textWidth > extent.y )
|
|
start.set( 0, 0 );
|
|
start.x = ( ( extent.x - font->getHeight() ) / 2 ) + font->getHeight();
|
|
|
|
}
|
|
else
|
|
{
|
|
// align the horizontal
|
|
switch( mProfile->mAlignment )
|
|
{
|
|
case GuiControlProfile::RightJustify:
|
|
start.set( extent.x - textWidth, 0 );
|
|
break;
|
|
case GuiControlProfile::CenterJustify:
|
|
start.set( ( extent.x - textWidth) / 2, 0 );
|
|
break;
|
|
default:
|
|
// GuiControlProfile::LeftJustify
|
|
start.set( 0, 0 );
|
|
break;
|
|
}
|
|
|
|
if( textWidth > extent.x )
|
|
start.set( 0, 0 );
|
|
start.y = ( extent.y - font->getHeight() ) / 2;
|
|
|
|
}
|
|
|
|
dglDrawText( font, start + offset, text, mProfile->mFontColors,9,rot );
|
|
}
|
|
|
|
// This is nothing but a clever hack to allow the tab page children
|
|
// to cast this to a GuiControl* so that the file doesn't need to have circular
|
|
// includes. generic method overriding for the win!
|
|
void GuiTabBookCtrl::setUpdate()
|
|
{
|
|
Parent::setUpdate();
|
|
|
|
setUpdateRegion(Point2I(0,0), mBounds.extent);
|
|
|
|
calculatePageTabs();
|
|
}
|
|
|
|
void GuiTabBookCtrl::solveDirty()
|
|
{
|
|
bool dirty = false;
|
|
if( mTabPosition != mLastTabPosition )
|
|
{
|
|
mLastTabPosition = mTabPosition;
|
|
dirty = true;
|
|
}
|
|
|
|
if( mTabHeight != mLastTabHeight )
|
|
{
|
|
mLastTabHeight = mTabHeight;
|
|
dirty = true;
|
|
}
|
|
|
|
if( mTabWidth != mLastTabWidth )
|
|
{
|
|
mLastTabWidth = mTabWidth;
|
|
dirty = true;
|
|
}
|
|
|
|
if( dirty )
|
|
{
|
|
calculatePageTabs();
|
|
resize( mBounds.point, mBounds.extent );
|
|
}
|
|
|
|
}
|
|
|
|
S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
|
|
{
|
|
if( !page )
|
|
return mTabWidth;
|
|
|
|
StringTableEntry text = page->getText();
|
|
|
|
if( !text || dStrlen(text) == 0 || mProfile->mFont == NULL )
|
|
return mTabWidth;
|
|
|
|
GFont *font = mProfile->mFont;
|
|
|
|
return font->getStrNWidth( text, dStrlen(text) );
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::calculatePageTabs()
|
|
{
|
|
// Short Circuit.
|
|
//
|
|
// If the tab size is zero, don't render tabs,
|
|
// and assume it's a tab-less tab-book - JDD
|
|
if( mPages.empty() || mTabHeight <= 0 )
|
|
return;
|
|
|
|
S32 currRow = 0;
|
|
S32 currColumn = 0;
|
|
S32 maxRow = 0;
|
|
S32 maxColumn = 0;
|
|
S32 currX = 0;
|
|
S32 currY = 0;
|
|
S32 maxWidth = 0;
|
|
S32 maxHeight = 0;
|
|
|
|
for( S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
// Fetch Tab Width
|
|
S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 );
|
|
tabWidth = getMax( tabWidth, mMinTabWidth );
|
|
TabHeaderInfo &info = mPages[i];
|
|
switch( mTabPosition )
|
|
{
|
|
case AlignTop:
|
|
case AlignBottom:
|
|
// If we're going to go outside our bounds
|
|
// with this tab move it down a row
|
|
if( currX + tabWidth > mBounds.extent.x )
|
|
{
|
|
// Calculate and Advance State.
|
|
maxWidth = getMax( tabWidth, maxWidth );
|
|
balanceRow( currRow, currX );
|
|
info.TabRow = ++currRow;
|
|
// Reset Necessaries
|
|
info.TabColumn = currColumn = maxWidth = currX = 0;
|
|
}
|
|
else
|
|
{
|
|
info.TabRow = currRow;
|
|
info.TabColumn = currColumn++;
|
|
}
|
|
|
|
// Calculate Tabs Bounding Rect
|
|
info.TabRect.point.x = currX;
|
|
info.TabRect.extent.x = tabWidth;
|
|
info.TabRect.extent.y = mTabHeight;
|
|
|
|
// Adjust Y Point based on alignment
|
|
if( mTabPosition == AlignTop )
|
|
info.TabRect.point.y = ( info.TabRow * mTabHeight );
|
|
else
|
|
info.TabRect.point.y = mBounds.extent.y - ( ( 1 + info.TabRow ) * mTabHeight );
|
|
|
|
currX += tabWidth;
|
|
break;
|
|
case AlignLeft:
|
|
case AlignRight:
|
|
// If we're going to go outside our bounds
|
|
// with this tab move it down a row
|
|
if( currY + tabWidth > mBounds.extent.y )
|
|
{
|
|
|
|
// Balance Tab Column.
|
|
balanceColumn( currColumn, currY );
|
|
|
|
// Calculate and Advance State.
|
|
info.TabColumn = ++currColumn;
|
|
info.TabRow = currRow = currY = 0;
|
|
}
|
|
else
|
|
{
|
|
info.TabColumn = currColumn;
|
|
info.TabRow = currRow++;
|
|
}
|
|
|
|
// Calculate Tabs Bounding Rect
|
|
info.TabRect.point.y = currY;
|
|
info.TabRect.extent.y = tabWidth;
|
|
info.TabRect.extent.x = mTabHeight;
|
|
|
|
// Adjust Y Point based on alignment
|
|
if( mTabPosition == AlignLeft )
|
|
info.TabRect.point.x = ( info.TabColumn * mTabHeight );
|
|
else
|
|
info.TabRect.point.x = mBounds.extent.x - ( (1 + info.TabColumn) * mTabHeight );
|
|
|
|
currY += tabWidth;
|
|
break;
|
|
};
|
|
}
|
|
|
|
currRow++;
|
|
currColumn++;
|
|
|
|
Point2I localPoint = mBounds.extent;
|
|
|
|
// Calculate
|
|
switch( mTabPosition )
|
|
{
|
|
case AlignTop:
|
|
|
|
localPoint.y -= mBounds.point.y;
|
|
|
|
mTabRect.point.x = 0;
|
|
mTabRect.extent.x = localPoint.x;
|
|
mTabRect.point.y = 0;
|
|
mTabRect.extent.y = currRow * mTabHeight;
|
|
|
|
mPageRect.point.x = 0;
|
|
mPageRect.point.y = mTabRect.extent.y;
|
|
mPageRect.extent.x = mTabRect.extent.x;
|
|
mPageRect.extent.y = mBounds.extent.y - mTabRect.extent.y;
|
|
|
|
break;
|
|
case AlignBottom:
|
|
mTabRect.point.x = 0;
|
|
mTabRect.extent.x = localPoint.x;
|
|
mTabRect.extent.y = currRow * mTabHeight;
|
|
mTabRect.point.y = mBounds.extent.y - mTabRect.extent.y;
|
|
|
|
mPageRect.point.x = 0;
|
|
mPageRect.point.y = 0;
|
|
mPageRect.extent.x = mTabRect.extent.x;
|
|
mPageRect.extent.y = localPoint.y - mTabRect.extent.y;
|
|
|
|
break;
|
|
case AlignLeft:
|
|
|
|
|
|
mTabRect.point.y = 0;
|
|
mTabRect.extent.y = mBounds.extent.y;
|
|
mTabRect.point.x = 0;
|
|
mTabRect.extent.x = currColumn * mTabHeight;
|
|
|
|
mPageRect.point.y = 0;
|
|
mPageRect.point.x = mTabRect.extent.x;
|
|
mPageRect.extent.y = localPoint.y;
|
|
mPageRect.extent.x = localPoint.x - mTabRect.extent.x;
|
|
|
|
break;
|
|
case AlignRight:
|
|
|
|
|
|
mTabRect.extent.x = currColumn * mTabHeight;
|
|
mTabRect.point.y = 0;
|
|
mTabRect.extent.y = localPoint.y;
|
|
mTabRect.point.x = localPoint.x - mTabRect.extent.x;
|
|
|
|
mPageRect.point.y = 0;
|
|
mPageRect.point.x = 0;
|
|
mPageRect.extent.y = localPoint.y;
|
|
mPageRect.extent.x = localPoint.x - mTabRect.extent.x;
|
|
|
|
break;
|
|
};
|
|
|
|
|
|
}
|
|
|
|
void GuiTabBookCtrl::balanceColumn( S32 column , S32 totalTabWidth )
|
|
{
|
|
// Short Circuit.
|
|
//
|
|
// If the tab size is zero, don't render tabs,
|
|
// and assume it's a tab-less tab-book - JDD
|
|
if( mPages.empty() || mTabHeight <= 0 )
|
|
return;
|
|
|
|
Vector<TabHeaderInfo*> rowTemp;
|
|
rowTemp.clear();
|
|
|
|
for( S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
TabHeaderInfo &info = mPages[i];
|
|
|
|
if(info.TabColumn == column )
|
|
rowTemp.push_back( &mPages[i] );
|
|
}
|
|
|
|
if( rowTemp.empty() )
|
|
return;
|
|
|
|
// Balance the tabs across the remaining space
|
|
S32 spaceToDivide = mBounds.extent.y - totalTabWidth;
|
|
S32 pointDelta = 0;
|
|
for( S32 i = 0; i < rowTemp.size(); i++ )
|
|
{
|
|
TabHeaderInfo &info = *rowTemp[i];
|
|
S32 extraSpace = (S32)( spaceToDivide / rowTemp.size() );
|
|
info.TabRect.extent.y += extraSpace;
|
|
info.TabRect.point.y += pointDelta;
|
|
pointDelta += extraSpace;
|
|
}
|
|
|
|
}
|
|
void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth )
|
|
{
|
|
// Short Circuit.
|
|
//
|
|
// If the tab size is zero, don't render tabs,
|
|
// and assume it's a tab-less tab-book - JDD
|
|
if( mPages.empty() || mTabHeight <= 0 )
|
|
return;
|
|
|
|
Vector<TabHeaderInfo*> rowTemp;
|
|
rowTemp.clear();
|
|
|
|
for( S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
TabHeaderInfo &info = mPages[i];
|
|
|
|
if(info.TabRow == row )
|
|
rowTemp.push_back( &mPages[i] );
|
|
}
|
|
|
|
if( rowTemp.empty() )
|
|
return;
|
|
|
|
// Balance the tabs across the remaining space
|
|
S32 spaceToDivide = mBounds.extent.x - totalTabWidth;
|
|
S32 pointDelta = 0;
|
|
for( S32 i = 0; i < rowTemp.size(); i++ )
|
|
{
|
|
TabHeaderInfo &info = *rowTemp[i];
|
|
S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() );
|
|
info.TabRect.extent.x += extraSpace;
|
|
info.TabRect.point.x += pointDelta;
|
|
pointDelta += extraSpace;
|
|
}
|
|
}
|
|
|
|
|
|
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
|
|
{
|
|
return findHitTab( event.mousePoint );
|
|
}
|
|
|
|
GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
|
|
{
|
|
// Short Circuit.
|
|
//
|
|
// If the tab size is zero, don't render tabs,
|
|
// and assume it's a tab-less tab-book - JDD
|
|
if( mPages.empty() || mTabHeight <= 0 )
|
|
return NULL;
|
|
|
|
for( S32 i = 0; i < mPages.size(); i++ )
|
|
{
|
|
if( mPages[i].TabRect.pointInRect( hitPoint ) )
|
|
return mPages[i].Page;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ConsoleMethod( GuiTabBookCtrl, selectPage, void, 3, 3, "(int pageIndex)")
|
|
{
|
|
S32 pageIndex = dAtoi(argv[2]);
|
|
|
|
object->selectPage(pageIndex);
|
|
}
|
|
|
|
void GuiTabBookCtrl::selectPage( S32 index )
|
|
{
|
|
if( mPages.size() < index )
|
|
return;
|
|
|
|
// Select the page
|
|
selectPage( mPages[ index ].Page );
|
|
}
|
|
|
|
|
|
void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
|
|
{
|
|
Vector<TabHeaderInfo>::iterator i = mPages.begin();
|
|
for( ; i != mPages.end() ; i++ )
|
|
{
|
|
GuiTabPageCtrl *tab = reinterpret_cast<GuiTabPageCtrl*>((*i).Page);
|
|
if( page == tab )
|
|
{
|
|
mActivePage = tab;
|
|
tab->setVisible( true );
|
|
|
|
// Notify User
|
|
char *retBuffer = Con::getReturnBuffer( 512 );
|
|
dStrcpy( retBuffer, tab->getText() );
|
|
Con::executef( this, 2, "onTabSelected", retBuffer );
|
|
|
|
}
|
|
else
|
|
tab->setVisible( false );
|
|
}
|
|
}
|
|
|
|
bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
|
|
{
|
|
// Tab = Next Page
|
|
// Ctrl-Tab = Previous Page
|
|
if( 0 && event.keyCode == KEY_TAB )
|
|
{
|
|
if( event.modifier & SI_CTRL )
|
|
selectPrevPage();
|
|
else
|
|
selectNextPage();
|
|
|
|
return true;
|
|
}
|
|
|
|
return Parent::onKeyDown( event );
|
|
}
|
|
|
|
void GuiTabBookCtrl::selectNextPage()
|
|
{
|
|
if( mPages.empty() )
|
|
return;
|
|
|
|
if( mActivePage == NULL )
|
|
mActivePage = mPages[0].Page;
|
|
|
|
S32 nI = 0;
|
|
for( ; nI < mPages.size(); nI++ )
|
|
{
|
|
GuiTabPageCtrl *tab = mPages[ nI ].Page;
|
|
if( tab == mActivePage )
|
|
{
|
|
if( nI == ( mPages.size() - 1 ) )
|
|
selectPage( 0 );
|
|
else if ( nI + 1 <= ( mPages.size() - 1 ) )
|
|
selectPage( nI + 1 );
|
|
else
|
|
selectPage( 0 );
|
|
|
|
// Notify User
|
|
if( isMethod( "onTabSelected" ) )
|
|
{
|
|
char *retBuffer = Con::getReturnBuffer( 512 );
|
|
dStrcpy( retBuffer, tab->getText() );
|
|
Con::executef( this, 2, "onTabSelected", retBuffer );
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GuiTabBookCtrl::selectPrevPage()
|
|
{
|
|
if( mPages.empty() )
|
|
return;
|
|
|
|
if( mActivePage == NULL )
|
|
mActivePage = mPages[0].Page;
|
|
|
|
S32 nI = 0;
|
|
for( ; nI < mPages.size(); nI++ )
|
|
{
|
|
GuiTabPageCtrl *tab = mPages[ nI ].Page;
|
|
if( tab == mActivePage )
|
|
{
|
|
if( nI == 0 )
|
|
selectPage( mPages.size() - 1 );
|
|
else
|
|
selectPage( nI - 1 );
|
|
|
|
// Notify User
|
|
if( isMethod( "onTabSelected" ) )
|
|
{
|
|
char *retBuffer = Con::getReturnBuffer( 512 );
|
|
dStrcpy( retBuffer, tab->getText() );
|
|
Con::executef( this, 2, "onTabSelected", retBuffer );
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
}
|