Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,358 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiAutoScrollCtrl.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT( GuiAutoScrollCtrl );
ConsoleDocClass( GuiAutoScrollCtrl,
"@brief A container that scrolls its child control up over time.\n\n"
"This container can be used to scroll a single child control in either of the four directions.\n\n"
"@tsexample\n"
"// Create a GuiAutoScrollCtrl that scrolls a long text of credits.\n"
"new GuiAutoScrollCtrl( CreditsScroller )\n"
"{\n"
" position = \"0 0\";\n"
" extent = Canvas.extent.x SPC Canvas.extent.y;\n"
"\n"
" scrollDirection = \"Up\"; // Scroll upwards.\n"
" startDelay = 4; // Wait 4 seconds before starting to scroll.\n"
" isLooping = false; // Don't loop the credits.\n"
" scrollOutOfSight = true; // Scroll up fully.\n"
"\n"
" new GuiMLTextCtrl()\n"
" {\n"
" text = $CREDITS;\n"
" };\n"
"};\n"
"\n"
"function CreditsScroller::onComplete( %this )\n"
"{\n"
" // Switch back to main menu after credits have rolled.\n"
" Canvas.setContent( MainMenu );\n"
"}\n"
"\n"
"// Start rolling credits.\n"
"Canvas.setContent( CreditsScroller );\n"
"@endtsexample\n\n"
"@note Only the first child will be scrolled.\n\n"
"@ingroup GuiContainers"
);
IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onTick, void, (), (),
"Called every 32ms on the control." );
IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onStart, void, (), (),
"Called when the control starts to scroll." );
IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onComplete, void, (), (),
"Called when the child control has been scrolled in entirety." );
IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onReset, void, (), (),
"Called when the child control is reset to its initial position and the cycle starts again." );
ImplementEnumType( GuiAutoScrollDirection,
"Direction in which to scroll the child control.\n\n"
"@ingroup GuiContainers" )
{ GuiAutoScrollCtrl::Up, "Up", "Scroll from bottom towards top." },
{ GuiAutoScrollCtrl::Down, "Down", "Scroll from top towards bottom." },
{ GuiAutoScrollCtrl::Left, "Left", "Scroll from right towards left." },
{ GuiAutoScrollCtrl::Right, "Right", "Scroll from left towards right." },
EndImplementEnumType;
//-----------------------------------------------------------------------------
GuiAutoScrollCtrl::GuiAutoScrollCtrl()
: mDirection( Up ),
mIsLooping( true ),
mCurrentPhase( PhaseComplete ),
mCurrentTime( 0.f ),
mStartDelay( 3.f ),
mResetDelay( 5.f ),
mChildBorder( 10 ),
mScrollSpeed( 1.f ),
mScrollOutOfSight( false )
{
mIsContainer = true;
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::initPersistFields()
{
addGroup( "Scrolling" );
addField( "scrollDirection", TYPEID< Direction >(), Offset( mDirection, GuiAutoScrollCtrl ),
"Direction in which the child control is moved." );
addField( "startDelay", TypeF32, Offset( mStartDelay, GuiAutoScrollCtrl ),
"Seconds to wait before starting to scroll." );
addField( "resetDelay", TypeF32, Offset( mResetDelay, GuiAutoScrollCtrl ),
"Seconds to wait after scrolling completes before resetting and starting over.\n\n"
"@note Only takes effect if #isLooping is true." );
addField( "childBorder", TypeS32, Offset( mChildBorder, GuiAutoScrollCtrl ),
"Padding to put around child control (in pixels)." );
addField( "scrollSpeed", TypeF32, Offset( mScrollSpeed, GuiAutoScrollCtrl ),
"Scrolling speed in pixels per second." );
addField( "isLooping", TypeBool, Offset( mIsLooping, GuiAutoScrollCtrl ),
"If true, the scrolling will reset to the beginning once completing a cycle." );
addField( "scrollOutOfSight", TypeBool, Offset( mScrollOutOfSight, GuiAutoScrollCtrl ),
"If true, the child control will be completely scrolled out of sight; otherwise it will only scroll "
"until the other end becomes visible." );
endGroup( "Scrolling" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
bool GuiAutoScrollCtrl::onWake()
{
if( !Parent::onWake() )
return false;
setProcessTicks( true );
return true;
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::onSleep()
{
setProcessTicks( false );
Parent::onSleep();
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::onChildAdded( GuiControl* control )
{
_reset( control );
Parent::onChildAdded( control );
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::onChildRemoved( GuiControl* control )
{
mCurrentPhase = PhaseComplete;
Parent::onChildRemoved( control );
}
//-----------------------------------------------------------------------------
bool GuiAutoScrollCtrl::_isScrollComplete() const
{
if( empty() )
return true;
GuiControl* control = static_cast< GuiControl* >( at( 0 ) );
U32 axis = _getScrollAxis();
F32 amount = _getScrollAmount();
if( mScrollOutOfSight )
{
// If scrolling out of sight, scrolling is complete when the control's rectangle
// does not intersect our own rectangle anymore.
RectI thisRect( Point2I( 0, 0 ), getExtent() );
return !( thisRect.overlaps( control->getBounds() ) );
}
else
{
if( amount < 0 )
return ( control->getPosition()[ axis ] + control->getExtent()[ axis ] ) < ( getExtent()[ axis ] - mChildBorder );
else
return ( control->getPosition()[ axis ] >= mChildBorder );
}
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::_reset( GuiControl* control )
{
U32 axis = _getScrollAxis();
U32 counterAxis = ( axis == 1 ? 0 : 1 );
Point2I newPosition( mChildBorder, mChildBorder );
Point2I newExtent = control->getExtent();
// Fit control on axis that is not scrolled.
newExtent[ counterAxis ] = getExtent()[ counterAxis ] - mChildBorder * 2;
// For the right and down scrolls, position the control away from the
// right/bottom edge of our control.
if( mDirection == Right )
newPosition.x = - ( newExtent.x - getExtent().x + mChildBorder );
else if( mDirection == Down )
newPosition.y = - ( newExtent.y - getExtent().y + mChildBorder );
// Set the child geometry.
control->setPosition( newPosition );
control->setExtent( newExtent );
// Reset counters.
mCurrentTime = 0.0f;
mCurrentPhase = PhaseInitial;
mCurrentPosition = control->getPosition()[ axis ];
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::reset()
{
if( !empty() )
_reset( static_cast< GuiControl* >( at( 0 ) ) );
}
//-----------------------------------------------------------------------------
bool GuiAutoScrollCtrl::resize( const Point2I &newPosition, const Point2I &newExtent )
{
if( !Parent::resize( newPosition, newExtent ) )
return false;
for( iterator i = begin(); i != end(); ++ i )
{
GuiControl* control = static_cast< GuiControl* >( *i );
if( control )
_reset( control );
}
return true;
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::childResized( GuiControl* child )
{
Parent::childResized( child );
_reset(child);
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::processTick()
{
onTick_callback();
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::advanceTime( F32 timeDelta )
{
if( mCurrentPhase == PhaseComplete )
return;
// Wait out initial delay.
if( ( mCurrentTime + timeDelta ) < mStartDelay)
{
mCurrentTime += timeDelta;
return;
}
// Start scrolling if we haven't already.
if( mCurrentPhase == PhaseInitial )
{
onStart_callback();
mCurrentPhase = PhaseScrolling;
}
GuiControl* control = static_cast< GuiControl* >( at( 0 ) );
if( !control ) // Should not happen.
return;
// If not yet complete, scroll some more.
if( !_isScrollComplete() )
{
U32 axis = _getScrollAxis();
F32 amount = _getScrollAmount();
mCurrentPosition += amount * timeDelta;
Point2I newPosition = control->getPosition();
newPosition[ axis ] = mCurrentPosition;
control->setPosition( newPosition );
}
else
{
mCurrentTime += timeDelta;
if( mCurrentPhase != PhaseComplete && mCurrentPhase != PhaseWait )
{
if( mCurrentPhase != PhaseWait )
{
onComplete_callback();
mCurrentPhase = PhaseComplete;
}
mCompleteTime = mCurrentTime;
}
// Reset, if looping.
if( mIsLooping )
{
// Wait out reset time and restart.
mCurrentPhase = PhaseWait;
if( mCurrentTime > ( mCompleteTime + mResetDelay ) )
{
onReset_callback();
_reset( control );
}
}
}
}
//-----------------------------------------------------------------------------
void GuiAutoScrollCtrl::inspectPostApply()
{
Parent::inspectPostApply();
reset();
}
//=============================================================================
// API.
//=============================================================================
// MARK: ---- API ----
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiAutoScrollCtrl, reset, void, (),,
"Reset scrolling." )
{
object->reset();
}

View file

@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIAUTOSCROLLCTRL_H_
#define _GUIAUTOSCROLLCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUITICKCTRL_H_
#include "gui/shiny/guiTickCtrl.h"
#endif
/// A control that automatically scrolls its child control upwards.
class GuiAutoScrollCtrl : public GuiTickCtrl
{
public:
typedef GuiTickCtrl Parent;
/// Scrolling direction.
enum Direction
{
Up,
Down,
Left,
Right
};
protected:
enum Phase
{
PhaseInitial, ///< Waiting to begin scrolling.
PhaseScrolling, ///< Currently scrolling.
PhaseComplete, ///< Scrolling complete.
PhaseWait ///< Wait before starting a new loop.
};
/// The direction in which to scroll.
Direction mDirection;
/// If true, scrolling will start from the beginning once finished.
bool mIsLooping;
/// Whether to scroll the child control completely out of sight.
bool mScrollOutOfSight;
/// Current phase in the scrolling animation.
Phase mCurrentPhase;
/// The current animation time.
F32 mCurrentTime;
/// The time scrolling was completed.
F32 mCompleteTime;
/// Current scrolling position. This is kept separate from the control's
/// current position value since we will receive time updates in increments
/// less than a second and thus need to have this value in floating-point.
F32 mCurrentPosition;
/// Seconds to wait before starting to scroll.
F32 mStartDelay;
/// Seconds to wait after scrolling is complete before reseting the control
/// to the initial state (only if #mIsLooping is true).
F32 mResetDelay;
/// Border to put around scrolled child control.
S32 mChildBorder;
/// Speed at which to scroll in pixels per second.
F32 mScrollSpeed;
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onTick, () );
DECLARE_CALLBACK( void, onStart, () );
DECLARE_CALLBACK( void, onComplete, () );
DECLARE_CALLBACK( void, onReset, () );
/// @}
void _reset( GuiControl* control );
bool _isScrollComplete() const;
U32 _getScrollAxis() const
{
switch( mDirection )
{
case Up: return 1;
case Down: return 1;
case Left: return 0;
case Right: return 0;
}
return 0;
}
F32 _getScrollAmount() const
{
switch( mDirection )
{
case Up: return - mScrollSpeed;
case Down: return mScrollSpeed;
case Left: return - mScrollSpeed;
case Right: return mScrollSpeed;
}
return 0.f;
}
public:
GuiAutoScrollCtrl();
void reset();
virtual bool onWake();
virtual void onSleep();
virtual void onChildAdded( GuiControl* control );
virtual void onChildRemoved( GuiControl* control );
virtual bool resize( const Point2I& newPosition, const Point2I& newExtent );
virtual void childResized( GuiControl *child );
virtual void processTick();
virtual void advanceTime( F32 timeDelta );
virtual void inspectPostApply();
static void initPersistFields();
DECLARE_CONOBJECT( GuiAutoScrollCtrl );
DECLARE_CATEGORY( "Gui Containers" );
DECLARE_DESCRIPTION( "A container that automatically scrolls its child control upwards.\n"
"Can be used, for example, for credits screens." );
};
typedef GuiAutoScrollCtrl::Direction GuiAutoScrollDirection;
DefineEnumType( GuiAutoScrollDirection );
#endif

View file

@ -0,0 +1,439 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "gui/containers/guiContainer.h"
#include "gui/containers/guiPanel.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT( GuiContainer );
ConsoleDocClass( GuiContainer,
"@brief Brief Desc.\n\n"
"@tsexample\n"
"// Comment:\n"
"%okButton = new ClassObject()\n"
"instantiation\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
ImplementEnumType( GuiDockingType,
"\n\n"
"@ingroup GuiContainers" )
{ Docking::dockNone, "None" },
{ Docking::dockClient, "Client" },
{ Docking::dockTop, "Top" },
{ Docking::dockBottom, "Bottom" },
{ Docking::dockLeft, "Left" },
{ Docking::dockRight, "Right" }
EndImplementEnumType;
//-----------------------------------------------------------------------------
GuiContainer::GuiContainer()
{
mUpdateLayout = false;
mValidDockingMask = Docking::dockNone | Docking::dockBottom |
Docking::dockTop | Docking::dockClient |
Docking::dockLeft | Docking::dockRight;
mIsContainer = true;
}
//-----------------------------------------------------------------------------
GuiContainer::~GuiContainer()
{
}
//-----------------------------------------------------------------------------
void GuiContainer::initPersistFields()
{
Con::setIntVariable("$DOCKING_NONE", Docking::dockNone);
Con::setIntVariable("$DOCKING_CLIENT", Docking::dockClient);
Con::setIntVariable("$DOCKING_TOP", Docking::dockTop);
Con::setIntVariable("$DOCKING_BOTTOM", Docking::dockBottom);
Con::setIntVariable("$DOCKING_LEFT", Docking::dockLeft);
Con::setIntVariable("$DOCKING_RIGHT", Docking::dockRight);
addGroup( "Layout" );
addProtectedField("docking", TYPEID< Docking::DockingType >(), Offset(mSizingOptions.mDocking, GuiContainer), &setDockingField, &defaultProtectedGetFn, "" );
addField("margin", TypeRectSpacingI, Offset(mSizingOptions.mPadding, GuiContainer));
addField("padding", TypeRectSpacingI, Offset(mSizingOptions.mInternalPadding, GuiContainer));
addField("anchorTop", TypeBool, Offset(mSizingOptions.mAnchorTop, GuiContainer));
addField("anchorBottom", TypeBool, Offset(mSizingOptions.mAnchorBottom, GuiContainer));
addField("anchorLeft", TypeBool, Offset(mSizingOptions.mAnchorLeft, GuiContainer));
addField("anchorRight", TypeBool, Offset(mSizingOptions.mAnchorRight, GuiContainer));
endGroup( "Layout" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
void GuiContainer::onChildAdded(GuiControl* control)
{
Parent::onChildAdded( control );
setUpdateLayout();
}
//-----------------------------------------------------------------------------
void GuiContainer::onChildRemoved(GuiControl* control)
{
Parent::onChildRemoved( control );
setUpdateLayout();
}
//-----------------------------------------------------------------------------
bool GuiContainer::reOrder(SimObject* obj, SimObject* target)
{
if ( !Parent::reOrder(obj, target) )
return false;
setUpdateLayout();
return true;
}
//-----------------------------------------------------------------------------
bool GuiContainer::resize( const Point2I &newPosition, const Point2I &newExtent )
{
RectI oldBounds = getBounds();
Point2I minExtent = getMinExtent();
if( !Parent::resize( newPosition, newExtent ) )
return false;
RectI clientRect = getClientRect();
layoutControls( clientRect );
GuiControl *parent = getParent();
S32 docking = getDocking();
if( parent && docking != Docking::dockNone && docking != Docking::dockInvalid )
setUpdateLayout( updateParent );
return true;
}
//-----------------------------------------------------------------------------
void GuiContainer::addObject(SimObject *obj)
{
Parent::addObject(obj);
setUpdateLayout();
}
//-----------------------------------------------------------------------------
void GuiContainer::removeObject(SimObject *obj)
{
Parent::removeObject(obj);
setUpdateLayout();
}
//-----------------------------------------------------------------------------
void GuiContainer::parentResized(const RectI &oldParentRect, const RectI &newParentRect)
{
//if(!mCanResize)
// return;
// If it's a control that specifies invalid docking, we'll just treat it as an old GuiControl
if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone)
return Parent::parentResized( oldParentRect, newParentRect );
S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x;
S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;
// Update Self
RectI oldThisRect = getBounds();
anchorControl( this, Point2I( deltaX, deltaY ) );
RectI newThisRect = getBounds();
// Update Deltas to pass on to children
deltaX = newThisRect.extent.x - oldThisRect.extent.x;
deltaY = newThisRect.extent.y - oldThisRect.extent.y;
// Iterate over all children and update their anchors
iterator nI = begin();
for( ; nI != end(); nI++ )
{
// Sanity
GuiControl *control = dynamic_cast<GuiControl*>( (*nI) );
if( control )
control->parentResized( oldThisRect, newThisRect );
}
}
//-----------------------------------------------------------------------------
void GuiContainer::childResized(GuiControl *child)
{
Parent::childResized( child );
setUpdateLayout();
}
//-----------------------------------------------------------------------------
bool GuiContainer::layoutControls( RectI &clientRect )
{
// This variable is set to the first 'Client' docking
// control that is found. We defer client docking until
// after all other docks have been made since it will consume
// the remaining client area available.
GuiContainer *clientDocking = NULL;
// Iterate over all children and perform docking
iterator nI = begin();
for( ; nI != end(); nI++ )
{
// Layout Content with proper docking (Client Default)
GuiControl *control = static_cast<GuiControl*>(*nI);
// If we're invisible we don't get counted in docking
if( control == NULL || !control->isVisible() )
continue;
S32 dockingMode = Docking::dockNone;
GuiContainer *container = dynamic_cast<GuiContainer*>(control);
if( container != NULL )
dockingMode = container->getDocking();
else
continue;
// See above note about clientDocking pointer
if( dockingMode & Docking::dockClient && clientDocking == NULL )
clientDocking = container;
// Dock Appropriately
if( !(dockingMode & Docking::dockClient) )
dockControl( container, dockingMode, clientRect );
}
// Do client dock
if( clientDocking != NULL )
dockControl( clientDocking, Docking::dockClient, clientRect );
return true;
}
//-----------------------------------------------------------------------------
bool GuiContainer::dockControl( GuiContainer *control, S32 dockingMode, RectI &clientRect )
{
if( !control )
return false;
// Make sure this class support docking of this type
if( !(dockingMode & getValidDockingMask()))
return false;
// If our client rect has run out of room, we can't dock any more
if( !clientRect.isValidRect() )
return false;
// Dock Appropriately
RectI dockRect;
RectSpacingI rectShrinker;
ControlSizing sizingOptions = control->getSizingOptions();
switch( dockingMode )
{
case Docking::dockClient:
// Inset by padding
sizingOptions.mPadding.insetRect(clientRect);
// Dock to entirety of client rectangle
control->resize( clientRect.point, clientRect.extent );
// Remove Client Rect, can only have one client dock
clientRect.set(0,0,0,0);
break;
case Docking::dockTop:
dockRect = clientRect;
dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom , clientRect.extent.y );
// Subtract our rect
clientRect.point.y += dockRect.extent.y;
clientRect.extent.y -= dockRect.extent.y;
// Inset by padding
sizingOptions.mPadding.insetRect(dockRect);
// Resize
control->resize( dockRect.point, dockRect.extent );
break;
case Docking::dockBottom:
dockRect = clientRect;
dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom, clientRect.extent.y );
dockRect.point.y += clientRect.extent.y - dockRect.extent.y;
// Subtract our rect
clientRect.extent.y -= dockRect.extent.y;
// Inset by padding
sizingOptions.mPadding.insetRect(dockRect);
// Resize
control->resize( dockRect.point, dockRect.extent );
break;
case Docking::dockLeft:
dockRect = clientRect;
dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x );
// Subtract our rect
clientRect.point.x += dockRect.extent.x;
clientRect.extent.x -= dockRect.extent.x;
// Inset by padding
sizingOptions.mPadding.insetRect(dockRect);
// Resize
control->resize( dockRect.point, dockRect.extent );
break;
case Docking::dockRight:
dockRect = clientRect;
dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x );
dockRect.point.x += clientRect.extent.x - dockRect.extent.x;
// Subtract our rect
clientRect.extent.x -= dockRect.extent.x;
// Inset by padding
sizingOptions.mPadding.insetRect(dockRect);
// Resize
control->resize( dockRect.point, dockRect.extent );
break;
case Docking::dockNone:
control->setUpdateLayout();
break;
}
return true;
}
//-----------------------------------------------------------------------------
bool GuiContainer::anchorControl( GuiControl *control, const Point2I &deltaParentExtent )
{
GuiContainer *container = dynamic_cast<GuiContainer*>( control );
if( !control || !container )
return false;
// If we're docked, we don't anchor to anything
if( (container->getDocking() & Docking::dockAny) || !(container->getDocking() & Docking::dockInvalid) )
return false;
if( deltaParentExtent.isZero() )
return false;
RectI oldRect = control->getBounds();
RectI newRect = control->getBounds();
F32 deltaBottom = mSizingOptions.mAnchorBottom ? (F32)deltaParentExtent.y : 0.0f;
F32 deltaRight = mSizingOptions.mAnchorRight ? (F32)deltaParentExtent.x : 0.0f;
F32 deltaLeft = mSizingOptions.mAnchorLeft ? 0.0f : (F32)deltaParentExtent.x;
F32 deltaTop = mSizingOptions.mAnchorTop ? 0.0f : (F32)deltaParentExtent.y;
// Apply Delta's to newRect
newRect.point.x += (S32)deltaLeft;
newRect.extent.x += (S32)(deltaRight - deltaLeft);
newRect.point.y += (S32)deltaTop;
newRect.extent.y += (S32)(deltaBottom - deltaTop);
Point2I minExtent = control->getMinExtent();
// Only resize if our minExtent is satisfied with it.
if( !( newRect.extent.x >= control->getMinExtent().x && newRect.extent.y >= control->getMinExtent().y ) )
return false;
if( newRect.point == oldRect.point && newRect.extent == oldRect.extent )
return false;
// Finally Size the control
control->resize( newRect.point, newRect.extent );
// We made changes
return true;
}
//-----------------------------------------------------------------------------
void GuiContainer::onPreRender()
{
if( mUpdateLayout == updateNone )
return;
RectI clientRect = getClientRect();
if( mUpdateLayout & updateSelf )
layoutControls( clientRect );
GuiContainer *parent = dynamic_cast<GuiContainer*>( getParent() );
if( parent && ( mUpdateLayout & updateParent ) )
parent->setUpdateLayout();
// Always set AFTER layoutControls call to prevent recursive calling of layoutControls - JDD
mUpdateLayout = updateNone;
Parent::onPreRender();
}
//-----------------------------------------------------------------------------
const RectI GuiContainer::getClientRect()
{
RectI resRect = RectI( Point2I(0,0), getExtent() );
// Inset by padding
mSizingOptions.mInternalPadding.insetRect( resRect );
return resRect;
}
//-----------------------------------------------------------------------------
void GuiContainer::setDocking( S32 docking )
{
mSizingOptions.mDocking = docking;
setUpdateLayout( updateParent );
}

View file

@ -0,0 +1,155 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUICONTAINER_H_
#define _GUICONTAINER_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
/// Base class for controls that act as containers to other controls.
///
/// @addtogroup gui_container_group Containers
///
/// @ingroup gui_group Gui System
/// @{
class GuiContainer : public GuiControl
{
public:
typedef GuiControl Parent;
enum
{
updateSelf = BIT(1),
updateParent = BIT(2),
updateNone = 0
};
protected:
S32 mUpdateLayout; ///< Layout Update Mask
ControlSizing mSizingOptions; ///< Control Sizing Options
S32 mValidDockingMask;
public:
DECLARE_CONOBJECT(GuiContainer);
DECLARE_CATEGORY( "Gui Containers" );
GuiContainer();
virtual ~GuiContainer();
static void initPersistFields();
/// @name Container Sizing
/// @{
/// Returns the Mask of valid docking modes supported by this container
inline S32 getValidDockingMask() { return mValidDockingMask; };
/// Docking Accessors
inline S32 getDocking() { return mSizingOptions.mDocking; };
virtual void setDocking( S32 docking );
/// Docking Protected Field Setter
static bool setDockingField( void *object, const char *index, const char *data )
{
GuiContainer *pContainer = static_cast<GuiContainer*>(object);
pContainer->setUpdateLayout( updateParent );
return true;
}
inline bool getAnchorTop() const { return mSizingOptions.mAnchorTop; }
inline bool getAnchorBottom() const { return mSizingOptions.mAnchorBottom; }
inline bool getAnchorLeft() const { return mSizingOptions.mAnchorLeft; }
inline bool getAnchorRight() const { return mSizingOptions.mAnchorRight; }
inline void setAnchorTop(bool val) { mSizingOptions.mAnchorTop = val; }
inline void setAnchorBottom(bool val) { mSizingOptions.mAnchorBottom = val; }
inline void setAnchorLeft(bool val) { mSizingOptions.mAnchorLeft = val; }
inline void setAnchorRight(bool val) { mSizingOptions.mAnchorRight = val; }
ControlSizing getSizingOptions() const { return mSizingOptions; }
void setSizingOptions(ControlSizing val) { mSizingOptions = val; }
/// @}
/// @name Sizing Constraints
/// @{
virtual const RectI getClientRect();
/// @}
/// @name Control Layout Methods
/// @{
/// Called when the Layout for a Container needs to be updated because of a resize call or a call to setUpdateLayout
/// @param clientRect The Client Rectangle that is available for this Container to layout it's children in
virtual bool layoutControls( RectI &clientRect );
/// Set the layout flag to Dirty on a Container, triggering an update to it's layout on the next onPreRender call.
/// @attention This can be called without regard to whether the flag is already set, as setting it
/// does not actually cause an update, but rather tells the container it should update the next
/// chance it gets
/// @param updateType A Mask that indicates how the layout should be updated.
inline void setUpdateLayout( S32 updateType = updateSelf ) { mUpdateLayout |= updateType; };
/// @}
/// @name Container Sizing Methods
/// @{
/// Dock a Control with the given docking mode inside the given client rect.
/// @attention The clientRect passed in will be modified by the docking of
/// the control. It will return the rect that remains after the docking operation.
virtual bool dockControl( GuiContainer *control, S32 dockingMode, RectI &clientRect );
/// Update a Controls Anchor based on a delta sizing of it's parents extent
/// This function should return true if the control was changed in size or position at all
virtual bool anchorControl( GuiControl *control, const Point2I &deltaParentExtent );
/// @}
/// @name GuiControl Inherited
/// @{
virtual void onChildAdded(GuiControl* control);
virtual void onChildRemoved(GuiControl* control);
virtual bool resize( const Point2I &newPosition, const Point2I &newExtent );
virtual void childResized(GuiControl *child);
virtual void addObject(SimObject *obj);
virtual void removeObject(SimObject *obj);
virtual bool reOrder(SimObject* obj, SimObject* target);
virtual void onPreRender();
/// GuiContainer deals with parentResized calls differently than GuiControl. It will
/// update the layout for all of it's non-docked child controls. parentResized calls
/// on the child controls will be handled by their default functions, but for our
/// purposes we want at least our immediate children to use the anchors that they have
/// set on themselves. - JDD [9/20/2006]
virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect);
/// @}
};
/// @}
#endif

View file

@ -0,0 +1,190 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiCtrlArrayCtrl.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(GuiControlArrayControl);
ConsoleDocClass( GuiControlArrayControl,
"@brief Brief Desc.\n\n"
"@tsexample\n"
"// Comment:\n"
"%okButton = new ClassObject()\n"
"instantiation\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
// One call back that the system isnt ready for yet in this class
GuiControlArrayControl::GuiControlArrayControl()
{
mResizing = false;
mCols = 0;
mRowSize = 30;
mRowSpacing = 2;
mColSpacing = 0;
mIsContainer = true;
}
void GuiControlArrayControl::initPersistFields()
{
addGroup( "Array" );
addField( "colCount", TypeS32, Offset(mCols, GuiControlArrayControl),
"Number of colums in the array." );
addField( "colSizes", TypeS32Vector, Offset(mColumnSizes, GuiControlArrayControl),
"Size of each individual column." );
addField( "rowSize", TypeS32, Offset(mRowSize, GuiControlArrayControl),
"Heigth of a row in the array." );
addField( "rowSpacing", TypeS32, Offset(mRowSpacing, GuiControlArrayControl),
"Padding to put between rows." );
addField( "colSpacing", TypeS32, Offset(mColSpacing, GuiControlArrayControl),
"Padding to put between columns." );
endGroup( "Array" );
Parent::initPersistFields();
}
bool GuiControlArrayControl::onWake()
{
if ( !Parent::onWake() )
return false;
return true;
}
void GuiControlArrayControl::onSleep()
{
Parent::onSleep();
}
void GuiControlArrayControl::inspectPostApply()
{
Parent::inspectPostApply();
updateArray();
}
bool GuiControlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
if( !Parent::resize(newPosition, newExtent) )
return false;
return updateArray();
}
void GuiControlArrayControl::addObject(SimObject *obj)
{
Parent::addObject(obj);
updateArray();
}
void GuiControlArrayControl::removeObject(SimObject *obj)
{
Parent::removeObject(obj);
updateArray();
}
bool GuiControlArrayControl::reOrder(SimObject* obj, SimObject* target)
{
bool ret = Parent::reOrder(obj, target);
if (ret)
updateArray();
return ret;
}
bool GuiControlArrayControl::updateArray()
{
// Prevent recursion
if(mResizing)
return false;
// Set Resizing.
mResizing = true;
if(mCols < 1 || size() < 1)
{
mResizing = false;
return false;
}
S32 *sizes = new S32[mCols];
S32 *offsets = new S32[mCols];
S32 totalSize = 0;
Point2I extent = getExtent();
// Calculate the column sizes
for(S32 i=0; i<mCols; i++)
{
if( i >= mColumnSizes.size() )
sizes[ i ] = 0;
else
sizes[i] = mColumnSizes[i];
offsets[i] = totalSize;
// If it's an auto-size one, then... auto-size...
if(sizes[i] == -1)
{
sizes[i] = extent.x - totalSize;
break;
}
totalSize += sizes[i] + mColSpacing;
}
// Now iterate through the children and resize them to fit the grid...
for(S32 i=0; i<size(); i++)
{
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](i));
// Get the current column and row...
S32 curCol = i % mCols;
S32 curRow = i / mCols;
if(gc)
{
Point2I newPos(offsets[curCol], curRow * (mRowSize + mRowSpacing));
Point2I newExtents(sizes[curCol], mRowSize);
gc->resize(newPos, newExtents);
}
}
// Clear Sizing Flag.
mResizing = false;
delete [] sizes;
delete [] offsets;
return true;
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUICTRLARRAYCTRL_H_
#define _GUICTRLARRAYCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "gfx/gfxDevice.h"
#include "console/console.h"
#include "console/consoleTypes.h"
class GuiControlArrayControl : public GuiControl
{
private:
typedef GuiControl Parent;
bool mResizing;
S32 mCols;
Vector<S32> mColumnSizes;
S32 mRowSize;
S32 mRowSpacing;
S32 mColSpacing;
public:
GuiControlArrayControl();
bool resize(const Point2I &newPosition, const Point2I &newExtent);
bool onWake();
void onSleep();
void inspectPostApply();
bool updateArray();
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
bool reOrder(SimObject* obj, SimObject* target = 0);
static void initPersistFields();
DECLARE_CONOBJECT(GuiControlArrayControl);
DECLARE_CATEGORY( "Gui Containers" );
};
#endif

View file

@ -0,0 +1,271 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiDragAndDropCtrl.h"
#include "gui/core/guiCanvas.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT( GuiDragAndDropControl );
ConsoleDocClass( GuiDragAndDropControl,
"@brief A container control that can be used to implement drag&drop behavior.\n\n"
"GuiDragAndDropControl is a special control that can be used to allow drag&drop behavior to be implemented where "
"GuiControls may be dragged across the canvas and the dropped on other GuiControls.\n\n"
"To start a drag operation, construct a GuiDragAndDropControl and add the control that should be drag&dropped "
"as a child to it. Note that this must be a single child control. To drag multiple controls, wrap them in a new "
"GuiControl object as a temporary container.\n\n"
"Then, to initiate the drag, add the GuiDragAndDropControl to the canvas and call startDragging(). You can optionally "
"supply an offset to better position the GuiDragAndDropControl on the mouse cursor.\n\n"
"As the GuiDragAndDropControl is then moved across the canvas, it will call the onControlDragEnter(), onControlDragExit(), "
"onControlDragged(), and finally onControlDropped() callbacks on the visible topmost controls that it moves across. "
"onControlDropped() is called when the mouse button is released and the drag operation thus finished.\n\n"
"@tsexample\n"
"// The following example implements drag&drop behavior for GuiSwatchButtonCtrl so that\n"
"// one color swatch may be dragged over the other to quickly copy its color.\n"
"//\n"
"// This code is taken from the stock scripts.\n"
"\n"
"//---------------------------------------------------------------------------------------------\n"
"\n"
"// With this method, we start the operation when the mouse is click-dragged away from a color swatch.\n"
"function GuiSwatchButtonCtrl::onMouseDragged( %this )\n"
"{\n"
" // First we construct a new temporary swatch button that becomes the payload for our\n"
" // drag operation and give it the properties of the swatch button we want to copy.\n"
"\n"
" %payload = new GuiSwatchButtonCtrl();\n"
" %payload.assignFieldsFrom( %this );\n"
" %payload.position = \"0 0\";\n"
" %payload.dragSourceControl = %this; // Remember where the drag originated from so that we don't copy a color swatch onto itself.\n"
"\n"
" // Calculate the offset of the GuiDragAndDropControl from the mouse cursor. Here we center\n"
" // it on the cursor.\n"
"\n"
" %xOffset = getWord( %payload.extent, 0 ) / 2;\n"
" %yOffset = getWord( %payload.extent, 1 ) / 2;\n"
"\n"
" // Compute the initial position of the GuiDragAndDrop control on the cavas based on the current\n"
" // mouse cursor position.\n"
"\n"
" %cursorpos = Canvas.getCursorPos();\n"
" %xPos = getWord( %cursorpos, 0 ) - %xOffset;\n"
" %yPos = getWord( %cursorpos, 1 ) - %yOffset;\n"
"\n"
" // Create the drag control.\n"
"\n"
" %ctrl = new GuiDragAndDropControl()\n"
" {\n"
" canSaveDynamicFields = \"0\";\n"
" Profile = \"GuiSolidDefaultProfile\";\n"
" HorizSizing = \"right\";\n"
" VertSizing = \"bottom\";\n"
" Position = %xPos SPC %yPos;\n"
" extent = %payload.extent;\n"
" MinExtent = \"4 4\";\n"
" canSave = \"1\";\n"
" Visible = \"1\";\n"
" hovertime = \"1000\";\n"
"\n"
" // Let the GuiDragAndDropControl delete itself on mouse-up. When the drag is aborted,\n"
" // this not only deletes the drag control but also our payload.\n"
" deleteOnMouseUp = true;\n"
"\n"
" // To differentiate drags, use the namespace hierarchy to classify them.\n"
" // This will allow a color swatch drag to tell itself apart from a file drag, for example.\n"
" class = \"GuiDragAndDropControlType_ColorSwatch\";\n"
" };\n"
"\n"
" // Add the temporary color swatch to the drag control as the payload.\n"
" %ctrl.add( %payload );\n"
"\n"
" // Start drag by adding the drag control to the canvas and then calling startDragging().\n"
"\n"
" Canvas.getContent().add( %ctrl );\n"
" %ctrl.startDragging( %xOffset, %yOffset );\n"
"}\n"
"\n"
"//---------------------------------------------------------------------------------------------\n"
"\n"
"// This method receives the drop when the mouse button is released over a color swatch control\n"
"// during a drag operation.\n"
"function GuiSwatchButtonCtrl::onControlDropped( %this, %payload, %position )\n"
"{\n"
" // Make sure this is a color swatch drag operation.\n"
" if( !%payload.parentGroup.isInNamespaceHierarchy( \"GuiDragAndDropControlType_ColorSwatch\" ) )\n"
" return;\n"
"\n"
" // If dropped on same button whence we came from,\n"
" // do nothing.\n"
"\n"
" if( %payload.dragSourceControl == %this )\n"
" return;\n"
"\n"
" // If a swatch button control is dropped onto this control,\n"
" // copy it's color.\n"
"\n"
" if( %payload.isMemberOfClass( \"GuiSwatchButtonCtrl\" ) )\n"
" {\n"
" // If the swatch button is part of a color-type inspector field,\n"
" // remember the inspector field so we can later set the color\n"
" // through it.\n"
"\n"
" if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorI\" ) )\n"
" %this.parentGroup.apply( ColorFloatToInt( %payload.color ) );\n"
" else if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorF\" ) )\n"
" %this.parentGroup.apply( %payload.color );\n"
" else\n"
" %this.setColor( %payload.color );\n"
" }\n"
"}\n"
"@endtsexample\n\n"
"@see GuiControl::onControlDragEnter\n"
"@see GuiControl::onControlDragExit\n"
"@see GuiControl::onControlDragged\n"
"@see GuiControl::onControlDropped\n\n"
"@ingroup GuiUtil"
);
//-----------------------------------------------------------------------------
void GuiDragAndDropControl::initPersistFields()
{
addField( "deleteOnMouseUp", TypeBool, Offset( mDeleteOnMouseUp, GuiDragAndDropControl ),
"If true, the control deletes itself when the left mouse button is released.\n\n"
"If at this point, the drag&drop control still contains its payload, it will be deleted along with the control." );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
void GuiDragAndDropControl::startDragging( Point2I offset )
{
GuiCanvas* canvas = getRoot();
if( !canvas )
{
Con::errorf( "GuiDragAndDropControl::startDragging - GuiDragAndDropControl wasn't added to the gui before the drag started." );
if( mDeleteOnMouseUp )
deleteObject();
return;
}
if( canvas->getMouseLockedControl() )
{
GuiEvent event;
canvas->getMouseLockedControl()->onMouseLeave(event);
canvas->mouseUnlock( canvas->getMouseLockedControl() );
}
canvas->mouseLock(this);
canvas->setFirstResponder(this);
mOffset = offset;
mLastTarget=NULL;
}
//-----------------------------------------------------------------------------
void GuiDragAndDropControl::onMouseDown( const GuiEvent& event )
{
startDragging( event.mousePoint - getPosition() );
}
//-----------------------------------------------------------------------------
void GuiDragAndDropControl::onMouseDragged( const GuiEvent& event )
{
setPosition( event.mousePoint - mOffset );
// Allow the control under the drag to react to a potential drop
GuiControl* enterTarget = findDragTarget( event.mousePoint, "onControlDragEnter" );
if( mLastTarget != enterTarget )
{
if( mLastTarget )
mLastTarget->onControlDragExit_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
if( enterTarget )
enterTarget->onControlDragEnter_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
mLastTarget = enterTarget;
}
GuiControl* dragTarget = findDragTarget( event.mousePoint, "onControlDragged" );
if( dragTarget )
dragTarget->onControlDragged_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
}
//-----------------------------------------------------------------------------
void GuiDragAndDropControl::onMouseUp(const GuiEvent& event)
{
mouseUnlock();
GuiControl* target = findDragTarget( event.mousePoint, "onControlDropped" );
if( target )
target->onControlDropped_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() );
if( mDeleteOnMouseUp )
deleteObject();
}
//-----------------------------------------------------------------------------
GuiControl* GuiDragAndDropControl::findDragTarget( Point2I mousePoint, const char* method )
{
// If there are any children and we have a parent.
GuiControl* parent = getParent();
if (size() && parent)
{
mVisible = false;
GuiControl* dropControl = parent->findHitControl(mousePoint);
mVisible = true;
while( dropControl )
{
if (dropControl->isMethod(method))
return dropControl;
else
dropControl = dropControl->getParent();
}
}
return NULL;
}
//=============================================================================
// Console Methods.
//=============================================================================
// MARK: ---- Console Methods ----
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiDragAndDropControl, startDragging, void, ( S32 x, S32 y ), ( 0, 0 ),
"Start the drag operation.\n\n"
"@param x X coordinate for the mouse pointer offset which the drag control should position itself.\n"
"@param y Y coordinate for the mouse pointer offset which the drag control should position itself.")
{
object->startDragging( Point2I( x, y ) );
}

View file

@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIDRAGANDDROPCTRL_H_
#define _GUIDRAGANDDROPCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GFXDEVICE_H_
#include "gfx/gfxDevice.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
/// A special control that implements drag-and-drop behavior.
///
class GuiDragAndDropControl : public GuiControl
{
public:
typedef GuiControl Parent;
private:
/// The mouse down offset from the upper left of the control.
Point2I mOffset;
/// If true, the control deletes itself when the left mouse button is released.
bool mDeleteOnMouseUp;
/// Controls may want to react when they are dragged over, entered or exited.
SimObjectPtr<GuiControl> mLastTarget;
GuiControl* findDragTarget(Point2I mousePoint, const char* method);
Point2I getDropPoint() const
{
return getPosition() + (getExtent() / 2);
}
public:
GuiDragAndDropControl() {}
void startDragging(Point2I offset = Point2I(0, 0));
// GuiControl.
virtual void onMouseDown(const GuiEvent& event);
virtual void onMouseDragged(const GuiEvent& event);
virtual void onMouseUp(const GuiEvent& event);
static void initPersistFields();
DECLARE_CONOBJECT( GuiDragAndDropControl );
DECLARE_CATEGORY( "Gui Other" );
DECLARE_DESCRIPTION( "A special control that implements drag&drop behavior.\n"
"The control will notify other controls as it moves across the canvas.\n"
"Content can be attached through dynamic fields or child objects." );
};
#endif

View file

@ -0,0 +1,303 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/engineAPI.h"
#include "platform/platform.h"
#include "gui/containers/guiDynamicCtrlArrayCtrl.h"
GuiDynamicCtrlArrayControl::GuiDynamicCtrlArrayControl()
{
mCols = 0;
mColSize = 64;
mRows = 0;
mRowSize = 64;
mRowSpacing = 0;
mColSpacing = 0;
mIsContainer = true;
mResizing = false;
mSizeToChildren = false;
mAutoCellSize = false;
mFrozen = false;
mDynamicSize = false;
mFillRowFirst = true;
mPadding.set( 0, 0, 0, 0 );
}
GuiDynamicCtrlArrayControl::~GuiDynamicCtrlArrayControl()
{
}
IMPLEMENT_CONOBJECT(GuiDynamicCtrlArrayControl);
ConsoleDocClass( GuiDynamicCtrlArrayControl,
"@brief A container that arranges children into a grid.\n\n"
"This container maintains a 2D grid of GUI controls. If one is added, deleted, "
"or resized, then the grid is updated. The insertion order into the grid is "
"determined by the internal order of the children (ie. the order of addition).<br>"
"Children are added to the grid by row or column until they fill the assocated "
"GuiDynamicCtrlArrayControl extent (width or height). For example, a "
"GuiDynamicCtrlArrayControl with 15 children, and <i>fillRowFirst</i> set to "
"true may be arranged as follows:\n\n"
"<pre>\n"
"1 2 3 4 5 6\n"
"7 8 9 10 11 12\n"
"13 14 15\n"
"</pre>\n"
"If <i>dynamicSize</i> were set to true in this case, the GuiDynamicCtrlArrayControl "
"height would be calculated to fit the 3 rows of child controls.\n\n"
"@tsexample\n"
"new GuiDynamicCtrlArrayControl()\n"
"{\n"
" colSize = \"128\";\n"
" rowSize = \"18\";\n"
" colSpacing = \"2\";\n"
" rowSpacing = \"2\";\n"
" frozen = \"0\";\n"
" autoCellSize = \"1\";\n"
" fillRowFirst = \"1\";\n"
" dynamicSize = \"1\";\n"
" padding = \"0 0 0 0\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
// ConsoleObject...
void GuiDynamicCtrlArrayControl::initPersistFields()
{
addField( "colCount", TypeS32, Offset( mCols, GuiDynamicCtrlArrayControl ),
"Number of columns the child controls have been arranged into. This "
"value is calculated automatically when children are added, removed or "
"resized; writing it directly has no effect." );
addField( "colSize", TypeS32, Offset( mColSize, GuiDynamicCtrlArrayControl ),
"Width of each column. If <i>autoCellSize</i> is set, this will be "
"calculated automatically from the widest child control" );
addField( "rowCount", TypeS32, Offset( mRows, GuiDynamicCtrlArrayControl ),
"Number of rows the child controls have been arranged into. This value "
"is calculated automatically when children are added, removed or resized; "
"writing it directly has no effect." );
addField( "rowSize", TypeS32, Offset( mRowSize, GuiDynamicCtrlArrayControl ),
"Height of each row. If <i>autoCellSize</i> is set, this will be "
"calculated automatically from the tallest child control" );
addField( "rowSpacing", TypeS32, Offset( mRowSpacing, GuiDynamicCtrlArrayControl ),
"Spacing between rows" );
addField( "colSpacing", TypeS32, Offset( mColSpacing, GuiDynamicCtrlArrayControl ),
"Spacing between columns" );
addField( "frozen", TypeBool, Offset( mFrozen, GuiDynamicCtrlArrayControl ),
"When true, the array will not update when new children are added or in "
"response to child resize events. This is useful to prevent unnecessary "
"resizing when adding, removing or resizing a number of child controls." );
addField( "autoCellSize", TypeBool, Offset( mAutoCellSize, GuiDynamicCtrlArrayControl ),
"When true, the cell size is set to the widest/tallest child control." );
addField( "fillRowFirst", TypeBool, Offset( mFillRowFirst, GuiDynamicCtrlArrayControl ),
"Controls whether rows or columns are filled first.\n\nIf true, controls are "
"added to the grid left-to-right (to fill a row); then rows are added "
"top-to-bottom as shown below:\n"
"<pre>1 2 3 4\n"
"5 6 7 8</pre>\n"
"If false, controls are added to the grid top-to-bottom (to fill a column); "
"then columns are added left-to-right as shown below:\n"
"<pre>1 3 5 7\n"
"2 4 6 8</pre>" );
addField( "dynamicSize", TypeBool, Offset( mDynamicSize, GuiDynamicCtrlArrayControl ),
"If true, the width or height of this control will be automatically "
"calculated based on the number of child controls (width if "
"<i>fillRowFirst</i> is false, height if <i>fillRowFirst</i> is true)." );
addField( "padding", TypeRectSpacingI, Offset( mPadding, GuiDynamicCtrlArrayControl ),
"Padding around the top, bottom, left, and right of this control. This "
"reduces the area available for child controls." );
Parent::initPersistFields();
}
// SimObject...
void GuiDynamicCtrlArrayControl::inspectPostApply()
{
resize(getPosition(), getExtent());
Parent::inspectPostApply();
}
// SimSet...
void GuiDynamicCtrlArrayControl::addObject(SimObject *obj)
{
Parent::addObject(obj);
if ( !mFrozen )
refresh();
}
// GuiControl...
bool GuiDynamicCtrlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
if ( size() == 0 )
return Parent::resize( newPosition, newExtent );
if ( mResizing )
return false;
mResizing = true;
// Calculate the cellSize based on our widest/tallest child control
// if the flag to do so is set.
if ( mAutoCellSize )
{
mColSize = 1;
mRowSize = 1;
for ( U32 i = 0; i < size(); i++ )
{
GuiControl *child = dynamic_cast<GuiControl*>(operator [](i));
if ( child && child->isVisible() )
{
if ( mColSize < child->getWidth() )
mColSize = child->getWidth();
if ( mRowSize < child->getHeight() )
mRowSize = child->getHeight();
}
}
}
// Count number of visible, children guiControls.
S32 numChildren = 0;
for ( U32 i = 0; i < size(); i++ )
{
GuiControl *child = dynamic_cast<GuiControl*>(operator [](i));
if ( child && child->isVisible() )
numChildren++;
}
// Calculate number of rows and columns.
if ( !mFillRowFirst )
{
mRows = 1;
while ( ( ( mRows + 1 ) * mRowSize + mRows * mRowSpacing ) <= ( newExtent.y - ( mPadding.top + mPadding.bottom ) ) )
mRows++;
mCols = numChildren / mRows;
if ( numChildren % mRows > 0 )
mCols++;
}
else
{
mCols = 1;
while ( ( ( mCols + 1 ) * mColSize + mCols * mColSpacing ) <= ( newExtent.x - ( mPadding.left + mPadding.right ) ) )
mCols++;
mRows = numChildren / mCols;
if ( numChildren % mCols > 0 )
mRows++;
}
// Place each child...
S32 childcount = 0;
for ( S32 i = 0; i < size(); i++ )
{
// Place control
GuiControl *gc = dynamic_cast<GuiControl*>(operator [](i));
// Added check if child is visible. Invisible children don't take part
if ( gc && gc->isVisible() )
{
S32 curCol, curRow;
// Get the current column and row...
if ( mFillRowFirst )
{
curCol = childcount % mCols;
curRow = childcount / mCols;
}
else
{
curCol = childcount / mRows;
curRow = childcount % mRows;
}
// Reposition and resize
Point2I newPos( mPadding.left + curCol * ( mColSize + mColSpacing ), mPadding.top + curRow * ( mRowSize + mRowSpacing ) );
gc->resize( newPos, Point2I( mColSize, mRowSize ) );
childcount++;
}
}
Point2I realExtent( newExtent );
if ( mDynamicSize )
{
if ( mFillRowFirst )
realExtent.y = mRows * mRowSize + ( mRows - 1 ) * mRowSpacing + ( mPadding.top + mPadding.bottom );
else
realExtent.x = mCols * mColSize + ( mCols - 1 ) * mColSpacing + ( mPadding.left + mPadding.right );
}
mResizing = false;
return Parent::resize( newPosition, realExtent );
}
void GuiDynamicCtrlArrayControl::childResized(GuiControl *child)
{
Parent::childResized(child);
if ( !mFrozen )
refresh();
}
void GuiDynamicCtrlArrayControl::refresh()
{
resize( getPosition(), getExtent() );
}
DefineEngineMethod( GuiDynamicCtrlArrayControl, refresh, void, (),,
"Recalculates the position and size of this control and all its children." )
{
object->refresh();
}

View file

@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIDYNAMICCTRLARRAYCTRL_H_
#define _GUIDYNAMICCTRLARRAYCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "gfx/gfxDevice.h"
#include "console/console.h"
#include "console/consoleTypes.h"
class GuiDynamicCtrlArrayControl : public GuiControl
{
typedef GuiControl Parent;
public:
GuiDynamicCtrlArrayControl();
virtual ~GuiDynamicCtrlArrayControl();
DECLARE_CONOBJECT(GuiDynamicCtrlArrayControl);
DECLARE_CATEGORY( "Gui Containers" );
// ConsoleObject
static void initPersistFields();
// SimObject
void inspectPostApply();
// SimSet
void addObject(SimObject *obj);
// GuiControl
bool resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
// GuiDynamicCtrlArrayCtrl
void refresh();
protected:
S32 mCols;
S32 mRows;
S32 mRowSize;
S32 mColSize;
S32 mRowSpacing;
S32 mColSpacing;
bool mResizing;
bool mSizeToChildren;
bool mAutoCellSize;
bool mFrozen;
bool mDynamicSize;
bool mFillRowFirst;
RectSpacingI mPadding;
};
#endif // _GUIDYNAMICCTRLARRAYCTRL_H_

View file

@ -0,0 +1,413 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/engineAPI.h"
#include "platform/platform.h"
#include "gui/containers/guiFormCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gfx/gfxDrawUtil.h"
#ifdef TORQUE_TOOLS
IMPLEMENT_CONOBJECT(GuiFormCtrl);
ConsoleDocClass( GuiFormCtrl,
"@brief A generic form control.\n\n"
"Currently editor use only.\n\n "
"@internal"
);
IMPLEMENT_CALLBACK( GuiFormCtrl, onResize, void, (), (),
"Called when the control is resized." );
GuiFormCtrl::GuiFormCtrl()
{
setMinExtent(Point2I(200,100));
mActive = true;
mMouseOver = false;
mDepressed = false;
mCanMove = false;
mCaption = "[none]";
mUseSmallCaption = false;
mContentLibrary = StringTable->insert("");
mContent = StringTable->insert("");
mCanSaveFieldDictionary = true;
mIsContainer = true;
// The attached menu bar
mHasMenu = false;
mMenuBar = NULL;
}
GuiFormCtrl::~GuiFormCtrl()
{
// If we still have a menu bar, delete it.
if( mMenuBar )
mMenuBar->deleteObject();
}
bool GuiFormCtrl::_setHasMenu( void *object, const char *index, const char *data )
{
GuiFormCtrl* ctrl = reinterpret_cast< GuiFormCtrl* >( object );
ctrl->setHasMenu( dAtob( data ) );
return false;
}
void GuiFormCtrl::initPersistFields()
{
addField("caption", TypeRealString, Offset(mCaption, GuiFormCtrl));
addField("contentLibrary",TypeString, Offset(mContentLibrary, GuiFormCtrl));
addField("content", TypeString, Offset(mContent, GuiFormCtrl));
addField("movable", TypeBool, Offset(mCanMove, GuiFormCtrl));
addProtectedField( "hasMenu", TypeBool, Offset(mHasMenu, GuiFormCtrl),
&_setHasMenu, &defaultProtectedGetFn,
"" );
Parent::initPersistFields();
}
void GuiFormCtrl::setHasMenu( bool value )
{
if( mHasMenu == value )
return;
if( !value )
{
mMenuBar->deleteObject();
mMenuBar = NULL;
}
else
{
if( !mMenuBar )
{
mMenuBar = new GuiMenuBar();
mMenuBar->setField( "profile", "GuiFormMenuBarProfile" );
mMenuBar->setField( "horizSizing", "right" );
mMenuBar->setField( "vertSizing", "bottom" );
mMenuBar->setField( "extent", "16 16" );
mMenuBar->setField( "minExtent", "16 16" );
mMenuBar->setField( "position", "0 0" );
mMenuBar->setField( "class", "FormMenuBarClass "); // Give a generic class to the menu bar so that one set of functions may be used for all of them.
mMenuBar->registerObject();
mMenuBar->setProcessTicks(true); // Activate the processing of ticks to track if the mouse pointer has been hovering within the menu
}
addObject( mMenuBar ); // Add the menu bar to the form
}
mHasMenu = value;
}
bool GuiFormCtrl::onWake()
{
if ( !Parent::onWake() )
return false;
mFont = mProfile->mFont;
AssertFatal(mFont, "GuiFormCtrl::onWake: invalid font in profile" );
mProfile->constructBitmapArray();
if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size())
{
mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
if(mFont->getHeight() > mThumbSize.y)
mThumbSize.y = mFont->getHeight();
}
else
{
mThumbSize.set(20, 20);
}
return true;
}
void GuiFormCtrl::addObject(SimObject *newObj )
{
if( ( mHasMenu && size() > 1) || (!mHasMenu && size() > 0 ) )
{
Con::warnf("GuiFormCtrl::addObject - Forms may only have one *direct* child - Placing on Parent!");
GuiControl* parent = getParent();
if ( parent )
parent->addObject( newObj );
return;
}
GuiControl *newCtrl = dynamic_cast<GuiControl*>( newObj );
GuiFormCtrl*formCtrl = dynamic_cast<GuiFormCtrl*>( newObj );
if( newCtrl && formCtrl )
newCtrl->setCanSave( true );
else if ( newCtrl )
newCtrl->setCanSave( false );
Parent::addObject( newObj );
}
void GuiFormCtrl::removeObject( SimObject* object )
{
if( object == mMenuBar )
{
mHasMenu = false;
mMenuBar = NULL;
}
Parent::removeObject( object );
}
bool GuiFormCtrl::acceptsAsChild( SimObject* object ) const
{
return Parent::acceptsAsChild( object ) &&
( ( mHasMenu && size() == 1 ) || ( !mHasMenu && !size() ) ); // Only accept a single child.
}
void GuiFormCtrl::onSleep()
{
Parent::onSleep();
mFont = NULL;
}
bool GuiFormCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
if( !Parent::resize(newPosition, newExtent) )
return false;
if( !mAwake || !mProfile->mBitmapArrayRects.size() )
return false;
// Should the caption be modified because the title bar is too small?
S32 textWidth = mProfile->mFont->getStrWidth(mCaption);
S32 newTextArea = getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x;
if(newTextArea < textWidth)
{
static char buf[256];
mUseSmallCaption = true;
mSmallCaption = StringTable->insert("");
S32 strlen = dStrlen((const char*)mCaption);
for(S32 i=strlen; i>=0; --i)
{
dStrcpy(buf, "");
dStrncat(buf, (const char*)mCaption, i);
dStrcat(buf, "...");
textWidth = mProfile->mFont->getStrWidth(buf);
if(textWidth < newTextArea)
{
mSmallCaption = StringTable->insert(buf, true);
break;
}
}
} else
{
mUseSmallCaption = false;
}
onResize_callback();
return true;
}
void GuiFormCtrl::onRender(Point2I offset, const RectI &updateRect)
{
// Fill in the control's child area
RectI boundsRect(offset, getExtent());
boundsRect.point.y += mThumbSize.y;
boundsRect.extent.y -= mThumbSize.y;
// draw the border of the form if specified
if (mProfile->mOpaque)
GFX->getDrawUtil()->drawRectFill(boundsRect, mProfile->mFillColor);
if (mProfile->mBorder)
renderBorder(boundsRect, mProfile);
// If we don't have a child, put some text in the child area
if( empty() )
{
GFX->getDrawUtil()->setBitmapModulation(ColorI(0,0,0));
renderJustifiedText(boundsRect.point, boundsRect.extent, "[none]");
}
S32 textWidth = 0;
// Draw our little bar, too
if (mProfile->mBitmapArrayRects.size() >= 5)
{
GFX->getDrawUtil()->clearBitmapModulation();
S32 barStart = offset.x + textWidth;
S32 barTop = mThumbSize.y / 2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y / 2;
Point2I barOffset(barStart, barTop);
// Draw the start of the bar...
GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject ,RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] );
// Now draw the middle...
barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x + 1;
if (barMiddleSize > 0)
{
// We have to do this inset to prevent nasty stretching artifacts
RectI foo = mProfile->mBitmapArrayRects[3];
foo.inset(1,0);
GFX->getDrawUtil()->drawBitmapStretchSR(
mProfile->mTextureObject,
RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
foo
);
}
// And the end
barOffset.x += barMiddleSize;
GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
mProfile->mBitmapArrayRects[4]);
GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
renderJustifiedText(Point2I(mThumbSize.x, 0) + offset, Point2I(getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption) );
}
// Render the children
renderChildControls(offset, updateRect);
}
void GuiFormCtrl::onMouseMove(const GuiEvent &event)
{
Point2I localMove = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
mMouseOver = (localMove.y < mThumbSize.y);
if(isMouseLocked())
mDepressed = mMouseOver;
}
void GuiFormCtrl::onMouseEnter(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
{
mDepressed = true;
mMouseOver = true;
}
else
{
mMouseOver = true;
}
}
void GuiFormCtrl::onMouseLeave(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
mDepressed = false;
mMouseOver = false;
}
void GuiFormCtrl::onMouseDown(const GuiEvent &event)
{
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
if(localClick.y < mThumbSize.y)
{
mouseLock();
mDepressed = true;
mMouseMovingWin = mCanMove;
//update
setUpdate();
}
mOrigBounds = getBounds();
mMouseDownPosition = event.mousePoint;
if (mMouseMovingWin )
{
mouseLock();
}
else
{
GuiControl *ctrl = findHitControl(localClick);
if (ctrl && ctrl != this)
ctrl->onMouseDown(event);
}
}
void GuiFormCtrl::onMouseUp(const GuiEvent &event)
{
// Make sure we only get events we ought to be getting...
if (! mActive)
return;
mouseUnlock();
setUpdate();
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
//if(localClick.y < mThumbSize.y && mDepressed)
// setCollapsed(!mCollapsed);
}
DefineEngineMethod( GuiFormCtrl, getMenuID, S32, (),,
"Get the ID of this form's menu.\n\n"
"@return The ID of the form menu\n" )
{
return object->getMenuBarID();
}
U32 GuiFormCtrl::getMenuBarID()
{
return mMenuBar ? mMenuBar->getId() : 0;
}
DefineEngineMethod( GuiFormCtrl, setCaption, void, ( const char* caption ),,
"Sets the title of the form.\n\n"
"@param caption Form caption\n" )
{
object->setCaption( caption );
}
#endif

