mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 07:04:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
358
Engine/source/gui/containers/guiAutoScrollCtrl.cpp
Normal file
358
Engine/source/gui/containers/guiAutoScrollCtrl.cpp
Normal 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();
|
||||
}
|
||||
162
Engine/source/gui/containers/guiAutoScrollCtrl.h
Normal file
162
Engine/source/gui/containers/guiAutoScrollCtrl.h
Normal 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
|
||||
439
Engine/source/gui/containers/guiContainer.cpp
Normal file
439
Engine/source/gui/containers/guiContainer.cpp
Normal 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 );
|
||||
}
|
||||
155
Engine/source/gui/containers/guiContainer.h
Normal file
155
Engine/source/gui/containers/guiContainer.h
Normal 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
|
||||
190
Engine/source/gui/containers/guiCtrlArrayCtrl.cpp
Normal file
190
Engine/source/gui/containers/guiCtrlArrayCtrl.cpp
Normal 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;
|
||||
}
|
||||
68
Engine/source/gui/containers/guiCtrlArrayCtrl.h
Normal file
68
Engine/source/gui/containers/guiCtrlArrayCtrl.h
Normal 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
|
||||
271
Engine/source/gui/containers/guiDragAndDropCtrl.cpp
Normal file
271
Engine/source/gui/containers/guiDragAndDropCtrl.cpp
Normal 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 ) );
|
||||
}
|
||||
86
Engine/source/gui/containers/guiDragAndDropCtrl.h
Normal file
86
Engine/source/gui/containers/guiDragAndDropCtrl.h
Normal 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
|
||||
303
Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.cpp
Normal file
303
Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.cpp
Normal 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();
|
||||
}
|
||||
80
Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.h
Normal file
80
Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.h
Normal 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_
|
||||
413
Engine/source/gui/containers/guiFormCtrl.cpp
Normal file
413
Engine/source/gui/containers/guiFormCtrl.cpp
Normal 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
|
||||
121
Engine/source/gui/containers/guiFormCtrl.h
Normal file
121
Engine/source/gui/containers/guiFormCtrl.h
Normal 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
|
||||
1153
Engine/source/gui/containers/guiFrameCtrl.cpp
Normal file
1153
Engine/source/gui/containers/guiFrameCtrl.cpp
Normal file
File diff suppressed because it is too large
Load diff
191
Engine/source/gui/containers/guiFrameCtrl.h
Normal file
191
Engine/source/gui/containers/guiFrameCtrl.h
Normal 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
|
||||
396
Engine/source/gui/containers/guiPaneCtrl.cpp
Normal file
396
Engine/source/gui/containers/guiPaneCtrl.cpp
Normal 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 );
|
||||
}
|
||||
120
Engine/source/gui/containers/guiPaneCtrl.h
Normal file
120
Engine/source/gui/containers/guiPaneCtrl.h
Normal 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
|
||||
113
Engine/source/gui/containers/guiPanel.cpp
Normal file
113
Engine/source/gui/containers/guiPanel.cpp
Normal 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 );
|
||||
}
|
||||
62
Engine/source/gui/containers/guiPanel.h
Normal file
62
Engine/source/gui/containers/guiPanel.h
Normal 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_
|
||||
650
Engine/source/gui/containers/guiRolloutCtrl.cpp
Normal file
650
Engine/source/gui/containers/guiRolloutCtrl.cpp
Normal 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();
|
||||
}
|
||||
179
Engine/source/gui/containers/guiRolloutCtrl.h
Normal file
179
Engine/source/gui/containers/guiRolloutCtrl.h
Normal 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_
|
||||
1410
Engine/source/gui/containers/guiScrollCtrl.cpp
Normal file
1410
Engine/source/gui/containers/guiScrollCtrl.cpp
Normal file
File diff suppressed because it is too large
Load diff
276
Engine/source/gui/containers/guiScrollCtrl.h
Normal file
276
Engine/source/gui/containers/guiScrollCtrl.h
Normal 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
|
||||
615
Engine/source/gui/containers/guiSplitContainer.cpp
Normal file
615
Engine/source/gui/containers/guiSplitContainer.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
110
Engine/source/gui/containers/guiSplitContainer.h
Normal file
110
Engine/source/gui/containers/guiSplitContainer.h
Normal 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_
|
||||
388
Engine/source/gui/containers/guiStackCtrl.cpp
Normal file
388
Engine/source/gui/containers/guiStackCtrl.cpp
Normal 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();
|
||||
}
|
||||
112
Engine/source/gui/containers/guiStackCtrl.h
Normal file
112
Engine/source/gui/containers/guiStackCtrl.h
Normal 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
|
||||
982
Engine/source/gui/containers/guiTabBookCtrl.cpp
Normal file
982
Engine/source/gui/containers/guiTabBookCtrl.cpp
Normal 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 ¤tTabInfo = 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();
|
||||
}
|
||||
274
Engine/source/gui/containers/guiTabBookCtrl.h
Normal file
274
Engine/source/gui/containers/guiTabBookCtrl.h
Normal 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_
|
||||
38
Engine/source/gui/containers/guiWindowCollapseCtrl.cpp
Normal file
38
Engine/source/gui/containers/guiWindowCollapseCtrl.cpp
Normal 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;
|
||||
}
|
||||
52
Engine/source/gui/containers/guiWindowCollapseCtrl.h
Normal file
52
Engine/source/gui/containers/guiWindowCollapseCtrl.h
Normal 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
|
||||
1932
Engine/source/gui/containers/guiWindowCtrl.cpp
Normal file
1932
Engine/source/gui/containers/guiWindowCtrl.cpp
Normal file
File diff suppressed because it is too large
Load diff
287
Engine/source/gui/containers/guiWindowCtrl.h
Normal file
287
Engine/source/gui/containers/guiWindowCtrl.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue