123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- //-----------------------------------------------------------------------------
- // 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->EmptyString();
- 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->getBitmapResource(),
- 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->getBitmapResource(),
- 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->getBitmapResource(),
- RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)),
- foo
- );
- }
- // And the end
- barOffset.x += barMiddleSize;
- GFX->getDrawUtil()->drawBitmapStretchSR(
- mProfile->getBitmapResource(),
- 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 );
- }
|