View file

@ -0,0 +1,121 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIFORMCTRL_H_
#define _GUIFORMCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUI_PANEL_H_
#include "gui/containers/guiPanel.h"
#endif
#ifndef _GUIMENUBAR_H_
#include "gui/editor/guiMenuBar.h"
#endif
#ifndef _GUICANVAS_H_
#include "gui/core/guiCanvas.h"
#endif
#include "console/console.h"
#include "console/consoleTypes.h"
class GuiMenuBar;
/// @addtogroup gui_container_group Containers
///
/// @ingroup gui_group Gui System
/// @{
class GuiFormCtrl : public GuiPanel
{
private:
typedef GuiPanel Parent;
Resource<GFont> mFont;
String mCaption;
bool mCanMove;
bool mUseSmallCaption;
String mSmallCaption;
StringTableEntry mContentLibrary;
StringTableEntry mContent;
Point2I mThumbSize;
bool mHasMenu;
GuiMenuBar* mMenuBar;
bool mMouseMovingWin;
Point2I mMouseDownPosition;
RectI mOrigBounds;
RectI mStandardBounds;
RectI mCloseButton;
RectI mMinimizeButton;
RectI mMaximizeButton;
bool mMouseOver;
bool mDepressed;
static bool _setHasMenu( void *object, const char *index, const char *data );
protected:
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onResize, () );
/// @}
public:
GuiFormCtrl();
virtual ~GuiFormCtrl();
void setCaption( const char* caption ) { mCaption = caption; }
void setHasMenu( bool value );
bool resize(const Point2I &newPosition, const Point2I &newExtent);
void onRender(Point2I offset, const RectI &updateRect);
bool onWake();
void onSleep();
virtual void addObject( SimObject *object );
virtual void removeObject( SimObject* object );
virtual bool acceptsAsChild( SimObject* object ) const;
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event);
U32 getMenuBarID();
static void initPersistFields();
DECLARE_CONOBJECT(GuiFormCtrl);
};
/// @}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,191 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIFRAMECTRL_H_
#define _GUIFRAMECTRL_H_
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
// for debugging porpoises...
#define GUI_FRAME_DEBUG
// ...save the porpoises
/// A gui control allowing a window to be subdivided into panes,
/// each of which displays a gui control child of the
/// GuiFrameSetCtrl. Each gui control child will have an associated
/// FrameDetail through which frame-specific details can be
/// assigned. Frame-specific values override the values specified
/// for the entire frameset.
///
/// Note that it is possible to have more children than frames,
/// or more frames than children. In the former case, the extra
/// children will not be visible (they are moved beyond the
/// visible extent of the frameset). In the latter case, frames
/// will be empty.
///
/// If a frameset had two columns and two rows but only three
/// gui control children they would be assigned to frames as
/// follows:
/// 1 | 2
/// -----
/// 3 |
///
/// The last frame would be blank.
///
class GuiFrameSetCtrl : public GuiContainer
{
private:
typedef GuiContainer Parent;
public:
enum FrameState
{
FRAME_STATE_ON, // ON overrides OFF
FRAME_STATE_OFF, // OFF overrides AUTO
FRAME_STATE_AUTO, // AUTO == ON, unless overridden
NO_HIT = -1,
DEFAULT_BORDER_WIDTH = 4,
DEFAULT_COLUMNS = 1,
DEFAULT_ROWS = 1,
DEFAULT_MIN_FRAME_EXTENT = 64
};
enum Region
{
VERTICAL_DIVIDER,
HORIZONTAL_DIVIDER,
DIVIDER_INTERSECTION,
NONE
};
struct FrameDetail
{
U32 mBorderWidth;
ColorI mBorderColor;
S32 mBorderEnable;
S32 mBorderMovable;
Point2I mMinExtent;
RectSpacingI mPadding;
FrameDetail() { mBorderWidth = DEFAULT_BORDER_WIDTH; mBorderEnable = FRAME_STATE_AUTO; mBorderMovable = FRAME_STATE_AUTO; mMinExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); mPadding.setAll( 0 ); }
};
DECLARE_CONOBJECT(GuiFrameSetCtrl);
DECLARE_DESCRIPTION( "A container that allows to subdivide its space into rows and columns.\n"
"Child controls are assigned to the cells row by row." );
static void initPersistFields();
GuiFrameSetCtrl();
GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[] = NULL, const U32 rowOffsets[] = NULL);
virtual ~GuiFrameSetCtrl();
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
virtual bool resize(const Point2I &newPosition, const Point2I &newExtent);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
bool onAdd();
void onRender(Point2I offset, const RectI &updateRect );
protected:
/* member variables */
Vector<S32> mColumnOffsets;
Vector<S32> mRowOffsets;
GuiCursor *mMoveCursor;
GuiCursor *mUpDownCursor;
GuiCursor *mLeftRightCursor;
GuiCursor *mDefaultCursor;
FrameDetail mFramesetDetails;
VectorPtr<FrameDetail *> mFrameDetails;
bool mAutoBalance;
S32 mFudgeFactor;
/* divider activation member variables */
Region mCurHitRegion;
Point2I mLocOnDivider;
S32 mCurVerticalHit;
S32 mCurHorizontalHit;
bool init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]);
Region findHitRegion(const Point2I &point);
Region pointInAnyRegion(const Point2I &point);
S32 findResizableFrames(S32 indexes[]);
bool hitVerticalDivider(S32 x, const Point2I &point);
bool hitHorizontalDivider(S32 y, const Point2I &point);
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
void rebalance(const Point2I &newExtent);
void computeSizes(bool balanceFrames = false);
void computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]);
void drawDividers(const Point2I &offset);
public:
U32 columns() const { return(mColumnOffsets.size()); }
U32 rows() const { return(mRowOffsets.size()); }
U32 borderWidth() const { return(mFramesetDetails.mBorderWidth); }
Vector<S32>* columnOffsets() { return(&mColumnOffsets); }
Vector<S32>* rowOffsets() { return(&mRowOffsets); }
FrameDetail* framesetDetails() { return(&mFramesetDetails); }
bool findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd);
void frameBorderEnable(S32 index, const char *state = NULL);
void frameBorderMovable(S32 index, const char *state = NULL);
void frameMinExtent(S32 index, const Point2I &extent);
void framePadding(S32 index, const RectSpacingI &padding);
RectSpacingI getFramePadding(S32 index);
void balanceFrames() { computeSizes(true); }
void updateSizes() { computeSizes(); }
bool onWake();
private:
GuiFrameSetCtrl(const GuiFrameSetCtrl &);
GuiFrameSetCtrl& operator=(const GuiFrameSetCtrl &);
};
typedef GuiFrameSetCtrl::FrameState GuiFrameState;
DefineEnumType( GuiFrameState );
//-----------------------------------------------------------------------------
// x is the first value inside the next column, so the divider x-coords
// precede x.
inline bool GuiFrameSetCtrl::hitVerticalDivider(S32 x, const Point2I &point)
{
return((point.x >= S32(x - mFramesetDetails.mBorderWidth)) && (point.x < x) && (point.y >= 0) && (point.y < S32(getHeight())));
}
//-----------------------------------------------------------------------------
// y is the first value inside the next row, so the divider y-coords precede y.
inline bool GuiFrameSetCtrl::hitHorizontalDivider(S32 y, const Point2I &point)
{
return((point.x >= 0) && (point.x < S32(getWidth())) && (point.y >= S32(y - mFramesetDetails.mBorderWidth)) && (point.y < y));
}
#endif // _GUI_FRAME_CTRL_H

