123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- //-----------------------------------------------------------------------------
- // 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 )
- {
- 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 >= minExtent.x && newRect.extent.y >= minExtent.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 );
- }
|