View file

@ -0,0 +1,396 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/engineAPI.h"
#include "platform/platform.h"
#include "gui/containers/guiPaneCtrl.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT(GuiPaneControl);
ConsoleDocClass( GuiPaneControl,
"@brief A collapsable pane control.\n\n"
"This class wraps a single child control and displays a header with caption "
"above it. If you click the header it will collapse or expand (if <i>collapsable</i> "
"is enabled). The control resizes itself based on its collapsed/expanded size.<br>"
"In the GUI editor, if you just want the header you can make <i>collapsable</i> "
"false. The caption field lets you set the caption; it expects a bitmap (from "
"the GuiControlProfile) that contains two images - the first is displayed when "
"the control is expanded and the second is displayed when it is collapsed. The "
"header is sized based on the first image.\n\n"
"@tsexample\n"
"new GuiPaneControl()\n"
"{\n"
" caption = \"Example Pane\";\n"
" collapsable = \"1\";\n"
" barBehindText = \"1\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
//-----------------------------------------------------------------------------
GuiPaneControl::GuiPaneControl()
{
setMinExtent(Point2I(16,16));
mActive = true;
mCollapsable = true;
mCollapsed = false;
mBarBehindText = true;
mMouseOver = false;
mDepressed = false;
mCaption = "A Pane";
mCaptionID = StringTable->insert("");
mIsContainer = true;
mOriginalExtents.set(10,10);
}
//-----------------------------------------------------------------------------
void GuiPaneControl::initPersistFields()
{
addGroup( "Pane" );
addField("caption", TypeRealString, Offset(mCaption, GuiPaneControl),
"Text label to display as the pane header." );
addField("captionID", TypeString, Offset(mCaptionID, GuiPaneControl),
"String table text ID to use as caption string (overrides 'caption')." );
addField("collapsable", TypeBool, Offset(mCollapsable, GuiPaneControl),
"Whether the pane can be collapsed by clicking its header." );
addField("barBehindText", TypeBool, Offset(mBarBehindText, GuiPaneControl),
"Whether to draw the bitmapped pane bar behind the header text, too." );
endGroup( "Pane" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
bool GuiPaneControl::onWake()
{
if ( !Parent::onWake() )
return false;
if( !mProfile->mFont )
{
Con::errorf( "GuiPaneControl::onWake - profile has no valid font" );
return false;
}
if(mCaptionID && *mCaptionID != 0)
setCaptionID(mCaptionID);
mProfile->constructBitmapArray();
if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size())
{
mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y );
mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent );
if( mProfile->mFont->getHeight() > mThumbSize.y )
mThumbSize.y = mProfile->mFont->getHeight();
}
else
{
mThumbSize.set(20, 20);
}
return true;
}
//-----------------------------------------------------------------------------
void GuiPaneControl::setCaptionID(const char *id)
{
S32 n = Con::getIntVariable(id, -1);
if(n != -1)
{
mCaptionID = StringTable->insert(id);
setCaptionID(n);
}
}
//-----------------------------------------------------------------------------
void GuiPaneControl::setCaptionID(S32 id)
{
mCaption = getGUIString(id);
}
//-----------------------------------------------------------------------------
bool GuiPaneControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
// CodeReview WTF is going on here that we need to bypass parent sanity?
// Investigate this [7/1/2007 justind]
if( !Parent::resize( newPosition, newExtent ) )
return false;
mOriginalExtents.x = getWidth();
/*
GuiControl *parent = getParent();
if (parent)
parent->childResized(this);
setUpdate();
*/
// Resize the child control if we're not collapsed
if(size() && !mCollapsed)
{
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
if(gc)
{
Point2I offset(0, mThumbSize.y);
gc->resize(offset, newExtent - offset);
}
}
// For now.
return true;
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onRender(Point2I offset, const RectI &updateRect)
{
// Render our awesome little doogong
if(mProfile->mBitmapArrayRects.size() >= 2 && mCollapsable)
{
S32 idx = mCollapsed ? 0 : 1;
GFX->getDrawUtil()->clearBitmapModulation();
GFX->getDrawUtil()->drawBitmapStretchSR(
mProfile->mTextureObject,
RectI(offset, mProfile->mBitmapArrayRects[idx].extent),
mProfile->mBitmapArrayRects[idx]
);
}
S32 textWidth = 0;
if(!mBarBehindText)
{
GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
textWidth = GFX->getDrawUtil()->drawText(
mProfile->mFont,
Point2I(mThumbSize.x, 0) + offset,
mCaption,
mProfile->mFontColors
);
}
// Draw our little bar, too
if(mProfile->mBitmapArrayRects.size() >= 5)
{
GFX->getDrawUtil()->clearBitmapModulation();
S32 barStart = mThumbSize.x + offset.x + textWidth;
S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2;
Point2I barOffset(barStart, barTop);
// Draw the start of the bar...
GFX->getDrawUtil()->drawBitmapStretchSR(
mProfile->mTextureObject,
RectI(barOffset, mProfile->mBitmapArrayRects[2].extent),
mProfile->mBitmapArrayRects[2]
);
// Now draw the middle...
barOffset.x += mProfile->mBitmapArrayRects[2].extent.x;
S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x;
if(barMiddleSize>0)
{
// We have to do this inset to prevent nasty stretching artifacts
RectI foo = mProfile->mBitmapArrayRects[3];
foo.inset(1,0);
GFX->getDrawUtil()->drawBitmapStretchSR(
mProfile->mTextureObject,
RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
foo
);
}
// And the end
barOffset.x += barMiddleSize;
GFX->getDrawUtil()->drawBitmapStretchSR(
mProfile->mTextureObject,
RectI(barOffset, mProfile->mBitmapArrayRects[4].extent),
mProfile->mBitmapArrayRects[4]
);
}
if(mBarBehindText)
{
GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor));
GFX->getDrawUtil()->drawText(
mProfile->mFont,
Point2I(mThumbSize.x, 0) + offset,
mCaption,
mProfile->mFontColors
);
}
// Draw child controls if appropriate
if(!mCollapsed)
renderChildControls(offset, updateRect);
}
//-----------------------------------------------------------------------------
void GuiPaneControl::setCollapsed(bool isCollapsed)
{
// Get the child
if(size() == 0 || !mCollapsable) return;
GuiControl *gc = dynamic_cast<GuiControl*>(operator[](0));
if(mCollapsed && !isCollapsed)
{
resize(getPosition(), mOriginalExtents);
mCollapsed = false;
if(gc)
gc->setVisible(true);
}
else if(!mCollapsed && isCollapsed)
{
mCollapsed = true;
mOriginalExtents = getExtent();
resize(getPosition(), Point2I(getExtent().x, mThumbSize.y));
if(gc)
gc->setVisible(false);
}
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onMouseMove(const GuiEvent &event)
{
Point2I localMove = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
mMouseOver = (localMove.y < mThumbSize.y);
if(isMouseLocked())
mDepressed = mMouseOver;
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onMouseEnter(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
{
mDepressed = true;
mMouseOver = true;
}
else
{
mMouseOver = true;
}
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onMouseLeave(const GuiEvent &event)
{
setUpdate();
if(isMouseLocked())
mDepressed = false;
mMouseOver = false;
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onMouseDown(const GuiEvent &event)
{
if(!mCollapsable)
return;
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
if(localClick.y < mThumbSize.y)
{
mouseLock();
mDepressed = true;
//update
setUpdate();
}
}
//-----------------------------------------------------------------------------
void GuiPaneControl::onMouseUp(const GuiEvent &event)
{
// Make sure we only get events we ought to be getting...
if (! mActive)
return;
if(!mCollapsable)
return;
mouseUnlock();
setUpdate();
Point2I localClick = globalToLocalCoord(event.mousePoint);
// If we're clicking in the header then resize
if(localClick.y < mThumbSize.y && mDepressed)
setCollapsed(!mCollapsed);
}
//=============================================================================
// Console Methods.
//=============================================================================
DefineEngineMethod( GuiPaneControl, setCollapsed, void, ( bool collapse ),,
"Collapse or un-collapse the control.\n\n"
"@param collapse True to collapse the control, false to un-collapse it\n" )
{
object->setCollapsed( collapse );
}

View file

@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIPANECTRL_H_
#define _GUIPANECTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GFXDEVICE_H_
#include "gfx/gfxDevice.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
/// Collapsable pane control.
///
/// This class wraps a single child control and displays a header with caption
/// above it. If you click the header it will collapse or expand. The control
/// resizes itself based on its collapsed/expanded size.
///
/// In the GUI editor, if you just want the header you can make collapsable
/// false. The caption field lets you set the caption. It expects a bitmap
/// (from the GuiControlProfile) that contains two images - the first is
/// displayed when the control is expanded and the second is displayed when
/// it is collapsed. The header is sized based off of the first image.
class GuiPaneControl : public GuiControl
{
public:
typedef GuiControl Parent;
protected:
/// If true, the pane can be collapsed and extended by clicking its header.
bool mCollapsable;
/// Whether the pane is currently collapsed.
bool mCollapsed;
/// Whether to display the bitmapped pane bar behind the header text, too.
bool mBarBehindText;
///
Point2I mOriginalExtents;
/// Text to display as the pane caption.
String mCaption;
/// String table text ID to use as caption string.
StringTableEntry mCaptionID;
///
Point2I mThumbSize;
///
bool mMouseOver;
///
bool mDepressed;
public:
GuiPaneControl();
/// Return whether the pane is currently collapsed.
bool getCollapsed() { return mCollapsed; };
/// Collapse or expand the pane.
void setCollapsed(bool isCollapsed);
virtual void setCaptionID(S32 id);
virtual void setCaptionID(const char *id);
// GuiControl.
virtual bool onWake();
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseMove(const GuiEvent &event);
virtual void onMouseLeave(const GuiEvent &event);
virtual void onMouseEnter(const GuiEvent &event);
virtual bool resize(const Point2I &newPosition, const Point2I &newExtent);
virtual void onRender(Point2I offset, const RectI &updateRect);
static void initPersistFields();
DECLARE_CONOBJECT(GuiPaneControl);
DECLARE_CATEGORY( "Gui Containers" );
DECLARE_DESCRIPTION( "A container that wraps a single child control displaying a header\n"
"with a caption above it. If enabled, the pane can be collapsed and expanded\n"
"by clicking the header." );
};
#endif

View file

@ -0,0 +1,113 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "gui/containers/guiPanel.h"
#include "console/consoleTypes.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
//-----------------------------------------------------------------------------
// GuiPanel
//-----------------------------------------------------------------------------
ConsoleDocClass( GuiPanel,
"@brief The GuiPanel panel is a container that when opaque will "
"draw a left to right gradient using its profile fill and "
"fill highlight colors.\n\n"
"@tsexample\n"
"// Mandatory GuiDefaultProfile\n"
"// Contains the fill color information required by a GuiPanel\n"
"// Some values left out for sake of this example\n"
"new GuiControlProfile (GuiDefaultProfile)\n"
"{\n"
" // fill color\n"
" opaque = false;\n"
" fillColor = \"242 241 240\";\n"
" fillColorHL =\"228 228 235\";\n"
" fillColorSEL = \"98 100 137\";\n"
" fillColorNA = \"255 255 255 \";\n"
"};\n\n"
"new GuiPanel(TestPanel)\n"
"{\n"
" position = \"45 33\";\n"
" extent = \"342 379\";\n"
" minExtent = \"16 16\";\n"
" horizSizing = \"right\";\n"
" vertSizing = \"bottom\";\n"
" profile = \"GuiDefaultProfile\"; // Color fill info is in this profile\n"
" isContainer = \"1\";\n"
"};\n"
"@endtsexample\n\n"
"@see GuiControlProfile\n"
"@ingroup GuiContainers\n");
GuiPanel::GuiPanel()
{
setMinExtent( Point2I( 16,16 ) );
setDocking( Docking::dockNone );
mIsContainer = true;
}
GuiPanel::~GuiPanel()
{
}
IMPLEMENT_CONOBJECT(GuiPanel);
void GuiPanel::onRender(Point2I offset, const RectI &updateRect)
{
if ( !mProfile->mOpaque )
{
RectI ctrlRect = getClientRect();
ctrlRect.point += offset;
// Draw border.
if( mProfile->mBorder != 0 )
{
GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mBorderColor );
ctrlRect.inset( mProfile->mBorderThickness, mProfile->mBorderThickness );
}
// Draw a gradient left to right.
PrimBuild::begin( GFXTriangleStrip, 4 );
PrimBuild::color( mProfile->mFillColorHL );
PrimBuild::vertex2i( ctrlRect.point.x, ctrlRect.point.y );
PrimBuild::vertex2i( ctrlRect.point.x, ctrlRect.point.y + ctrlRect.extent.y );
PrimBuild::color( mProfile->mFillColor );
PrimBuild::vertex2i( ctrlRect.point.x + ctrlRect.extent.x, ctrlRect.point.y);
PrimBuild::vertex2i( ctrlRect.point.x + ctrlRect.extent.x, ctrlRect.point.y + ctrlRect.extent.y );
PrimBuild::end();
}
Parent::onRender( offset, updateRect );
}

View file

@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUI_PANEL_H_
#define _GUI_PANEL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUITICKCTRL_H_
#include "gui/shiny/guiTickCtrl.h"
#endif
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
/// The GuiPanel panel is a container that when opaque will
/// draw a left to right gradient using its profile fill and
/// fill highlight colors.
///
/// @addtogroup gui_container_group Containers
///
/// @ingroup gui_group Gui System
/// @{
class GuiPanel : public GuiContainer
{
private:
typedef GuiContainer Parent;
public:
// Constructor/Destructor/ConObject Declaration
GuiPanel();
virtual ~GuiPanel();
DECLARE_CONOBJECT(GuiPanel);
// GuiControl
void onRender(Point2I offset, const RectI &updateRect);
void setVisible(bool value) { Parent::setVisible(value); setUpdateLayout( updateParent ); }
};
/// @}
#endif // _GUI_PANEL_H_

View file

@ -0,0 +1,650 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiRolloutCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT( GuiRolloutCtrl );
ConsoleDocClass( GuiRolloutCtrl,
"@brief A container that shows a single child with an optional header bar that can be used to collapse and expand the rollout.\n\n"
"A rollout is a container that can be collapsed and expanded using smooth animation. By default, rollouts will display a header "
"with a caption along the top edge of the control which can be clicked by the user to toggle the collapse state of the rollout.\n\n"
"Rollouts will automatically size themselves to exactly fit around their child control. They will also automatically position their child "
"control in their upper left corner below the header (if present).\n\n"
"@note GuiRolloutCtrls will only work correctly with a single child control. To put multiple controls in a rollout, put them "
"in their own group using a new GuiControl which then can be put inside the rollout.\n\n"
"@ingroup GuiContainers"
);
IMPLEMENT_CALLBACK( GuiRolloutCtrl, onHeaderRightClick, void, (), (),
"Called when the user right-clicks on the rollout's header. This is useful for implementing "
"context menus for rollouts." );
IMPLEMENT_CALLBACK( GuiRolloutCtrl, onExpanded, void, (), (),
"Called when the rollout is expanded." );
IMPLEMENT_CALLBACK( GuiRolloutCtrl, onCollapsed, void, (), (),
"Called when the rollout is collapsed." );
//-----------------------------------------------------------------------------
GuiRolloutCtrl::GuiRolloutCtrl()
{
mExpanded.set(0,0,200,60);
mCaption = StringTable->EmptyString();
mIsExpanded = true;
mIsAnimating = false;
mCollapsing = false;
mAnimateDestHeight = 40;
mAnimateStep = 1;
mDefaultHeight = 40;
mHideHeader = false;
mMargin.set( 0, 0, 0, 0 );
mIsContainer = true;
mCanCollapse = true;
mAutoCollapseSiblings = false;
// Make sure we receive our ticks.
setProcessTicks();
}
//-----------------------------------------------------------------------------
GuiRolloutCtrl::~GuiRolloutCtrl()
{
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::initPersistFields()
{
addGroup( "Rollout" );
addField( "caption", TypeRealString, Offset( mCaption, GuiRolloutCtrl ),
"Text label to display on the rollout header." );
addField( "margin", TypeRectI, Offset( mMargin, GuiRolloutCtrl ),
"Margin to put around child control." );
addField( "defaultHeight", TypeS32, Offset( mDefaultHeight, GuiRolloutCtrl ),
"Default height of the client area. This is used when no child control has been added to the rollout." );
addProtectedField( "expanded", TypeBool, Offset( mIsExpanded, GuiRolloutCtrl), &setExpanded, &defaultProtectedGetFn,
"The current rollout expansion state." );
addField( "clickCollapse", TypeBool, Offset( mCanCollapse, GuiRolloutCtrl ),
"Whether the rollout can be collapsed by clicking its header." );
addField( "hideHeader", TypeBool, Offset( mHideHeader, GuiRolloutCtrl ),
"Whether to render the rollout header.\n\n"
"@note If this is false, the user cannot toggle the rollout state with the mouse." );
addField( "autoCollapseSiblings", TypeBool, Offset( mAutoCollapseSiblings, GuiRolloutCtrl ),
"Whether to automatically collapse sibling rollouts.\n\n"
"If this is true, the rollout will automatically collapse all sibling rollout controls when it "
"is expanded. If this is false, the auto-collapse behavior can be triggered by CTRL (CMD on MAC) "
"clicking the rollout header. CTRL/CMD clicking also works if this is false, in which case the "
"auto-collapsing of sibling controls will be temporarily deactivated." );
endGroup( "Rollout" );
Parent::initPersistFields();
}
//=============================================================================
// Events.
//=============================================================================
// MARK: ---- Events ----
//-----------------------------------------------------------------------------
bool GuiRolloutCtrl::onAdd()
{
if ( !Parent::onAdd() )
return false;
mHasTexture = ( mProfile ? mProfile->constructBitmapArray() > 0 : false );
if ( mHasTexture )
mBitmapBounds = mProfile->mBitmapArrayRects.address();
// Calculate Heights for this control
calculateHeights();
return true;
}
//-----------------------------------------------------------------------------
bool GuiRolloutCtrl::onWake()
{
if (! Parent::onWake())
return false;
if( !mIsAnimating && mIsExpanded )
sizeToContents();
return true;
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::addObject( SimObject *obj )
{
// Call Parent.
Parent::addObject( obj );
sizeToContents();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::removeObject( SimObject *obj )
{
// Call Parent.
Parent::removeObject( obj );
// Recalculate our rectangles.
calculateHeights();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::onMouseDown( const GuiEvent &event )
{
Point2I localPoint = globalToLocalCoord( event.mousePoint );
mouseLock();
}
//-----------------------------------------------------------------------------
bool GuiRolloutCtrl::_onMouseUp( const GuiEvent &event, bool lockedMouse )
{
Point2I localPoint = globalToLocalCoord( event.mousePoint );
if( mCanCollapse && mHeader.pointInRect( localPoint ) && !mIsAnimating && ( !lockedMouse || isMouseLocked() ) )
{
// If Ctrl/Cmd-clicking a header, collapse all sibling GuiRolloutCtrls.
if( ( mAutoCollapseSiblings && !mIsExpanded && !( event.modifier & SI_PRIMARY_CTRL )
|| ( !mAutoCollapseSiblings && event.modifier & SI_PRIMARY_CTRL ) ) )
{
for( SimSet::iterator iter = getParent()->begin(); iter != getParent()->end(); ++ iter )
{
GuiRolloutCtrl* ctrl = dynamic_cast< GuiRolloutCtrl* >( *iter );
if( ctrl && ctrl != this && ctrl->mCanCollapse )
ctrl->instantCollapse();
}
if( !mIsExpanded )
expand();
}
else
{
// Toggle expansion.
toggleExpanded( false );
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::onMouseUp( const GuiEvent &event )
{
_onMouseUp( event, true );
if( isMouseLocked() )
mouseUnlock();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::onRightMouseUp( const GuiEvent& event )
{
Parent::onRightMouseUp( event );
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mHeader.pointInRect( localMouse ) )
onHeaderRightClick_callback();
}
//-----------------------------------------------------------------------------
bool GuiRolloutCtrl::onMouseUpEditor( const GuiEvent& event, Point2I offset )
{
return ( event.modifier & SI_PRIMARY_ALT && _onMouseUp( event, false ) );
}
//=============================================================================
// Sizing.
//=============================================================================
// MARK: ---- Sizing ----
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::calculateHeights()
{
S32 barHeight = 20;
if ( mHasTexture && mProfile && mProfile->mBitmapArrayRects.size() >= NumBitmaps )
{
// Store Header Rectangle
mHeader.set( 0, 0, getWidth(), mProfile->mBitmapArrayRects[ CollapsedCenter ].extent.y );
// Bottom Bar Max
barHeight = mProfile->mBitmapArrayRects[ TopLeftHeader ].extent.y;
}
else
{
mHeader.set( 0, 0, getWidth(), barHeight );
}
if ( mHideHeader )
{
barHeight = 0;
mHeader.extent.y = 0;
}
GuiControl *content = static_cast<GuiControl*>( at(0) );
if ( content != NULL )
mExpanded.set( 0, 0, getWidth(), barHeight + content->getHeight() + ( mMargin.point.y + mMargin.extent.y ) );
else
mExpanded.set( 0, 0, getWidth(), barHeight + mDefaultHeight );
}
//-----------------------------------------------------------------------------
bool GuiRolloutCtrl::resize( const Point2I &newPosition, const Point2I &newExtent )
{
if ( !Parent::resize( newPosition, newExtent ) )
return false;
// Recalculate Heights and resize ourself appropriately.
calculateHeights();
GuiControl *content = dynamic_cast<GuiControl*>( at(0) );
// Size Content Properly?!
if ( mNotifyChildrenResized && content != NULL )
{
S32 barHeight = ( mHideHeader ) ? 0 : 20;
if( !mHideHeader && mHasTexture && mProfile && mProfile->mBitmapArrayRects.size() >= NumBitmaps )
{
barHeight = mProfile->mBitmapArrayRects[ TopLeftHeader ].extent.y;
}
mChildRect.set( mMargin.point.x,
mHeader.extent.y + mMargin.point.y,
getWidth() - ( mMargin.point.x + mMargin.extent.x ),
getHeight() - ( barHeight + ( mMargin.point.y + mMargin.extent.y ) ) );
if ( content->resize( mChildRect.point, mChildRect.extent ) )
return true;
}
// Nothing sized
return false;
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::sizeToContents()
{
calculateHeights();
// Set destination height
if ( size() > 0 )
instantExpand();
else
instantCollapse();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::instantExpand()
{
mAnimateDestHeight = mExpanded.extent.y;
mCollapsing = false;
mIsExpanded = true;
mIsAnimating = false;
resize( getPosition() + mExpanded.point, mExpanded.extent );
onExpanded_callback();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::instantCollapse()
{
mAnimateDestHeight = mHeader.extent.y;
mCollapsing = false;
mIsExpanded = false;
mIsAnimating = false;
resize( getPosition() + mHeader.point, mHeader.extent );
onCollapsed_callback();
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::toggleExpanded( bool instant )
{
if ( mIsExpanded )
{
if ( instant )
instantCollapse();
else
collapse();
}
else
{
if ( instant )
instantExpand();
else
expand();
}
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::childResized( GuiControl *child )
{
Parent::childResized( child );
calculateHeights();
// While we are animating we are constantly resizing our children
// and therefore need to ignore this call to 'instantExpand' which would
// halt the animation in some crappy intermediate stage.
if ( mIsExpanded && !mIsAnimating )
{
mNotifyChildrenResized = false;
instantExpand();
mNotifyChildrenResized = true;
}
}
//=============================================================================
// Animation.
//=============================================================================
// MARK: ---- Animation ----
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::animateTo( S32 height )
{
// We do nothing if we're already animating
if( mIsAnimating )
return;
bool collapsing = (bool)( getHeight() > height );
// If we're already at the destination height, bail
if ( getHeight() >= height && !collapsing )
{
mIsExpanded = true;
return;
}
// If we're already at the destination height, bail
if ( getHeight() <= height && collapsing )
{
mIsExpanded = false;
return;
}
// Set destination height
mAnimateDestHeight = height;
// Set Animation Mode
mCollapsing = collapsing;
// Set Animation Step (Increment)
if ( collapsing )
mAnimateStep = (S32)mFloor( (F32)( getHeight() - height ) / 3.f );
else
mAnimateStep = (S32)mFloor( (F32)( height - getHeight() ) / 3.f );
// Start our animation
mIsAnimating = true;
}
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::processTick()
{
// We do nothing here if we're NOT animating
if ( !mIsAnimating )
return;
// Sanity check to fix non collapsing panels.
if ( mAnimateStep == 0 )
mAnimateStep = 1;
S32 newHeight = getHeight();
// We're collapsing ourself down (Hiding our contents)
if( mCollapsing )
{
if ( newHeight < mAnimateDestHeight )
newHeight = mAnimateDestHeight;
else if ( ( newHeight - mAnimateStep ) < mAnimateDestHeight )
newHeight = mAnimateDestHeight;
if ( newHeight == mAnimateDestHeight )
mIsAnimating = false;
else
newHeight -= mAnimateStep;
if( !mIsAnimating )
{
mIsExpanded = false;
}
}
else // We're expanding ourself (Showing our contents)
{
if ( newHeight > mAnimateDestHeight )
newHeight = mAnimateDestHeight;
else if ( ( newHeight + mAnimateStep ) > mAnimateDestHeight )
newHeight = mAnimateDestHeight;
if ( newHeight == mAnimateDestHeight )
mIsAnimating = false;
else
newHeight += mAnimateStep;
if ( !mIsAnimating )
mIsExpanded = true;
}
if ( newHeight != getHeight() )
setHeight( newHeight );
if ( !mIsAnimating )
{
if( mCollapsing )
onCollapsed_callback();
else if( !mCollapsing )
onExpanded_callback();
calculateHeights();
}
GuiControl* parent = getParent();
if ( parent )
{
parent->childResized( this );
// if our parent's parent is a scroll control, scrollvisible.
GuiScrollCtrl* scroll = dynamic_cast<GuiScrollCtrl*>( parent->getParent() );
if ( scroll )
{
scroll->scrollRectVisible( getBounds() );
}
}
}
//=============================================================================
// Rendering.
//=============================================================================
// MARK: ---- Rendering ----
//-----------------------------------------------------------------------------
void GuiRolloutCtrl::onRender( Point2I offset, const RectI &updateRect )
{
if( !mProfile || mProfile->mFont == NULL )
return;
// Calculate actual world bounds for rendering
RectI worldBounds( offset, getExtent() );
// if opaque, fill the update rect with the fill color
if ( mProfile->mOpaque )
GFX->getDrawUtil()->drawRectFill( worldBounds, mProfile->mFillColor );
if ( mProfile->mBitmapArrayRects.size() >= NumBitmaps )
{
GFX->getDrawUtil()->clearBitmapModulation();
// Draw Rollout From Skin
if ( !mIsExpanded && !mIsAnimating )
renderFixedBitmapBordersFilled( worldBounds, 1, mProfile );
else if ( mHideHeader )
renderSizableBitmapBordersFilledIndex( worldBounds, MidPageLeft, mProfile );
else
renderSizableBitmapBordersFilledIndex( worldBounds, TopLeftHeader, mProfile );
}
if ( !(mIsExpanded && mHideHeader ) )
{
// Draw Caption ( Vertically Centered )
ColorI currColor;
GFX->getDrawUtil()->getBitmapModulation( &currColor );
Point2I textPosition = mHeader.point + offset + mProfile->mTextOffset;
GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
renderJustifiedText( textPosition, mHeader.extent, mCaption );
GFX->getDrawUtil()->setBitmapModulation( currColor );
}
// If we're collapsed we contain the first child as our content
// thus we don't render it when collapsed. but to support modified
// rollouts with custom header buttons etc we still render our other
// children. -JDD
GuiControl *pChild = dynamic_cast<GuiControl*>( at(0) );
if ( pChild )
{
if ( !mIsExpanded && !mIsAnimating && pChild->isVisible() )
{
pChild->setVisible( false );
}
else if ( (mIsExpanded || mIsAnimating) && !pChild->isVisible() )
{
pChild->setVisible( true );
}
}
renderChildControls( offset, updateRect );
// Render our border should we have it specified in our profile.
renderBorder(worldBounds, mProfile);
}
//=============================================================================
// Console Methods.
//=============================================================================
// MARK: ---- Console Methods ----
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, isExpanded, bool, (),,
"Determine whether the rollout is currently expanded, i.e. whether the child control is visible.\n\n"
"@return True if the rollout is expanded, false if not." )
{
return object->isExpanded();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, collapse, void, (),,
"Collapse the rollout if it is currently expanded. This will make the rollout's child control invisible.\n\n"
"@note The rollout will animate to collapsed state. To instantly collapse without animation, use instantCollapse()." )
{
object->collapse();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, expand, void, (),,
"Expand the rollout if it is currently collapsed. This will make the rollout's child control visible.\n\n"
"@note The rollout will animate to expanded state. To instantly expand without animation, use instantExpand()." )
{
object->expand();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, toggleCollapse, void, (),,
"Toggle the current collapse state of the rollout. If it is currently expanded, then collapse it. If it "
"is currently collapsed, then expand it." )
{
if( object->isExpanded() )
object->collapse();
else
object->expand();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, toggleExpanded, void, ( bool instantly ), ( false ),
"Toggle the current expansion state of the rollout If it is currently expanded, then collapse it. If it "
"is currently collapsed, then expand it.\n\n"
"@param instant If true, the rollout will toggle its state without animation. Otherwise, the rollout will "
"smoothly slide into the opposite state." )
{
object->toggleExpanded( instantly );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, instantCollapse, void, (),,
"Instantly collapse the rollout without animation. To smoothly slide the rollout to collapsed state, use collapse()." )
{
object->instantCollapse();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, instantExpand, void, (),,
"Instantly expand the rollout without animation. To smoothly slide the rollout to expanded state, use expand()." )
{
object->instantExpand();
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiRolloutCtrl, sizeToContents, void, (),,
"Resize the rollout to exactly fit around its child control. This can be used to manually trigger a recomputation of "
"the rollout size." )
{
object->sizeToContents();
}

View file

@ -0,0 +1,179 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUI_ROLLOUTCTRL_H_
#define _GUI_ROLLOUTCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUISTACKCTRL_H_
#include "gui/containers/guiStackCtrl.h"
#endif
#ifndef _H_GUIDEFAULTCONTROLRENDER_
#include "gui/core/guiDefaultControlRender.h"
#endif
#ifndef _GUITICKCTRL_H_
#include "gui/shiny/guiTickCtrl.h"
#endif
/// A container with an optional header that allows its child control to
/// be collapsed using an animated effet.
class GuiRolloutCtrl : public GuiTickCtrl
{
public:
typedef GuiControl Parent;
// Theme Support
enum
{
CollapsedLeft = 0,
CollapsedCenter,
CollapsedRight,
TopLeftHeader,
TopMidHeader,
TopRightHeader,
MidPageLeft,
MidPageCenter,
MidPageRight,
BottomLeftHeader,
BottomMidHeader,
BottomRightHeader,
NumBitmaps ///< Number of bitmaps in this array
};
protected:
/// Label to display on rollout header.
String mCaption;
RectI mHeader;
RectI mExpanded;
RectI mChildRect;
RectI mMargin;
bool mIsExpanded;
bool mIsAnimating;
bool mCollapsing;
S32 mAnimateDestHeight;
S32 mAnimateStep;
S32 mDefaultHeight;
/// Whether the rollout can be collapsed.
bool mCanCollapse;
/// Whether to hide the rollout header.
bool mHideHeader;
/// Whether to automatically collapse sibling rollouts when this one
/// is expanded.
bool mAutoCollapseSiblings;
GuiCursor* mDefaultCursor;
GuiCursor* mVertSizingCursor;
/// Indicates whether we have a texture to render the tabs with.
bool mHasTexture;
/// Array of rectangles identifying textures for rollout.
RectI *mBitmapBounds;
// Property - "Expanded"
static bool setExpanded( void *object, const char *index, const char *data )
{
bool expand = dAtob( data );
if( expand )
static_cast<GuiRolloutCtrl*>(object)->instantExpand();
else
static_cast<GuiRolloutCtrl*>(object)->instantCollapse();
return false;
};
bool _onMouseUp( const GuiEvent& event, bool lockedMouse );
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onHeaderRightClick, () );
DECLARE_CALLBACK( void, onExpanded, () );
DECLARE_CALLBACK( void, onCollapsed, () );
/// @}
public:
GuiRolloutCtrl();
~GuiRolloutCtrl();
DECLARE_CONOBJECT(GuiRolloutCtrl);
DECLARE_CATEGORY( "Gui Containers" );
DECLARE_DESCRIPTION( "A container that displays a header with a caption on top of its child control\n"
"that when clicked collapses/expands the control to/from just the header." );
// Persistence
static void initPersistFields();
// Control Events
bool onWake();
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
virtual void childResized(GuiControl *child);
// Mouse Events
virtual void onMouseDown( const GuiEvent& event );
virtual void onMouseUp( const GuiEvent& event );
virtual void onRightMouseUp( const GuiEvent& event );
virtual bool onMouseUpEditor( const GuiEvent& event, Point2I offset );
// Sizing Helpers
virtual void calculateHeights();
virtual bool resize( const Point2I &newPosition, const Point2I &newExtent );
virtual void sizeToContents();
inline bool isExpanded() const { return mIsExpanded; }
// Sizing Animation Functions
void animateTo( S32 height );
virtual void processTick();
void collapse() { animateTo( mHeader.extent.y ); }
void expand() { animateTo( mExpanded.extent.y ); }
void instantCollapse();
void instantExpand();
void toggleExpanded( bool instant );
const String& getCaption() const { return mCaption; }
bool isHeaderHidden() const { return mHideHeader; }
bool canCollapse() const { return mCanCollapse; }
void setCaption( const String& str ) { mCaption = str; }
void setMargin( const RectI& rect ) { mMargin = rect; }
void setMargin( S32 left, S32 top, S32 right, S32 bottom ) { setMargin( RectI( left, top, right, bottom ) ); }
void setHeaderHidden( bool value ) { mHideHeader = value; }
void setCanCollapse( bool value ) { mCanCollapse = value; }
// Control Rendering
virtual void onRender(Point2I offset, const RectI &updateRect);
bool onAdd();
};
#endif // _GUI_ROLLOUTCTRL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,276 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUISCROLLCTRL_H_
#define _GUISCROLLCTRL_H_
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
/// A control providing a window inside a larger client area which can be
/// scrolled using scrollbars.
///
class GuiScrollCtrl : public GuiContainer
{
public:
typedef GuiContainer Parent;
enum Region
{
UpArrow,
DownArrow,
LeftArrow,
RightArrow,
UpPage,
DownPage,
LeftPage,
RightPage,
VertThumb,
HorizThumb,
None
};
enum ScrollBarBehavior
{
ScrollBarAlwaysOn = 0,
ScrollBarAlwaysOff = 1,
ScrollBarDynamic = 2
};
protected:
// the scroll control uses a bitmap array to draw all its
// graphics... these are the bitmaps it needs:
enum BitmapIndices
{
BmpUp,
BmpDown,
BmpVThumbTopCap,
BmpVThumb,
BmpVThumbBottomCap,
BmpVPage,
BmpLeft,
BmpRight,
BmpHThumbLeftCap,
BmpHThumb,
BmpHThumbRightCap,
BmpHPage,
BmpResize,
BmpCount
};
enum BitmapStates
{
BmpDefault = 0,
BmpHilite,
BmpDisabled,
BmpStates
};
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
GFXTexHandle mTextureObject;
S32 mBorderThickness; // this gets set per class in the constructor
Point2I mChildMargin; // the thickness of the margin around the child controls
// note - it is implicit in the scroll view that the buttons all have the same
// arrow length and that horizontal and vertical scroll bars have the
// same thickness
S32 mScrollBarThickness; // determined by the width of the vertical page bmp
S32 mScrollBarArrowBtnLength; // determined by the height of the up arrow
S32 mScrollBarDragTolerance; // maximal distance from scrollbar at which a scrollbar drag is still valid
bool mHBarEnabled;
bool mVBarEnabled;
bool mHasHScrollBar;
bool mHasVScrollBar;
Point2I mContentPos; // the position of the content region in the control's coord system
Point2I mContentExt; // the extent of the content region
Point2I mChildPos; // the position of the upper left corner of the child control(s)
Point2I mChildExt;
Point2I mChildRelPos; // the relative position of the upper left content corner in
// the child's coordinate system - 0,0 if scrolled all the way to upper left.
//--------------------------------------
// for mouse dragging the thumb
Point2I mChildRelPosAnchor; // the original childRelPos when scrolling started
S32 mThumbMouseDelta;
S32 mLastUpdated;
S32 mHThumbSize;
S32 mHThumbPos;
S32 mVThumbSize;
S32 mVThumbPos;
S32 mBaseThumbSize;
RectI mUpArrowRect;
RectI mDownArrowRect;
RectI mLeftArrowRect;
RectI mRightArrowRect;
RectI mHTrackRect;
RectI mVTrackRect;
//--------------------------------------
// for determining hit area
bool mStateDepressed; ///< Is the mouse currently depressed on a scroll region
Region mHitRegion; ///< Which region is hit by the mouse
S32 mForceHScrollBar; ///< Force showing the Horizontal scrollbar
S32 mForceVScrollBar; ///< Force showing the Vertical scrollbar
bool mLockHorizScroll; ///< Is horizontal scrolling disabled
bool mLockVertScroll; ///< Is vertical scrolling disabled
bool mUseConstantHeightThumb;
bool mWillFirstRespond; // for automatically handling arrow keys
/// Used internally to prevent infinite recursion.
bool mIgnoreChildResized;
/// MouseWheel scroll animation
/// @{
/// Is currently performing a scroll animation.
bool mAnimating;
/// Pixels moved per tick when performing a scroll animation.
S32 mScrollAnimSpeed;
/// The target position when performing a scroll animation.
Point2I mScrollTargetPos;
/// Platform time of the last call to onPreRender
S32 mLastPreRender;
/// @}
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onScroll, () );
/// @}
Region findHitRegion(const Point2I &);
virtual bool calcChildExtents();
virtual void calcScrollRects(void);
void calcThumbs();
void scrollByRegion(Region reg);
void scrollByMouseWheel( const GuiEvent &event );
/// Tell the kids that the mouse moved (relatively)
void updateChildMousePos();
///
void _onMouseDown( const GuiEvent& event, bool lockMouse );
public:
GuiScrollCtrl();
void autoScroll(Region reg);
void scrollDeltaAnimate(S32 x, S32 y);
void scrollTo(S32 x, S32 y);
void scrollToObject(GuiControl *targetControl);
void scrollDelta(S32 x, S32 y);
void scrollRectVisible(RectI rect);
/// Is the given client space rect completely visible within the actual
/// visible area, or is some of it clipped. Returns true if it is
/// completely visible.
bool isRectCompletelyVisible(const RectI& rect);
bool isPointVisible( const Point2I& point );
void computeSizes();
// you can change the bitmap array dynamically.
void loadBitmapArray();
void addObject(SimObject *obj);
bool resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
Point2I getChildPos() { return mChildPos; }
Point2I getChildRelPos() { return mChildRelPos; };
Point2I getChildExtent() { return mChildExt; }
Point2I getContentExtent() { return mContentExt; }
Point2I getChildMargin() { return mChildMargin; } // Added to aid in sizing calculations
S32 getBorderThickness(void) { return mBorderThickness; }
S32 scrollBarThickness() const { return(mScrollBarThickness); }
S32 scrollBarArrowBtnLength() const { return(mScrollBarArrowBtnLength); }
bool hasHScrollBar() const { return(mHasHScrollBar); }
bool hasVScrollBar() const { return(mHasVScrollBar); }
bool enabledHScrollBar() const { return(mHBarEnabled); }
bool enabledVScrollBar() const { return(mVBarEnabled); }
bool isScrolledToBottom() { return mChildPos.y + mChildExt.y <= mContentPos.y + mContentExt.y; }
bool wantsTabListMembership();
bool becomeFirstResponder();
bool loseFirstResponder();
Region getCurHitRegion(void) { return mHitRegion; }
// GuiControl
virtual bool onKeyDown(const GuiEvent &event);
virtual void onMouseDown(const GuiEvent &event);
virtual bool onMouseDownEditor( const GuiEvent& event, Point2I offset );
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual bool onMouseWheelUp(const GuiEvent &event);
virtual bool onMouseWheelDown(const GuiEvent &event);
virtual bool onWake();
virtual void onSleep();
virtual void onPreRender();
virtual void onRender(Point2I offset, const RectI &updateRect);
virtual void drawBorder(const Point2I &offset, bool isFirstResponder);
virtual void drawVScrollBar(const Point2I &offset);
virtual void drawHScrollBar(const Point2I &offset);
virtual void drawScrollCorner(const Point2I &offset);
virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1);
static void initPersistFields();
DECLARE_CONOBJECT(GuiScrollCtrl);
DECLARE_DESCRIPTION( "A container that allows to view a larger GUI control inside its smaller area "
"by providing horizontal and/or vertical scroll bars." );
};
typedef GuiScrollCtrl::ScrollBarBehavior GuiScrollBarBehavior;
DefineEnumType( GuiScrollBarBehavior );
#endif //_GUI_SCROLL_CTRL_H

View file

@ -0,0 +1,615 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "gui/containers/guiSplitContainer.h"
#include "gui/core/guiCanvas.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT( GuiSplitContainer );
ConsoleDocClass( GuiSplitContainer,
"@brief A container that splits its area between two child controls.\n\n"
"A GuiSplitContainer can be used to dynamically subdivide an area between two child controls. "
"A splitter bar is placed between the two controls and allows to dynamically adjust the sizing "
"ratio between the two sides. Splitting can be either horizontal (subdividing top and bottom) "
"or vertical (subdividing left and right) depending on #orientation.\n\n"
"By using #fixedPanel, one of the panels can be chosen to remain at a fixed size (#fixedSize)."
"@tsexample\n"
"// Create a vertical splitter with a fixed-size left panel.\n"
"%splitter = new GuiSplitContainer()\n"
"{\n"
" orientation = \"Vertical\";\n"
" fixedPanel = \"FirstPanel\";\n"
" fixedSize = 100;\n"
"\n"
" new GuiScrollCtrl()\n"
" {\n"
" new GuiMLTextCtrl()\n"
" {\n"
" text = %longText;\n"
" };\n"
" };\n"
"\n"
" new GuiScrollCtrl()\n"
" {\n"
" new GuiMLTextCtrl()\n"
" {\n"
" text = %moreLongText;\n"
" };\n"
" };\n"
"};\n"
"@endtsexample\n\n"
"@note The children placed inside GuiSplitContainers must be GuiContainers.\n\n"
"@ingroup GuiContainers"
);
ImplementEnumType( GuiSplitOrientation,
"Axis along which to divide the container's space.\n\n"
"@ingroup GuiContainers" )
{ GuiSplitContainer::Vertical, "Vertical", "Divide vertically placing one child left and one child right." },
{ GuiSplitContainer::Horizontal, "Horizontal", "Divide horizontally placing one child on top and one child below." }
EndImplementEnumType;
ImplementEnumType( GuiSplitFixedPanel,
"Which side of the splitter to keep at a fixed size (if any).\n\n"
"@ingroup GuiContainers" )
{ GuiSplitContainer::None, "None", "Allow both childs to resize (default)." },
{ GuiSplitContainer::FirstPanel, "FirstPanel", "Keep " },
{ GuiSplitContainer::SecondPanel, "SecondPanel" }
EndImplementEnumType;
//-----------------------------------------------------------------------------
GuiSplitContainer::GuiSplitContainer()
: mFixedPanel( None ),
mFixedPanelSize( 100 ),
mOrientation( Vertical ),
mSplitterSize( 2 ),
mSplitPoint( 0, 0 ),
mSplitRect( 0, 0, mSplitterSize, mSplitterSize ),
mDragging( false )
{
setMinExtent( Point2I(64,64) );
setExtent(200,200);
setDocking( Docking::dockNone );
mSplitPoint.set( 300, 100 );
// We only support client docked items in a split container
mValidDockingMask = Docking::dockClient;
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::initPersistFields()
{
addGroup( "Splitter", "Options to configure split panels contained by this control" );
addField( "orientation", TYPEID< Orientation >(), Offset( mOrientation, GuiSplitContainer),
"Whether to split between top and bottom (horizontal) or between left and right (vertical)." );
addField( "splitterSize", TypeS32, Offset( mSplitterSize, GuiSplitContainer),
"Width of the splitter bar between the two sides. Default is 2." );
addField( "splitPoint", TypePoint2I, Offset( mSplitPoint, GuiSplitContainer),
"Point on control through which the splitter goes.\n\n"
"Changed relatively if size of control changes." );
addField( "fixedPanel", TYPEID< FixedPanel >(), Offset( mFixedPanel, GuiSplitContainer),
"Which (if any) side of the splitter to keep at a fixed size." );
addField( "fixedSize", TypeS32, Offset( mFixedPanelSize, GuiSplitContainer),
"Width of the fixed panel specified by #fixedPanel (if any)." );
endGroup( "Splitter" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
bool GuiSplitContainer::onAdd()
{
if ( !Parent::onAdd() )
return false;
return true;
}
//-----------------------------------------------------------------------------
bool GuiSplitContainer::onWake()
{
if ( !Parent::onWake() )
return false;
// Create Panel 1
if ( empty() )
{
GuiPanel *newPanel = new GuiPanel();
AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #1!" );
newPanel->registerObject();
newPanel->setInternalName( "Panel1" );
newPanel->setDocking( Docking::dockClient );
addObject( (SimObject*)newPanel );
}
else
{
GuiContainer *containerCtrl = dynamic_cast<GuiContainer*>( at(0) );
if ( containerCtrl )
{
containerCtrl->setInternalName( "Panel1" );
containerCtrl->setDocking( Docking::dockClient );
}
}
if ( size() == 1 )
{
// Create Panel 2
GuiPanel *newPanel = new GuiPanel();
AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #2!" );
newPanel->registerObject();
newPanel->setInternalName( "Panel2" );
newPanel->setDocking( Docking::dockClient );
addObject( (SimObject*)newPanel );
}
else
{
GuiContainer *containerCtrl = dynamic_cast<GuiContainer*>( at(1) );
if ( containerCtrl )
{
containerCtrl->setInternalName( "Panel2" );
containerCtrl->setDocking( Docking::dockClient );
}
}
// Has FixedWidth been specified?
if ( mFixedPanelSize == 0 )
{
// Nope, so try to guess as best we can
GuiContainer *firstPanel = dynamic_cast<GuiContainer*>( at(0) );
GuiContainer *secondPanel = dynamic_cast<GuiContainer*>( at(1) );
if ( mFixedPanel == FirstPanel )
{
if ( mOrientation == Horizontal )
mFixedPanelSize = firstPanel->getExtent().y;
else
mFixedPanelSize = firstPanel->getExtent().x;
mSplitPoint = Point2I( mFixedPanelSize, mFixedPanelSize );
}
else if ( mFixedPanel == SecondPanel )
{
if ( mOrientation == Horizontal )
mFixedPanelSize = getExtent().y - secondPanel->getExtent().y;
else
mFixedPanelSize = getExtent().x - secondPanel->getExtent().x;
mSplitPoint = getExtent() - Point2I( mFixedPanelSize, mFixedPanelSize );
}
}
setUpdateLayout();
return true;
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::onRender( Point2I offset, const RectI &updateRect )
{
Parent::onRender( offset, updateRect );
// Only render if we're dragging the splitter
if ( mDragging && mSplitRect.isValidRect() )
{
// Splitter Rectangle (will adjust positioning only)
RectI splitterRect = mSplitRect;
// Currently being dragged to Rect
Point2I splitterPoint = localToGlobalCoord( mSplitRect.point );
splitterRect.point = localToGlobalCoord( mSplitPoint );
RectI clientRect = getClientRect();
clientRect.point = localToGlobalCoord( clientRect.point );
if ( mOrientation == Horizontal )
{
splitterRect.point.y -= mSplitterSize;
splitterRect.point.x = splitterPoint.x;
}
else
{
splitterRect.point.x -= mSplitterSize;
splitterRect.point.y = splitterPoint.y;
}
RectI oldClip = GFX->getClipRect();
GFX->setClipRect( clientRect );
GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColorHL );
GFX->setClipRect( oldClip );
}
else
{
RectI splitterRect = mSplitRect;
splitterRect.point += offset;
GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColor );
}
}
//-----------------------------------------------------------------------------
Point2I GuiSplitContainer::getMinExtent() const
{
GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) );
GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) );
if ( !panelOne || !panelTwo )
return Parent::getMinExtent();
Point2I minExtent = Point2I(0,0);
Point2I panelOneMinExtent = panelOne->getMinExtent();
Point2I panelTwoMinExtent = panelTwo->getMinExtent();
if ( mOrientation == Horizontal )
{
minExtent.y = 2 * mSplitterSize + panelOneMinExtent.y + panelTwoMinExtent.y;
minExtent.x = getMax( panelOneMinExtent.x, panelTwoMinExtent.x );
}
else
{
minExtent.x = 2 * mSplitterSize + panelOneMinExtent.x + panelTwoMinExtent.x;
minExtent.y = getMax( panelOneMinExtent.y, panelTwoMinExtent.y );
}
return minExtent;
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::parentResized( const RectI &oldParentRect, const RectI &newParentRect )
{
Parent::parentResized( oldParentRect, newParentRect );
return;
// TODO: Is this right James? This isn't needed anymore?
/*
// GuiSplitContainer overrides parentResized to make sure that the proper fixed frame's width/height
// is not compromised in the call
if ( size() < 2 )
return;
GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) );
GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) );
AssertFatal( panelOne && panelTwo, "GuiSplitContainer::parentResized - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" );
Point2I newDragPos;
if ( mFixedPanel == FirstPanel )
{
newDragPos = panelOne->getExtent();
newDragPos += Point2I( mSplitterSize, mSplitterSize );
}
else if ( mFixedPanel == SecondPanel )
{
newDragPos = getExtent() - panelTwo->getExtent();
newDragPos -= Point2I( mSplitterSize, mSplitterSize );
}
else // None
newDragPos.set( 1, 1);
RectI clientRect = getClientRect();
solvePanelConstraints( newDragPos, panelOne, panelTwo, clientRect );
setUpdateLayout();
*/
}
//-----------------------------------------------------------------------------
bool GuiSplitContainer::resize( const Point2I &newPosition, const Point2I &newExtent )
{
// Save previous extent.
Point2I oldExtent = getExtent();
// Resize ourselves.
if ( !Parent::resize( newPosition, newExtent ) || size() < 2 )
return false;
GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) );
GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) );
//
AssertFatal( panelOne && panelTwo, "GuiSplitContainer::resize - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" );
// We only need to update the split point if our second panel is fixed.
// If the first is fixed, then we can leave the split point alone because
// the remainder of the size will be added to or taken from the second panel
Point2I newDragPos;
if ( mFixedPanel == SecondPanel )
{
S32 deltaX = newExtent.x - oldExtent.x;
S32 deltaY = newExtent.y - oldExtent.y;
if( mOrientation == Horizontal )
mSplitPoint.y += deltaY;
else
mSplitPoint.x += deltaX;
}
// If we got here, parent returned true
return true;
}
//-----------------------------------------------------------------------------
bool GuiSplitContainer::layoutControls( RectI &clientRect )
{
if ( size() < 2 )
return false;
GuiContainer *panelOne = dynamic_cast<GuiContainer*>( at(0) );
GuiContainer *panelTwo = dynamic_cast<GuiContainer*>( at(1) );
//
AssertFatal( panelOne && panelTwo, "GuiSplitContainer::layoutControl - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" );
RectI panelOneRect = RectI( clientRect.point, Point2I( 0, 0 ) );
RectI panelTwoRect;
RectI splitRect;
solvePanelConstraints( getSplitPoint(), panelOne, panelTwo, clientRect );
switch( mOrientation )
{
case Horizontal:
panelOneRect.extent = Point2I( clientRect.extent.x, getSplitPoint().y );
panelTwoRect = panelOneRect;
panelTwoRect.intersect( clientRect );
panelTwoRect.point.y = panelOneRect.extent.y;
panelTwoRect.extent.y = clientRect.extent.y - panelOneRect.extent.y;
// Generate new Splitter Rectangle
splitRect = panelTwoRect;
splitRect.extent.y = 0;
splitRect.inset( 0, -mSplitterSize );
panelOneRect.extent.y -= mSplitterSize;
panelTwoRect.point.y += mSplitterSize;
panelTwoRect.extent.y -= mSplitterSize;
break;
case Vertical:
panelOneRect.extent = Point2I( getSplitPoint().x, clientRect.extent.y );
panelTwoRect = panelOneRect;
panelTwoRect.intersect( clientRect );
panelTwoRect.point.x = panelOneRect.extent.x;
panelTwoRect.extent.x = clientRect.extent.x - panelOneRect.extent.x;
// Generate new Splitter Rectangle
splitRect = panelTwoRect;
splitRect.extent.x = 0;
splitRect.inset( -mSplitterSize, 0 );
panelOneRect.extent.x -= mSplitterSize;
panelTwoRect.point.x += mSplitterSize;
panelTwoRect.extent.x -= mSplitterSize;
break;
}
// Update Split Rect
mSplitRect = splitRect;
// Dock Appropriately
if( !( mFixedPanel == FirstPanel && !panelOne->isVisible() ) )
dockControl( panelOne, panelOne->getDocking(), panelOneRect );
if( !( mFixedPanel == FirstPanel && !panelTwo->isVisible() ) )
dockControl( panelTwo, panelOne->getDocking(), panelTwoRect );
//
return false;
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::solvePanelConstraints( Point2I newDragPos, GuiContainer * firstPanel, GuiContainer * secondPanel, RectI clientRect )
{
if( !firstPanel || !secondPanel )
return;
if ( mOrientation == Horizontal )
{
// Constrain based on Y axis (Horizontal Splitter)
// This accounts for the splitter width
S32 splitterSize = (S32)(mSplitRect.extent.y * 0.5);
// Collapsed fixed panel
if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() )
{
newDragPos = Point2I(mSplitPoint.x, getExtent().y - splitterSize );
}
else if( mFixedPanel == SecondPanel && !firstPanel->isVisible() )
{
newDragPos = Point2I(mSplitPoint.x, splitterSize );
}
else // Normal constraints
{
//newDragPos.y -= splitterSize;
S32 newPosition = mClamp( newDragPos.y,
firstPanel->getMinExtent().y + splitterSize,
getExtent().y - secondPanel->getMinExtent().y - splitterSize );
newDragPos = Point2I( mSplitPoint.x, newPosition );
}
}
else
{
// Constrain based on X axis (Vertical Splitter)
// This accounts for the splitter width
S32 splitterSize = (S32)(mSplitRect.extent.x * 0.5);
// Collapsed fixed panel
if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() )
{
newDragPos = Point2I(getExtent().x - splitterSize, mSplitPoint.y );
}
else if ( mFixedPanel == FirstPanel && !firstPanel->isVisible() )
{
newDragPos = Point2I( splitterSize, mSplitPoint.x );
}
else // Normal constraints
{
S32 newPosition = mClamp( newDragPos.x, firstPanel->getMinExtent().x + splitterSize,
getExtent().x - secondPanel->getMinExtent().x - splitterSize );
newDragPos = Point2I( newPosition, mSplitPoint.y );
}
}
// Just in case, clamp to bounds of controls
newDragPos.x = mClamp( newDragPos.x, clientRect.point.x, clientRect.point.x + clientRect.extent.x );
newDragPos.y = mClamp( newDragPos.y, clientRect.point.y, clientRect.point.y + clientRect.extent.y );
mSplitPoint = newDragPos;
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent )
{
GuiCanvas *rootCtrl = getRoot();
if ( !rootCtrl )
return;
S32 desiredCursor = 0;
RectI splitRect = getSplitRect();
// Figure out which cursor we want if we need one
if ( mOrientation == Horizontal )
desiredCursor = PlatformCursorController::curResizeHorz;
else if ( mOrientation == Vertical )
desiredCursor = PlatformCursorController::curResizeVert;
PlatformWindow *platformWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
AssertFatal( platformWindow != NULL,"GuiControl without owning platform window! This should not be possible." );
PlatformCursorController *cusrorController = platformWindow->getCursorController();
AssertFatal( cusrorController != NULL,"PlatformWindow without an owned CursorController!" );
// Check to see if we need one or just the default...
Point2I localPoint = Point2I( globalToLocalCoord( lastGuiEvent.mousePoint ) );
if ( splitRect.pointInRect( localPoint ) || mDragging )
{
// Do we need to change it or is it already set?
if ( rootCtrl->mCursorChanged != desiredCursor )
{
// We've already changed the cursor, so set it back
if ( rootCtrl->mCursorChanged != -1 )
cusrorController->popCursor();
// Now change the cursor shape
cusrorController->pushCursor( desiredCursor );
rootCtrl->mCursorChanged = desiredCursor;
}
}
else if ( rootCtrl->mCursorChanged != -1 )
{
// Just the default
cusrorController->popCursor();
rootCtrl->mCursorChanged = -1;
}
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::onMouseDown( const GuiEvent &event )
{
GuiContainer *firstPanel = dynamic_cast<GuiContainer*>(at(0));
GuiContainer *secondPanel = dynamic_cast<GuiContainer*>(at(1));
// This function will constrain the panels to their minExtents and update the mSplitPoint
if ( firstPanel && secondPanel )
{
mouseLock();
mDragging = true;
RectI clientRect = getClientRect();
Point2I newDragPos = globalToLocalCoord( event.mousePoint );
solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect);
}
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::onMouseUp( const GuiEvent &event )
{
// If we've been dragging, we need to update the fixed panel extent.
// NOTE : This should ONLY ever happen in this function. the Fixed panel
// is to REMAIN FIXED unless the user changes it.
if ( mDragging )
{
Point2I newSplitPoint = getSplitPoint();
// Update Fixed Panel Extent
if ( mFixedPanel == FirstPanel )
mFixedPanelSize = ( mOrientation == Horizontal ) ? newSplitPoint.y : newSplitPoint.x;
else
mFixedPanelSize = ( mOrientation == Horizontal ) ? getExtent().y - newSplitPoint.y : getExtent().x - newSplitPoint.x;
setUpdateLayout();
}
mDragging = false;
mouseUnlock();
}
//-----------------------------------------------------------------------------
void GuiSplitContainer::onMouseDragged( const GuiEvent &event )
{
GuiContainer *firstPanel = dynamic_cast<GuiContainer*>(at(0));
GuiContainer *secondPanel = dynamic_cast<GuiContainer*>(at(1));
// This function will constrain the panels to their minExtents and update the mSplitPoint
if ( mDragging && firstPanel && secondPanel )
{
RectI clientRect = getClientRect();
Point2I newDragPos = globalToLocalCoord( event.mousePoint );
solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect);
}
}

View file

@ -0,0 +1,110 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUI_SPLTCONTAINER_H_
#define _GUI_SPLTCONTAINER_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
#ifndef _GUI_PANEL_H_
#include "gui/containers/guiPanel.h"
#endif
#ifndef _PLATFORMINPUT_H_
#include "platform/platformInput.h"
#endif
/// @addtogroup gui_container_group Containers
///
/// @ingroup gui_group Gui System
/// @{
class GuiSplitContainer : public GuiContainer
{
typedef GuiContainer Parent;
public:
enum Orientation
{
Vertical = 0,
Horizontal = 1
};
enum FixedPanel
{
None = 0,
FirstPanel = 1,
SecondPanel
};
GuiSplitContainer();
DECLARE_CONOBJECT( GuiSplitContainer );
DECLARE_DESCRIPTION( "A container that splits its area between two child controls.\n"
"The split ratio can be dynamically adjusted with a handle control between the two children.\n"
"Splitting can be either horizontal or vertical." );
// ConsoleObject
static void initPersistFields();
virtual bool onAdd();
// GuiControl
virtual bool onWake();
virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect);
virtual bool resize( const Point2I &newPosition, const Point2I &newExtent );
virtual void onRender(Point2I offset, const RectI &updateRect);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual bool layoutControls( RectI &clientRect );
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
virtual inline Point2I getSplitPoint() { return mSplitPoint; };
/// The Splitters entire Client Rectangle, this takes into account padding of this control
virtual inline RectI getSplitRect() { return mSplitRect; };
virtual void solvePanelConstraints( Point2I newDragPos, GuiContainer * firstPanel, GuiContainer * secondPanel, RectI clientRect );
virtual Point2I getMinExtent() const;
protected:
S32 mFixedPanel;
S32 mFixedPanelSize;
S32 mOrientation;
S32 mSplitterSize;
Point2I mSplitPoint;
RectI mSplitRect;
bool mDragging;
};
typedef GuiSplitContainer::Orientation GuiSplitOrientation;
typedef GuiSplitContainer::FixedPanel GuiSplitFixedPanel;
DefineEnumType( GuiSplitOrientation );
DefineEnumType( GuiSplitFixedPanel );
/// @}
#endif // _GUI_SPLTCONTAINER_H_

View file

@ -0,0 +1,388 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/engineAPI.h"
#include "gui/containers/guiStackCtrl.h"
IMPLEMENT_CONOBJECT(GuiStackControl);
ConsoleDocClass( GuiStackControl,
"@brief A container that stacks its children horizontally or vertically.\n\n"
"This container maintains a horizontal or vertical stack of GUI controls. If "
"one is added, deleted, or resized, then the stack is resized to fit. The "
"order of the stack is determined by the internal order of the children (ie. "
"the order of addition).<br>"
"@tsexample\n"
"new GuiStackControl()\n"
"{\n"
" stackingType = \"Dynamic\";\n"
" horizStacking = \"Left to Right\";\n"
" vertStacking = \"Top to Bottom\";\n"
" padding = \"4\";\n"
" dynamicSize = \"1\";\n"
" dynamicNonStackExtent = \"0\";\n"
" dynamicPos = \"0\";\n"
" changeChildSizeToFit = \"1\";\n"
" changeChildPosition = \"1\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
ImplementEnumType( GuiStackingType,
"Stacking method used to position child controls.\n\n"
"@ingroup GuiContainers" )
{ GuiStackControl::stackingTypeVert, "Vertical", "Stack children vertically by setting their Y position" },
{ GuiStackControl::stackingTypeHoriz,"Horizontal", "Stack children horizontall by setting their X position" },
{ GuiStackControl::stackingTypeDyn,"Dynamic", "Automatically switch between "
"Vertical and Horizontal stacking. Vertical stacking is chosen when the "
"stack control is taller than it is wide, horizontal stacking is chosen "
"when the stack control is wider than it is tall." }
EndImplementEnumType;
ImplementEnumType( GuiHorizontalStackingType,
"Determines how child controls are stacked horizontally.\n\n"
"@ingroup GuiContainers" )
{ GuiStackControl::horizStackLeft, "Left to Right", "Child controls are positioned in order from left to right (left-most control is first)" },
{ GuiStackControl::horizStackRight,"Right to Left", "Child controls are positioned in order from right to left (right-most control is first)" }
EndImplementEnumType;
ImplementEnumType( GuiVerticalStackingType,
"Determines how child controls are stacked vertically.\n\n"
"@ingroup GuiContainers" )
{ GuiStackControl::vertStackTop, "Top to Bottom", "Child controls are positioned in order from top to bottom (top-most control is first)" },
{ GuiStackControl::vertStackBottom,"Bottom to Top", "Child controls are positioned in order from bottom to top (bottom-most control is first)" }
EndImplementEnumType;
GuiStackControl::GuiStackControl()
{
setMinExtent(Point2I(16,16));
mResizing = false;
mStackingType = stackingTypeVert;
mStackVertSizing = vertStackTop;
mStackHorizSizing = horizStackLeft;
mPadding = 0;
mIsContainer = true;
mDynamicSize = true;
mDynamicNonStackExtent = false;
mDynamicPos = false;
mChangeChildSizeToFit = true;
mChangeChildPosition = true;
}
void GuiStackControl::initPersistFields()
{
addGroup( "Stacking" );
addField( "stackingType", TYPEID< StackingType >(), Offset(mStackingType, GuiStackControl),
"Determines the method used to position the child controls.\n\n" );
addField( "horizStacking", TYPEID< HorizontalType >(), Offset(mStackHorizSizing, GuiStackControl),
"Controls the type of horizontal stacking to use (<i>Left to Right</i> or "
"<i>Right to Left</i>)" );
addField( "vertStacking", TYPEID< VerticalType >(), Offset(mStackVertSizing, GuiStackControl),
"Controls the type of vertical stacking to use (<i>Top to Bottom</i> or "
"<i>Bottom to Top</i>)" );
addField( "padding", TypeS32, Offset(mPadding, GuiStackControl),
"Distance (in pixels) between stacked child controls." );
addField( "dynamicSize", TypeBool, Offset(mDynamicSize, GuiStackControl),
"Determines whether to resize the stack control along the stack axis (change "
"width for horizontal stacking, change height for vertical stacking).\n\n"
"If true, the stack width/height will be resized to the sum of the child control widths/heights. "
"If false, the stack will not be resized." );
addField( "dynamicNonStackExtent", TypeBool, Offset(mDynamicNonStackExtent, GuiStackControl),
"Determines whether to resize the stack control along the non-stack axis (change "
"height for horizontal stacking, change width for vertical stacking). No effect "
"if dynamicSize is false.\n\n"
"If true, the stack will be resized to the maximum of the child control widths/heights. "
"If false, the stack will not be resized." );
addField( "dynamicPos", TypeBool, Offset(mDynamicPos, GuiStackControl),
"Determines whether to reposition the stack along the stack axis when it is "
"auto-resized. No effect if dynamicSize is false.\n\n"
"If true, the stack will grow left for horizontal stacking, and grow up for vertical stacking.\n"
"If false, the stack will grow right for horizontal stacking, and grow down for vertical stacking.\n" );
addField( "changeChildSizeToFit", TypeBool, Offset(mChangeChildSizeToFit, GuiStackControl),
"Determines whether to resize child controls.\n\n"
"If true, horizontally stacked children keep their width, but have their "
"height set to the stack control height. Vertically stacked children keep "
"their height, but have their width set to the stack control width. If "
"false, child controls are not resized." );
addField( "changeChildPosition", TypeBool, Offset(mChangeChildPosition, GuiStackControl),
"Determines whether to reposition child controls.\n\n"
"If true, horizontally stacked children are aligned along the top edge of "
"the stack control. Vertically stacked children are aligned along the left "
"edge of the stack control. If false, horizontally stacked children retain "
"their Y position, and vertically stacked children retain their X position." );
endGroup( "Stacking" );
Parent::initPersistFields();
}
DefineEngineMethod( GuiStackControl, isFrozen, bool, (),,
"Return whether or not this control is frozen" )
{
return object->isFrozen();
}
DefineEngineMethod( GuiStackControl, freeze, void, ( bool freeze ),,
"Prevents control from restacking - useful when adding or removing child controls\n"
"@param freeze True to freeze the control, false to unfreeze it\n\n"
"@tsexample\n"
"%stackCtrl.freeze(true);\n"
"// add controls to stack\n"
"%stackCtrl.freeze(false);\n"
"@endtsexample\n" )
{
object->freeze( freeze );
}
DefineEngineMethod( GuiStackControl, updateStack, void, (),,
"Restack the child controls.\n" )
{
object->updatePanes();
}
bool GuiStackControl::onWake()
{
if ( !Parent::onWake() )
return false;
updatePanes();
return true;
}
void GuiStackControl::onSleep()
{
Parent::onSleep();
}
void GuiStackControl::updatePanes()
{
// Prevent recursion
if(mResizing)
return;
// Set Resizing.
mResizing = true;
Point2I extent = getExtent();
// Do we need to stack horizontally?
if( ( extent.x > extent.y && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeHoriz )
{
stackHorizontal( mStackHorizSizing == horizStackLeft );
}
// Or, vertically?
else if( ( extent.y > extent.x && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeVert)
{
stackVertical( mStackVertSizing == vertStackTop );
}
// Clear Sizing Flag.
mResizing = false;
}
void GuiStackControl::freeze(bool _shouldfreeze)
{
mResizing = _shouldfreeze;
}
void GuiStackControl::stackVertical(bool fromTop)
{
if( empty() )
return;
S32 begin, end, step;
if ( fromTop )
{
// Stack from Child0 at top to ChildN at bottom
begin = 0;
end = size();
step = 1;
}
else
{
// Stack from ChildN at top to Child0 at bottom
begin = size()-1;
end = -1;
step = -1;
}
// Place each visible child control
S32 maxWidth = 0;
Point2I curPos(0, 0);
for ( S32 i = begin; i != end; i += step )
{
GuiControl * gc = dynamic_cast<GuiControl*>( at(i) );
if ( gc && gc->isVisible() )
{
// Add padding between controls
if ( curPos.y > 0 )
curPos.y += mPadding;
Point2I childPos = curPos;
if ( !mChangeChildPosition )
childPos.x = gc->getLeft();
Point2I childSize( gc->getExtent() );
if ( mChangeChildSizeToFit )
childSize.x = getWidth();
gc->resize( childPos, childSize );
curPos.y += gc->getHeight();
maxWidth = getMax( maxWidth, childPos.x + childSize.x );
}
}
if ( mDynamicSize )
{
// Conform our size to the sum of the child sizes.
Point2I newPos( getPosition() );
Point2I newSize( mDynamicNonStackExtent ? maxWidth : getWidth(), curPos.y );
newSize.setMax( getMinExtent() );
// Grow the stack up instead of down?
if ( mDynamicPos )
newPos.y -= ( newSize.y - getHeight() );
resize( newPos, newSize );
}
}
void GuiStackControl::stackHorizontal(bool fromLeft)
{
if( empty() )
return;
S32 begin, end, step;
if ( fromLeft )
{
// Stack from Child0 at left to ChildN at right
begin = 0;
end = size();
step = 1;
}
else
{
// Stack from ChildN at left to Child0 at right
begin = size()-1;
end = -1;
step = -1;
}
// Place each visible child control
S32 maxHeight = 0;
Point2I curPos(0, 0);
for ( S32 i = begin; i != end; i += step )
{
GuiControl * gc = dynamic_cast<GuiControl*>( at(i) );
if ( gc && gc->isVisible() )
{
// Add padding between controls
if ( curPos.x > 0 )
curPos.x += mPadding;
Point2I childPos = curPos;
if ( !mChangeChildPosition )
childPos.y = gc->getTop();
Point2I childSize( gc->getExtent() );
if ( mChangeChildSizeToFit )
childSize.y = getHeight();
gc->resize( childPos, childSize );
curPos.x += gc->getWidth();
maxHeight = getMax( maxHeight, childPos.y + childSize.y );
}
}
if ( mDynamicSize )
{
// Conform our size to the sum of the child sizes.
Point2I newPos( getPosition() );
Point2I newSize( curPos.x, mDynamicNonStackExtent ? maxHeight : getHeight() );
newSize.setMax( getMinExtent() );
// Grow the stack left instead of right?
if ( mDynamicPos )
newPos.x -= ( newSize.x - getWidth() );
resize( newPos, newSize );
}
}
bool GuiStackControl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
if( !Parent::resize( newPosition, newExtent ) )
return false;
updatePanes();
// CodeReview This logic should be updated to correctly return true/false
// based on whether it sized it's children. [7/1/2007 justind]
return true;
}
void GuiStackControl::addObject(SimObject *obj)
{
Parent::addObject(obj);
updatePanes();
}
void GuiStackControl::removeObject(SimObject *obj)
{
Parent::removeObject(obj);
updatePanes();
}
bool GuiStackControl::reOrder(SimObject* obj, SimObject* target)
{
bool ret = Parent::reOrder(obj, target);
if (ret)
updatePanes();
return ret;
}
void GuiStackControl::childResized(GuiControl *child)
{
updatePanes();
}

View file

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUISTACKCTRL_H_
#define _GUISTACKCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#include "gfx/gfxDevice.h"
#include "console/console.h"
#include "console/consoleTypes.h"
/// A stack of GUI controls.
///
/// This maintains a horizontal or vertical stack of GUI controls. If one is deleted, or
/// resized, then the stack is resized to fit. The order of the stack is
/// determined by the internal order of the children (ie, order of addition).
class GuiStackControl : public GuiControl
{
protected:
typedef GuiControl Parent;
bool mResizing;
S32 mPadding;
S32 mStackHorizSizing; ///< Set from horizSizingOptions.
S32 mStackVertSizing; ///< Set from vertSizingOptions.
S32 mStackingType;
bool mDynamicSize; ///< Resize this control along the stack axis to fit the summed extent of the children (width or height depends on the stack type)
bool mDynamicNonStackExtent; ///< Resize this control along the non-stack axis to fit the max extent of the children (width or height depends on the stack type)
bool mDynamicPos; ///< Reposition this control along the stack axis when it is resized (by mDynamicSize) (left or up depends on the stack type)
bool mChangeChildSizeToFit; ///< Does the child resize to fit i.e. should a horizontal stack resize its children's height to fit?
bool mChangeChildPosition; ///< Do we reset the child's position in the opposite direction we are stacking?
public:
GuiStackControl();
enum StackingType
{
stackingTypeVert, ///< Always stack vertically
stackingTypeHoriz, ///< Always stack horizontally
stackingTypeDyn ///< Dynamically switch based on width/height
};
enum HorizontalType
{
horizStackLeft = 0,///< Stack from left to right when horizontal
horizStackRight, ///< Stack from right to left when horizontal
};
enum VerticalType
{
vertStackTop, ///< Stack from top to bottom when vertical
vertStackBottom, ///< Stack from bottom to top when vertical
};
bool resize(const Point2I &newPosition, const Point2I &newExtent);
void childResized(GuiControl *child);
bool isFrozen() { return mResizing; };
/// prevent resizing. useful when adding many items.
void freeze(bool);
bool onWake();
void onSleep();
void updatePanes();
virtual void stackVertical(bool fromTop);
virtual void stackHorizontal(bool fromLeft);
S32 getCount() { return size(); }; /// Returns the number of children in the stack
void addObject(SimObject *obj);
void removeObject(SimObject *obj);
bool reOrder(SimObject* obj, SimObject* target = 0);
static void initPersistFields();
DECLARE_CONOBJECT(GuiStackControl);
DECLARE_CATEGORY( "Gui Containers" );
DECLARE_DESCRIPTION( "A container that stacks its children horizontally or vertically." );
};
typedef GuiStackControl::StackingType GuiStackingType;
typedef GuiStackControl::HorizontalType GuiHorizontalStackingType;
typedef GuiStackControl::VerticalType GuiVerticalStackingType;
DefineEnumType( GuiStackingType );
DefineEnumType( GuiHorizontalStackingType );
DefineEnumType( GuiVerticalStackingType );
#endif

View file

@ -0,0 +1,982 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiTabBookCtrl.h"
#include "console/engineAPI.h"
#include "gui/core/guiCanvas.h"
#include "gui/editor/guiEditCtrl.h"
#include "gui/controls/guiPopUpCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT( GuiTabBookCtrl );
ConsoleDocClass( GuiTabBookCtrl,
"@brief A container \n\n"
"@tsexample\n"
"// Create \n"
"@endtsexample\n\n"
"@note Only GuiTabPageCtrls must be added to GuiTabBookCtrls. If an object of a different "
"class is added to the control, it will be reassigned to either the active page or the "
"tab book's parent.\n\n"
"@see GuiTabPageCtrl\n"
"@ingroup GuiContainers"
);
ImplementEnumType( GuiTabPosition,
"Where the control should put the tab headers for selecting individual pages.\n\n"
"@ingroup GuiContainers" )
{ GuiTabBookCtrl::AlignTop, "Top", "Tab headers on top edge." },
{ GuiTabBookCtrl::AlignBottom,"Bottom", "Tab headers on bottom edge." }
EndImplementEnumType;
IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U32 index ), ( text, index ),
"Called when a new tab page is selected.\n\n"
"@param text Text of the page header for the tab that is being selected.\n"
"@param index Index of the tab page being selected." );
IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ),
"Called when the user right-clicks on a tab page header.\n\n"
"@param text Text of the page header for the tab that is being selected.\n"
"@param index Index of the tab page being selected." );
//-----------------------------------------------------------------------------
GuiTabBookCtrl::GuiTabBookCtrl()
{
VECTOR_SET_ASSOCIATION( mPages );
mTabHeight = 24;
mTabPosition = AlignTop;
mActivePage = NULL;
mHoverTab = NULL;
mHasTexture = false;
mBitmapBounds = NULL;
setExtent( 400, 300 );
mPageRect = RectI(0,0,0,0);
mTabRect = RectI(0,0,0,0);
mFrontTabPadding = 0;
mPages.reserve(12);
mTabMargin = 7;
mMinTabWidth = 64;
mIsContainer = true;
mSelectedPageNum = -1;
mDefaultPageNum = -1;
mAllowReorder = false;
mDraggingTab = false;
mDraggingTabRect = false;
mIsFirstWake = true;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::initPersistFields()
{
addGroup( "TabBook" );
addField( "tabPosition", TYPEID< TabPosition >(), Offset( mTabPosition, GuiTabBookCtrl ),
"Where to place the tab page headers." );
addField( "tabMargin", TypeS32, Offset( mTabMargin, GuiTabBookCtrl ),
"Spacing to put between individual tab page headers." );
addField( "minTabWidth", TypeS32, Offset( mMinTabWidth, GuiTabBookCtrl ),
"Minimum width allocated to a tab page header." );
addField( "tabHeight", TypeS32, Offset( mTabHeight, GuiTabBookCtrl ),
"Height of tab page headers." );
addField( "allowReorder", TypeBool, Offset( mAllowReorder, GuiTabBookCtrl ),
"Whether reordering tabs with the mouse is allowed." );
addField( "defaultPage", TypeS32, Offset( mDefaultPageNum, GuiTabBookCtrl ),
"Index of page to select on first onWake() call (-1 to disable)." );
addProtectedField( "selectedPage", TypeS32, Offset( mSelectedPageNum, GuiTabBookCtrl ),
&_setSelectedPage, &defaultProtectedGetFn,
"Index of currently selected page." );
addField( "frontTabPadding", TypeS32, Offset( mFrontTabPadding, GuiTabBookCtrl ),
"X offset of first tab page header." );
endGroup( "TabBook" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
{
for (S32 i = 0; i < mPages.size(); i++ )
{
GuiTabPageCtrl* tab = mPages[i].Page;
if( tab == child )
{
mPages.erase( i );
break;
}
}
// Calculate Page Information
calculatePageTabs();
// Active Index.
mSelectedPageNum = getMin( mSelectedPageNum, mPages.size() - 1 );
if ( mSelectedPageNum != -1 )
{
// Select Page.
selectPage( mSelectedPageNum );
}
}
//-----------------------------------------------------------------------------
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();
if( page->getFitBook() )
fitPage( page );
// Select this Page
selectPage( page );
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::reOrder(SimObject* obj, SimObject* target)
{
if ( !Parent::reOrder(obj, target) )
return false;
// Store the Selected Page.
GuiTabPageCtrl *selectedPage = NULL;
if ( mSelectedPageNum != -1 )
selectedPage = mPages[mSelectedPageNum].Page;
// Determine the Target Page Index.
S32 targetIndex = -1;
for( S32 i = 0; i < mPages.size(); i++ )
{
const TabHeaderInfo &info = mPages[i];
if ( info.Page == target )
{
targetIndex = i;
break;
}
}
if ( targetIndex == -1 )
{
return false;
}
for( S32 i = 0; i < mPages.size(); i++ )
{
const TabHeaderInfo &info = mPages[i];
if ( info.Page == obj )
{
// Store Info.
TabHeaderInfo objPage = info;
// Remove.
mPages.erase( i );
// Insert.
mPages.insert( targetIndex, objPage );
break;
}
}
// Update Tabs.
calculatePageTabs();
// Reselect Page.
selectPage( selectedPage );
return true;
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::acceptsAsChild( SimObject* object ) const
{
// Only accept tab pages.
return ( dynamic_cast< GuiTabPageCtrl* >( object ) != NULL );
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::onWake()
{
if (! Parent::onWake())
return false;
mHasTexture = mProfile->constructBitmapArray() > 0;
if( mHasTexture )
{
mBitmapBounds = mProfile->mBitmapArrayRects.address();
mTabHeight = mBitmapBounds[TabSelected].extent.y;
}
calculatePageTabs();
if( mIsFirstWake )
{
// Awaken all pages, visible or not. We need to do this so
// any pages that make use of a language table for their label
// are correctly initialized.
for ( U32 i = 0; i < mPages.size(); ++i)
{
if ( !mPages[i].Page->isAwake() )
{
mPages[i].Page->awaken();
}
}
if( mDefaultPageNum >= 0 && mDefaultPageNum < mPages.size() )
selectPage( mDefaultPageNum );
mIsFirstWake = false;
}
return true;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::addNewPage( const char* text )
{
GuiTabPageCtrl* page = new GuiTabPageCtrl();
if( text )
page->setText( text );
page->registerObject();
addObject( page );
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
bool result = Parent::resize( newPosition, newExtent );
calculatePageTabs();
return result;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::childResized(GuiControl *child)
{
Parent::childResized( child );
//child->resize( mPageRect.point, mPageRect.extent );
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
{
mDraggingTab = false;
mDraggingTabRect = false;
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl *tab = findHitTab( localMouse );
if( tab != NULL )
{
selectPage( tab );
mDraggingTab = mAllowReorder;
}
else
{
mDraggingTabRect = true;
}
}
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::onMouseUp(const GuiEvent &event)
{
Parent::onMouseUp( event );
mDraggingTab = false;
mDraggingTabRect = false;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::onMouseDragged(const GuiEvent &event)
{
Parent::onMouseDragged( event );
if ( !mDraggingTab )
return;
GuiTabPageCtrl *selectedPage = NULL;
if ( mSelectedPageNum != -1 )
selectedPage = mPages[mSelectedPageNum].Page;
if ( !selectedPage )
return;
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl *tab = findHitTab( localMouse );
if( tab != NULL && tab != selectedPage )
{
S32 targetIndex = -1;
for( S32 i = 0; i < mPages.size(); i++ )
{
if( mPages[i].Page == tab )
{
targetIndex = i;
break;
}
}
if ( targetIndex > mSelectedPageNum )
{
reOrder( tab, selectedPage );
}
else
{
reOrder( selectedPage, 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 )
{
Parent::onMouseLeave( 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;
}
}
#ifdef TORQUE_TOOLS
// 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 );
}
#endif
// Return whether we handled this or not.
return handled;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::onRightMouseUp( const GuiEvent& event )
{
Point2I localMouse = globalToLocalCoord( event.mousePoint );
if( mTabRect.pointInRect( localMouse ) )
{
GuiTabPageCtrl* tab = findHitTab( localMouse );
if( tab )
onTabRightClick_callback( tab->getText(), getPageNum( tab ) );
}
}
//-----------------------------------------------------------------------------
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;
GFX->getDrawUtil()->getBitmapModulation( &oldModulation );
// Wipe it out
GFX->getDrawUtil()->clearBitmapModulation();
Parent::onRender(offset, updateRect);
// Clip to tab area
RectI savedClipRect = GFX->getClipRect();
GFX->setClipRect( tabRect );
// Render our tabs
renderTabs( offset, tabRect );
// Restore Rect.
GFX->setClipRect( savedClipRect );
// Restore old modulation
GFX->getDrawUtil()->setBitmapModulation( oldModulation );
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::renderTabs( const Point2I &offset, const RectI &tabRect )
{
// If the tab size is zero, don't render tabs,
// assuming it's a tab-less book
if( mPages.empty() || mTabHeight <= 0 )
return;
for( S32 i = 0; i < mPages.size(); i++ )
{
const TabHeaderInfo &currentTabInfo = mPages[i];
RectI tabBounds = mPages[i].TabRect;
tabBounds.point += offset;
GuiTabPageCtrl *tab = mPages[i].Page;
if( tab != NULL )
renderTab( tabBounds, tab );
// If we're on the last tab, draw the nice end piece
if( i + 1 == mPages.size() )
{
Point2I tabEndPoint = Point2I(currentTabInfo.TabRect.point.x + currentTabInfo.TabRect.extent.x + offset.x, currentTabInfo.TabRect.point.y + offset.y);
Point2I tabEndExtent = Point2I((tabRect.point.x + tabRect.extent.x) - tabEndPoint.x, currentTabInfo.TabRect.extent.y);
RectI tabEndRect = RectI(tabEndPoint,tabEndExtent);
GFX->setClipRect( tabEndRect );
// As it turns out the last tab can be outside the viewport in which
// case trying to render causes a DX assert. Could be better if
// setClipRect returned a bool.
if ( GFX->getViewport().isValidRect() )
renderFixedBitmapBordersFilled( tabEndRect, TabEnds + 1, mProfile );
}
}
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab )
{
StringTableEntry text = tab->getText();
ColorI oldColor;
GFX->getDrawUtil()->getBitmapModulation( &oldColor );
// Is this a skinned control?
if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 )
{
S32 indexMultiplier = 1;
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
if ( mActivePage == tab )
indexMultiplier += TabSelected;
else if( mHoverTab == tab )
indexMultiplier += TabHover;
else
indexMultiplier += TabNormal;
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 )
GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColor);
else if( mHoverTab == tab )
GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorHL);
else
GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorNA);
}
GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor);
switch( mTabPosition )
{
case AlignTop:
case AlignBottom:
renderJustifiedText( tabRect.point, tabRect.extent, text);
break;
}
GFX->getDrawUtil()->setBitmapModulation( oldColor);
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::setUpdate()
{
Parent::setUpdate();
setUpdateRegion(Point2I(0,0), getExtent());
calculatePageTabs();
}
//-----------------------------------------------------------------------------
S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
{
if( !page )
return mMinTabWidth;
const char* text = page->getText();
if( !text || dStrlen(text) == 0 || mProfile->mFont == NULL )
return mMinTabWidth;
GFont *font = mProfile->mFont;
return font->getStrNWidth( text, dStrlen(text) );
}
//-----------------------------------------------------------------------------
const RectI GuiTabBookCtrl::getClientRect()
{
if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps )
return Parent::getClientRect();
return mPageRect;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::calculatePageTabs()
{
// Short Circuit.
//
// If the tab size is zero, don't render tabs,
// assuming it's a tab-less book
if( mPages.empty() || mTabHeight <= 0 )
{
mPageRect.point.x = 0;
mPageRect.point.y = 0;
mPageRect.extent.x = getWidth();
mPageRect.extent.y = getHeight();
return;
}
S32 currRow = 0;
S32 currColumn = 0;
S32 currX = mFrontTabPadding;
S32 maxWidth = 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 > getWidth() )
{
// 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 = getHeight() - ( ( 1 + info.TabRow ) * mTabHeight );
currX += tabWidth;
break;
};
}
currRow++;
currColumn++;
Point2I localPoint = getExtent();
// Calculate
switch( mTabPosition )
{
case AlignTop:
localPoint.y -= getTop();
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 = getHeight() - mTabRect.extent.y;
break;
case AlignBottom:
mTabRect.point.x = 0;
mTabRect.extent.x = localPoint.x;
mTabRect.extent.y = currRow * mTabHeight;
mTabRect.point.y = getHeight() - 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;
}
}
//-----------------------------------------------------------------------------
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 = getWidth() - 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;
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::selectPage( S32 index )
{
if( mPages.empty() || index < 0 )
return;
if( mPages.size() <= index )
index = mPages.size() - 1;
// Select the page
selectPage( mPages[ index ].Page );
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
{
// Return if already selected.
if( mSelectedPageNum >= 0 && mSelectedPageNum < mPages.size() && mPages[ mSelectedPageNum ].Page == page )
return;
mSelectedPageNum = -1;
Vector<TabHeaderInfo>::iterator i = mPages.begin();
for( S32 index = 0; i != mPages.end() ; i++, index++ )
{
GuiTabPageCtrl *tab = (*i).Page;
if( page == tab )
{
mActivePage = tab;
tab->setVisible( true );
mSelectedPageNum = index;
// Notify User
onTabSelected_callback( tab->getText(), index );
}
else
tab->setVisible( false );
}
setUpdateLayout( updateSelf );
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::_setSelectedPage( void *object, const char *index, const char *data )
{
GuiTabBookCtrl* book = reinterpret_cast< GuiTabBookCtrl* >( object );
book->selectPage( dAtoi( data ) );
return false;
}
//-----------------------------------------------------------------------------
bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
{
// Tab = Next Page
// Ctrl-Tab = Previous Page
if( 0 && event.keyCode == KEY_TAB )
{
if( event.modifier & SI_PRIMARY_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 );
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 );
return;
}
}
}
//-----------------------------------------------------------------------------
void GuiTabBookCtrl::fitPage( GuiTabPageCtrl* page )
{
page->resize( mPageRect.point, mPageRect.extent );
}
//-----------------------------------------------------------------------------
S32 GuiTabBookCtrl::getPageNum( GuiTabPageCtrl* page ) const
{
const U32 numPages = mPages.size();
for( U32 i = 0; i < numPages; ++ i )
if( mPages[ i ].Page == page )
return i;
return -1;
}
//=============================================================================
// API.
//=============================================================================
// MARK: ---- API ----
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiTabBookCtrl, addPage, void, ( const char* title ), ( "" ),
"Add a new tab page to the control.\n\n"
"@param title Title text for the tab page header." )
{
object->addNewPage( title );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiTabBookCtrl, selectPage, void, ( S32 index ),,
"Set the selected tab page.\n\n"
"@param index Index of the tab page." )
{
object->selectPage( index );
}
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiTabBookCtrl, getSelectedPage, S32, (),,
"Get the index of the currently selected tab page.\n\n"
"@return Index of the selected tab page or -1 if no tab page is selected." )
{
return object->getSelectedPageNum();
}

View file

@ -0,0 +1,274 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUI_TABBOOKCTRL_H_
#define _GUI_TABBOOKCTRL_H_
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
#ifndef _GUITABPAGECTRL_H_
#include "gui/controls/guiTabPageCtrl.h"
#endif
/// Tab Book Control for creation of tabbed dialogs
///
/// @see GUI for an overview of the Torque GUI system.
///
/// @section GuiTabBookCtrl_Intro Introduction
///
/// GuiTabBookCtrl is a container class that holds children of type GuiTabPageCtrl
///
/// GuiTabBookCtrl creates an easy to work with system for creating tabbed dialogs
/// allowing for creation of dialogs that store a lot of information in a small area
/// by separating the information into pages which are changeable by clicking their
/// page title on top or bottom of the control
///
/// tabs may be aligned to be on top or bottom of the book and are changeable while
/// the GUI editor is open for quick switching between pages allowing multi-page dialogs
/// to be edited quickly and easily.
///
/// The control may only contain children of type GuiTabPageCtrl.
/// If a control is added to the Book that is not of type GuiTabPageCtrl, it will be
/// removed and relocated to the currently active page of the control.
/// If there is no active page in the book, the child control will be relocated to the
/// parent of the book.
///
/// @ref GUI has an overview of the GUI system.
///
/// @nosubgrouping
class GuiTabBookCtrl : public GuiContainer
{
public:
typedef GuiContainer Parent;
enum TabPosition
{
AlignTop, ///< Align the tabs on the top of the tab book control
AlignBottom ///< Align the tabs on the bottom of the tab book control
};
struct TabHeaderInfo
{
GuiTabPageCtrl *Page;
S32 TextWidth;
S32 TabRow;
S32 TabColumn;
RectI TabRect;
};
private:
static bool _setSelectedPage( void *object, const char *index, const char *data );
protected:
RectI mPageRect; ///< Rectangle of the tab page portion of the control
RectI mTabRect; ///< Rectangle of the tab portion of the control
Vector<TabHeaderInfo> mPages; ///< Vector of pages contained by the control
GuiTabPageCtrl* mActivePage; ///< Pointer to the active (selected) tab page child control
GuiTabPageCtrl* mHoverTab; ///< Pointer to the tab page that currently has the mouse positioned ontop of its tab
S32 mMinTabWidth; ///< Minimum Width a tab will display as.
TabPosition mTabPosition; ///< Current tab position (see alignment)
S32 mTabHeight; ///< Current tab height
S32 mTabMargin; ///< Margin left/right of tab text in tab
S32 mSelectedPageNum; ///< Current selected tab position
S32 mDefaultPageNum; ///< Page to select on first wake.
bool mAllowReorder; ///< Allow the user to reorder tabs by dragging them
S32 mFrontTabPadding; ///< Padding to the Left of the first Tab
bool mDraggingTab;
bool mDraggingTabRect;
bool mIsFirstWake;
enum
{
TabSelected = 0, ///< Index of selected tab texture
TabNormal, ///< Index of normal tab texture
TabHover, ///< Index of hover tab texture
TabEnds, ///< Index of end lines for horizontal tabs
NumBitmaps ///< Number of bitmaps in this array
};
bool mHasTexture; ///< Indicates whether we have a texture to render the tabs with
RectI *mBitmapBounds;///< Array of rectangles identifying textures for tab book
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onTabSelected, ( const String& text, U32 index ) );
DECLARE_CALLBACK( void, onTabRightClick, ( const String& text, U32 index ) );
/// @}
public:
GuiTabBookCtrl();
DECLARE_CONOBJECT(GuiTabBookCtrl);
DECLARE_DESCRIPTION( "A control that allows to select from a set of tabbed pages." );
static void initPersistFields();
/// @name Control Events
/// @{
bool onWake();
void onRender( Point2I offset, const RectI &updateRect );
/// @}
/// @name Child events
/// @{
void onChildRemoved( GuiControl* child );
void onChildAdded( GuiControl *child );
bool reOrder(SimObject* obj, SimObject* target);
bool acceptsAsChild( SimObject* object ) const;
/// @}
/// @name Rendering methods
/// @{
/// Tab rendering routine, iterates through all tabs rendering one at a time
/// @param offset the render offset to accomodate global coords
/// @param tabRect the Rectangle in which these tabs are to be rendered
void renderTabs( const Point2I &offset, const RectI &tabRect );
/// Tab rendering subroutine, renders one tab with specified options
/// @param tabRect the rectangle to render the tab into
/// @param tab pointer to the tab page control for which to render the tab
void renderTab( RectI tabRect, GuiTabPageCtrl* tab );
/// @}
/// @name Page Management
/// @{
/// Create a new tab page child in the book
///
/// Pages created are not titled and appear with no text on their tab when created.
/// This may change in the future.
///
/// @param text Tab page header text.
void addNewPage( const char* text = NULL );
/// Select a tab page based on an index
/// @param index The index in the list that specifies which page to select
void selectPage( S32 index );
/// Select a tab page by a pointer to that page
/// @param page A pointer to the GuiTabPageCtrl to select. This page must be a child of the tab book.
void selectPage( GuiTabPageCtrl *page );
/// Get the current selected tab number
S32 getSelectedPageNum(){ return mSelectedPageNum; };
/// Select the Next page in the tab book
void selectNextPage();
/// Select the Previous page in the tab book
void selectPrevPage();
/// Make the page fill the entire page space.
void fitPage( GuiTabPageCtrl* page );
/// Return the index for the given page. -1 if not a page in this book.
S32 getPageNum( GuiTabPageCtrl* page ) const;
/// @}
/// @name Internal Utility Functions
/// @{
/// Update ourselves by hooking common GuiControl functionality.
void setUpdate();
/// Balance a top/bottom tab row
void balanceRow( S32 row, S32 totalTabWidth );
/// Balance a left/right tab column
void balanceColumn( S32 row, S32 totalTabWidth );
/// Calculate the tab width of a page, given it's caption
S32 calculatePageTabWidth( GuiTabPageCtrl *page );
/// Calculate Page Header Information
void calculatePageTabs();
/// Get client area of tab book
virtual const RectI getClientRect();
/// Find the tab that was hit by the current event, if any
/// @param event The GuiEvent that caused this function call
GuiTabPageCtrl *findHitTab( const GuiEvent &event );
/// Find the tab that was hit, based on a point
/// @param hitPoint A Point2I that specifies where to search for a tab hit
GuiTabPageCtrl *findHitTab( Point2I hitPoint );
/// @}
/// @name Sizing
/// @{
/// Rezize our control
/// This method is overridden so that we may handle resizing of our child tab
/// pages when we are resized.
/// This ensures we keep our sizing in sync when we are moved or sized.
///
/// @param newPosition The new position of the control
/// @param newExtent The new extent of the control
bool resize(const Point2I &newPosition, const Point2I &newExtent);
/// Called when a child page is resized
/// This method is overridden so that we may handle resizing of our child tab
/// pages when one of them is resized.
/// This ensures we keep our sizing in sync when we our children are sized or moved.
///
/// @param child A pointer to the child control that has been resized
void childResized(GuiControl *child);
/// @}
virtual bool onKeyDown(const GuiEvent &event);
/// @name Mouse Events
/// @{
virtual void onMouseDown( const GuiEvent &event );
virtual void onMouseUp( const GuiEvent &event );
virtual void onMouseDragged( const GuiEvent &event );
virtual void onMouseMove( const GuiEvent &event );
virtual void onMouseLeave( const GuiEvent &event );
virtual bool onMouseDownEditor( const GuiEvent &event, Point2I offset );
virtual void onRightMouseUp( const GuiEvent& event );
/// @}
};
typedef GuiTabBookCtrl::TabPosition GuiTabPosition;
DefineEnumType( GuiTabPosition );
#endif //_GUI_TABBOOKCTRL_H_

View file

@ -0,0 +1,38 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "gui/containers/guiWindowCollapseCtrl.h"
IMPLEMENT_CONOBJECT( GuiWindowCollapseCtrl );
ConsoleDocClass( GuiWindowCollapseCtrl,
"@deprecated Use GuiWindowCtrl with GuiWindowCtrl::canCollapse = true.\n\n"
"@internal"
);
//-----------------------------------------------------------------------------
GuiWindowCollapseCtrl::GuiWindowCollapseCtrl()
{
mCanCollapse = true;
}

View file

@ -0,0 +1,52 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIWINDOWCOLLAPSECTRL_H_
#define _GUIWINDOWCOLLAPSECTRL_H_
#ifndef _GUIWINDOWCTRL_H_
#include "gui/containers/guiWindowCtrl.h"
#endif
/// @addtogroup gui_container_group Containers
///
/// Legacy control. Use GuiWindowCtrl with canCollapse=true.
///
/// @deprecated Functionality moved into GuiWindowCtrl.
///
/// @ingroup gui_group Gui System
/// @{
class GuiWindowCollapseCtrl : public GuiWindowCtrl
{
public:
typedef GuiWindowCtrl Parent;
GuiWindowCollapseCtrl();
DECLARE_CONOBJECT(GuiWindowCollapseCtrl);
DECLARE_DESCRIPTION( "Legacy control. Use GuiWindowCtrl with canCollapse=true." );
};
/// @}
#endif //_GUI_WINDOWCOLLAPSE_CTRL_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,287 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIWINDOWCTRL_H_
#define _GUIWINDOWCTRL_H_
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
/// @addtogroup gui_container_group Containers
///
/// @ingroup gui_group Gui System
/// @{
class GuiWindowCtrl : public GuiContainer
{
public:
typedef GuiContainer Parent;
protected:
enum
{
/// Pixel distance across which snapping takes effect.
SnapDistance = 12
};
/// Base indices for the button bitmap rects. Each button
/// bitmap is defined in each of the BitmapStates.
enum BitmapIndices
{
BmpClose,
BmpMaximize,
BmpNormal,
BmpMinimize,
BmpCount
};
/// Button bitmap states.
enum BitmapStates
{
BmpDefault = 0,
BmpHilite,
BmpDown,
BmpStates
};
/// Indices for non-button bitmap rects.
enum
{
BorderTopLeftKey = 12,
BorderTopRightKey,
BorderTopKey,
BorderTopLeftNoKey,
BorderTopRightNoKey,
BorderTopNoKey,
BorderLeft,
BorderRight,
BorderBottomLeft,
BorderBottom,
BorderBottomRight,
NumBitmaps
};
/// Window Edge Bit Masks
///
/// Edges can be combined to create a mask of multiple edges.
/// This is used for hit detection throughout this class.
enum Edges
{
edgeNone = 0, ///< No Edge
edgeTop = BIT(1), ///< Top Edge
edgeLeft = BIT(2), ///< Left Edge
edgeRight = BIT(3), ///< Right Edge
edgeBottom = BIT(4) ///< Bottom Edge
};
/// @name Flags
/// @{
/// Allow resizing width of window.
bool mResizeWidth;
/// Allow resizing height of window.
bool mResizeHeight;
/// Allow moving window.
bool mCanMove;
/// Display close button.
bool mCanClose;
/// Display minimize button.
bool mCanMinimize;
/// Display maximize button.
bool mCanMaximize;
///
bool mCanCollapse;
bool mCanDock; ///< Show a docking button on the title bar?
bool mEdgeSnap; ///< Should this window snap to other windows edges?
/// @}
bool mCloseButtonPressed;
bool mMinimizeButtonPressed;
bool mMaximizeButtonPressed;
bool mRepositionWindow;
bool mResizeWindow;
bool mSnapSignal;
StringTableEntry mCloseCommand;
/// Window title string.
String mText;
S32 mResizeEdge; ///< Resizing Edges Mask (See Edges Enumeration)
S32 mTitleHeight;
F32 mResizeMargin;
bool mMouseMovingWin;
bool mMouseResizeWidth;
bool mMouseResizeHeight;
bool mMinimized;
bool mMaximized;
Point2I mMousePosition;
Point2I mMouseDownPosition;
RectI mOrigBounds;
RectI mStandardBounds;
RectI mCloseButton;
RectI mMinimizeButton;
RectI mMaximizeButton;
S32 mMinimizeIndex;
S32 mTabIndex;
void positionButtons(void);
RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
GFXTexHandle mTextureObject;
/// @name Collapsing
/// @{
typedef Vector< GuiWindowCtrl *> CollapseGroupNumVec;
S32 mCollapseGroup;
S32 mCollapseGroupNum;
S32 mPreCollapsedYExtent;
S32 mPreCollapsedYMinExtent;
bool mIsCollapsed;
bool mIsMouseResizing;
S32 getCollapseGroupNum() { return mCollapseGroupNum; }
void moveFromCollapseGroup();
void moveWithCollapseGroup(Point2I windowPosition);
bool resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeWidth);
void refreshCollapseGroups();
void handleCollapseGroup();
/// @}
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onClose, () );
DECLARE_CALLBACK( void, onMinimize, () );
DECLARE_CALLBACK( void, onMaximize, () );
DECLARE_CALLBACK( void, onCollapse, () );
DECLARE_CALLBACK( void, onRestore, () );
/// @}
public:
GuiWindowCtrl();
bool isMinimized(S32 &index);
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
void setFont(S32 fntTag);
void setCloseCommand(const char *newCmd);
GuiControl* findHitControl (const Point2I &pt, S32 initialLayer = -1 );
S32 findHitEdges( const Point2I &globalPoint );
void getSnappableWindows( Vector<GuiWindowCtrl*> &windowOutVector, bool canCollapse = false );
bool resize( const Point2I &newPosition, const Point2I &newExtent );
//only cycle tabs through the current window, so overwrite the method
GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true);
GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true);
S32 getTabIndex(void) { return mTabIndex; }
void selectWindow(void);
////
const RectI getClientRect();
/// Mutators for window properties from code.
/// Using setDataField is a bit overkill.
void setMobility( bool canMove, bool canClose, bool canMinimize, bool canMaximize, bool canDock, bool edgeSnap )
{
mCanMove = canMove;
mCanClose = canClose;
mCanMinimize = canMinimize;
mCanMaximize = canMaximize;
mCanDock = canDock;
mEdgeSnap = edgeSnap;
}
/// Mutators for window properties from code.
/// Using setDataField is a bit overkill.
void setCanResize( bool canResizeWidth, bool canResizeHeight )
{
mResizeWidth = canResizeWidth;
mResizeHeight = canResizeHeight;
}
/// Set the text on the window title bar.
void setText( const String& text )
{
mText = text;
}
/// @name Collapsing
/// @{
void setCollapseGroup( bool state );
void toggleCollapseGroup();
void moveToCollapseGroup( GuiWindowCtrl* hitWindow, bool orientation );
/// @}
// GuiContainer.
virtual bool onWake();
virtual void onSleep();
virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseMove(const GuiEvent &event);
virtual bool onKeyDown(const GuiEvent &event);
virtual void onRender(Point2I offset, const RectI &updateRect);
DECLARE_CONOBJECT( GuiWindowCtrl );
DECLARE_DESCRIPTION( "A control that shows an independent window inside the canvas." );
static void initPersistFields();
};
/// @}
#endif //_GUI_WINDOW_CTRL_H