Engine directory for ticket #1

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

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// 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/console.h"
#include "gui/controls/guiBackgroundCtrl.h"
IMPLEMENT_CONOBJECT(GuiBackgroundCtrl);
ConsoleDocClass( GuiBackgroundCtrl,
"@brief Renders a background, so you can have a backdrop for your GUI.\n\n"
"Deprecated\n\n"
"@ingroup GuiImages\n"
"@internal"
);
//--------------------------------------------------------------------------
GuiBackgroundCtrl::GuiBackgroundCtrl() : GuiControl()
{
mDraw = false;
mIsContainer = true;
}
//--------------------------------------------------------------------------
void GuiBackgroundCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if ( mDraw )
Parent::onRender( offset, updateRect );
renderChildControls(offset, updateRect);
}

View file

@ -0,0 +1,48 @@
//-----------------------------------------------------------------------------
// 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 _GUIBACKGROUNDCTRL_H_
#define _GUIBACKGROUNDCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
/// Renders a background, so you can have a backdrop for your GUI.
class GuiBackgroundCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
bool mDraw;
//creation methods
DECLARE_CONOBJECT(GuiBackgroundCtrl);
DECLARE_CATEGORY( "Gui Containers" );
GuiBackgroundCtrl();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif

View file

@ -0,0 +1,197 @@
//-----------------------------------------------------------------------------
// 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/core/guiControl.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxDrawUtil.h"
// [rene 11/09/09] Why does this not use the bitmap array from its profile?
/// Renders a skinned border.
class GuiBitmapBorderCtrl : public GuiControl
{
typedef GuiControl Parent;
enum {
BorderTopLeft,
BorderTopRight,
BorderTop,
BorderLeft,
BorderRight,
BorderBottomLeft,
BorderBottom,
BorderBottomRight,
NumBitmaps
};
RectI *mBitmapBounds; ///< bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2]
GFXTexHandle mTextureObject;
public:
bool onWake();
void onSleep();
void onRender(Point2I offset, const RectI &updateRect);
DECLARE_CONOBJECT(GuiBitmapBorderCtrl);
DECLARE_CATEGORY( "Gui Images" );
DECLARE_DESCRIPTION( "A control that renders a skinned border." );
};
IMPLEMENT_CONOBJECT(GuiBitmapBorderCtrl);
ConsoleDocClass( GuiBitmapBorderCtrl,
"@brief A control that renders a skinned border specified in its profile.\n\n"
"This control uses the bitmap specified in it's profile (GuiControlProfile::bitmapName). It takes this image and breaks up aspects of it "
"to skin the border of this control with. It is also important to set GuiControlProfile::hasBitmapArray to true on the profile as well.\n\n"
"The bitmap referenced should be broken up into a 3 x 3 grid (using the top left color pixel as a border color between each of the images) "
"in which it will map to the following places:\n"
"1 = Top Left Corner\n"
"2 = Top Right Corner\n"
"3 = Top Center\n"
"4 = Left Center\n"
"5 = Right Center\n"
"6 = Bottom Left Corner\n"
"7 = Bottom Center\n"
"8 = Bottom Right Corner\n"
"0 = Nothing\n\n"
"1 2 3\n"
"4 5 0\n"
"6 7 8\n\n"
"@tsexample\n"
"singleton GuiControlProfile (BorderGUIProfile)\n"
"{\n"
" bitmap = \"core/art/gui/images/borderArray\";\n"
" hasBitmapArray = true;\n"
" opaque = false;\n"
"};\n\n"
"new GuiBitmapBorderCtrl(BitmapBorderGUI)\n"
"{\n"
" profile = \"BorderGUIProfile\";\n"
" position = \"0 0\";\n"
" extent = \"400 40\";\n"
" visible = \"1\";\n"
"};"
"@endtsexample\n\n"
"@see GuiControlProfile::bitmapName\n"
"@see GuiControlProfile::hasBitmapArray\n\n"
"@ingroup GuiImages"
);
bool GuiBitmapBorderCtrl::onWake()
{
if (! Parent::onWake())
return false;
//get the texture for the close, minimize, and maximize buttons
mBitmapBounds = NULL;
mTextureObject = mProfile->mTextureObject;
if( mProfile->constructBitmapArray() >= NumBitmaps )
mBitmapBounds = mProfile->mBitmapArrayRects.address();
else
Con::errorf( "GuiBitmapBorderCtrl: Could not construct bitmap array for profile '%s'", mProfile->getName() );
return true;
}
void GuiBitmapBorderCtrl::onSleep()
{
mTextureObject = NULL;
mBitmapBounds = NULL;
Parent::onSleep();
}
void GuiBitmapBorderCtrl::onRender(Point2I offset, const RectI &updateRect)
{
renderChildControls( offset, updateRect );
if( mBitmapBounds )
{
GFX->setClipRect(updateRect);
//draw the outline
RectI winRect;
winRect.point = offset;
winRect.extent = getExtent();
winRect.point.x += mBitmapBounds[BorderLeft].extent.x;
winRect.point.y += mBitmapBounds[BorderTop].extent.y;
winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x;
winRect.extent.y -= mBitmapBounds[BorderTop].extent.y + mBitmapBounds[BorderBottom].extent.y;
if(mProfile->mOpaque)
GFX->getDrawUtil()->drawRectFill(winRect, mProfile->mFillColor);
GFX->getDrawUtil()->clearBitmapModulation();
GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset, mBitmapBounds[BorderTopLeft]);
GFX->getDrawUtil()->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[BorderTopRight].extent.x, offset.y),
mBitmapBounds[BorderTopRight]);
RectI destRect;
destRect.point.x = offset.x + mBitmapBounds[BorderTopLeft].extent.x;
destRect.point.y = offset.y;
destRect.extent.x = getWidth() - mBitmapBounds[BorderTopLeft].extent.x - mBitmapBounds[BorderTopRight].extent.x;
destRect.extent.y = mBitmapBounds[BorderTop].extent.y;
RectI stretchRect = mBitmapBounds[BorderTop];
stretchRect.inset(1,0);
GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
destRect.point.x = offset.x;
destRect.point.y = offset.y + mBitmapBounds[BorderTopLeft].extent.y;
destRect.extent.x = mBitmapBounds[BorderLeft].extent.x;
destRect.extent.y = getHeight() - mBitmapBounds[BorderTopLeft].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
stretchRect = mBitmapBounds[BorderLeft];
stretchRect.inset(0,1);
GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x;
destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
destRect.point.y = offset.y + mBitmapBounds[BorderTopRight].extent.y;
destRect.extent.y = getHeight() - mBitmapBounds[BorderTopRight].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
stretchRect = mBitmapBounds[BorderRight];
stretchRect.inset(0,1);
GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y;
destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
stretchRect = mBitmapBounds[BorderBottom];
stretchRect.inset(1,0);
GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
}
}

View file

@ -0,0 +1,266 @@
//-----------------------------------------------------------------------------
// 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/controls/guiBitmapCtrl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT(GuiBitmapCtrl);
ConsoleDocClass( GuiBitmapCtrl,
"@brief A gui control that is used to display an image.\n\n"
"The image is stretched to the constraints of the control by default. However, the control can also\n"
"tile the image as well.\n\n"
"The image itself is stored inside the GuiBitmapCtrl::bitmap field. The boolean value that decides\n"
"whether the image is stretched or tiled is stored inside the GuiBitmapCtrl::wrap field.\n"
"@tsexample\n"
"// Create a tiling GuiBitmapCtrl that displays \"myImage.png\"\n"
"%bitmapCtrl = new GuiBitmapCtrl()\n"
"{\n"
" bitmap = \"myImage.png\";\n"
" wrap = \"true\";\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiControls"
);
GuiBitmapCtrl::GuiBitmapCtrl(void)
: mBitmapName(),
mStartPoint( 0, 0 ),
mWrap( false )
{
}
bool GuiBitmapCtrl::setBitmapName( void *object, const char *index, const char *data )
{
// Prior to this, you couldn't do bitmap.bitmap = "foo.jpg" and have it work.
// With protected console types you can now call the setBitmap function and
// make it load the image.
static_cast<GuiBitmapCtrl *>( object )->setBitmap( data );
// Return false because the setBitmap method will assign 'mBitmapName' to the
// argument we are specifying in the call.
return false;
}
void GuiBitmapCtrl::initPersistFields()
{
addGroup( "Bitmap" );
addProtectedField( "bitmap", TypeImageFilename, Offset( mBitmapName, GuiBitmapCtrl ),
&setBitmapName, &defaultProtectedGetFn,
"The bitmap file to display in the control." );
addField( "wrap", TypeBool, Offset( mWrap, GuiBitmapCtrl ),
"If true, the bitmap is tiled inside the control rather than stretched to fit." );
endGroup( "Bitmap" );
Parent::initPersistFields();
}
bool GuiBitmapCtrl::onWake()
{
if (! Parent::onWake())
return false;
setActive(true);
setBitmap(mBitmapName);
return true;
}
void GuiBitmapCtrl::onSleep()
{
if ( !mBitmapName.equal("texhandle", String::NoCase) )
mTextureObject = NULL;
Parent::onSleep();
}
//-------------------------------------
void GuiBitmapCtrl::inspectPostApply()
{
// if the extent is set to (0,0) in the gui editor and appy hit, this control will
// set it's extent to be exactly the size of the bitmap (if present)
Parent::inspectPostApply();
if (!mWrap && (getExtent().x == 0) && (getExtent().y == 0) && mTextureObject)
{
setExtent( mTextureObject->getWidth(), mTextureObject->getHeight());
}
}
void GuiBitmapCtrl::setBitmap( const char *name, bool resize )
{
mBitmapName = name;
if ( !isAwake() )
return;
if ( mBitmapName.isNotEmpty() )
{
if ( !mBitmapName.equal("texhandle", String::NoCase) )
mTextureObject.set( mBitmapName, &GFXDefaultGUIProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) );
// Resize the control to fit the bitmap
if ( mTextureObject && resize )
{
setExtent( mTextureObject->getWidth(), mTextureObject->getHeight() );
updateSizing();
}
}
else
mTextureObject = NULL;
setUpdate();
}
void GuiBitmapCtrl::updateSizing()
{
if(!getParent())
return;
// updates our bounds according to our horizSizing and verSizing rules
RectI fakeBounds( getPosition(), getParent()->getExtent());
parentResized( fakeBounds, fakeBounds);
}
void GuiBitmapCtrl::setBitmapHandle(GFXTexHandle handle, bool resize)
{
mTextureObject = handle;
mBitmapName = String("texhandle");
// Resize the control to fit the bitmap
if (resize)
{
setExtent(mTextureObject->getWidth(), mTextureObject->getHeight());
updateSizing();
}
}
void GuiBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if (mTextureObject)
{
GFX->getDrawUtil()->clearBitmapModulation();
if(mWrap)
{
// We manually draw each repeat because non power of two textures will
// not tile correctly when rendered with GFX->drawBitmapTile(). The non POT
// bitmap will be padded by the hardware, and we'll see lots of slack
// in the texture. So... lets do what we must: draw each repeat by itself:
GFXTextureObject* texture = mTextureObject;
RectI srcRegion;
RectI dstRegion;
float xdone = ((float)getExtent().x/(float)texture->mBitmapSize.x)+1;
float ydone = ((float)getExtent().y/(float)texture->mBitmapSize.y)+1;
int xshift = mStartPoint.x%texture->mBitmapSize.x;
int yshift = mStartPoint.y%texture->mBitmapSize.y;
for(int y = 0; y < ydone; ++y)
for(int x = 0; x < xdone; ++x)
{
srcRegion.set(0,0,texture->mBitmapSize.x,texture->mBitmapSize.y);
dstRegion.set( ((texture->mBitmapSize.x*x)+offset.x)-xshift,
((texture->mBitmapSize.y*y)+offset.y)-yshift,
texture->mBitmapSize.x,
texture->mBitmapSize.y);
GFX->getDrawUtil()->drawBitmapStretchSR(texture,dstRegion, srcRegion, GFXBitmapFlip_None, GFXTextureFilterLinear);
}
}
else
{
RectI rect(offset, getExtent());
GFX->getDrawUtil()->drawBitmapStretch(mTextureObject, rect, GFXBitmapFlip_None, GFXTextureFilterLinear, false);
}
}
if (mProfile->mBorder || !mTextureObject)
{
RectI rect(offset.x, offset.y, getExtent().x, getExtent().y);
GFX->getDrawUtil()->drawRect(rect, mProfile->mBorderColor);
}
renderChildControls(offset, updateRect);
}
void GuiBitmapCtrl::setValue(S32 x, S32 y)
{
if (mTextureObject)
{
x += mTextureObject->getWidth() / 2;
y += mTextureObject->getHeight() / 2;
}
while (x < 0)
x += 256;
mStartPoint.x = x % 256;
while (y < 0)
y += 256;
mStartPoint.y = y % 256;
}
DefineEngineMethod( GuiBitmapCtrl, setValue, void, ( S32 x, S32 y ),,
"Set the offset of the bitmap within the control.\n"
"@param x The x-axis offset of the image.\n"
"@param y The y-axis offset of the image.\n")
{
object->setValue(x, y);
}
//
static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap1(
"@brief Assign an image to the control.\n\n"
"Child controls with resize according to their layout settings.\n"
"@param filename The filename of the image.\n"
"@param resize Optional parameter. If true, the GUI will resize to fit the image.",
"GuiBitmapCtrl", // The class to place the method in; use NULL for functions.
"void setBitmap( String filename, bool resize );" ); // The definition string.
static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap2(
"@brief Assign an image to the control.\n\n"
"Child controls will resize according to their layout settings.\n"
"@param filename The filename of the image.\n"
"@param resize A boolean value that decides whether the ctrl refreshes or not.",
"GuiBitmapCtrl", // The class to place the method in; use NULL for functions.
"void setBitmap( String filename );" ); // The definition string.
//"Set the bitmap displayed in the control. Note that it is limited in size, to 256x256."
ConsoleMethod( GuiBitmapCtrl, setBitmap, void, 3, 4,
"( String filename | String filename, bool resize ) Assign an image to the control.\n\n"
"@hide" )
{
char filename[1024];
Con::expandScriptFilename(filename, sizeof(filename), argv[2]);
object->setBitmap(filename, argc > 3 ? dAtob( argv[3] ) : false );
}

View file

@ -0,0 +1,78 @@
//-----------------------------------------------------------------------------
// 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 _GUIBITMAPCTRL_H_
#define _GUIBITMAPCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
/// Renders a bitmap.
class GuiBitmapCtrl : public GuiControl
{
public:
typedef GuiControl Parent;
protected:
/// Name of the bitmap file. If this is 'texhandle' the bitmap is not loaded
/// from a file but rather set explicitly on the control.
String mBitmapName;
/// Loaded texture.
GFXTexHandle mTextureObject;
Point2I mStartPoint;
/// If true, bitmap tiles inside control. Otherwise stretches.
bool mWrap;
static bool setBitmapName( void *object, const char *index, const char *data );
static const char *getBitmapName( void *obj, const char *data );
public:
GuiBitmapCtrl();
static void initPersistFields();
void setBitmap(const char *name,bool resize = false);
void setBitmapHandle(GFXTexHandle handle, bool resize = false);
// GuiControl.
bool onWake();
void onSleep();
void inspectPostApply();
void updateSizing();
void onRender(Point2I offset, const RectI &updateRect);
void setValue(S32 x, S32 y);
DECLARE_CONOBJECT( GuiBitmapCtrl );
DECLARE_CATEGORY( "Gui Images" );
DECLARE_DESCRIPTION( "A control that displays a single, static image from a file.\n"
"The bitmap can either be tiled or stretched inside the control." );
};
#endif

View file

@ -0,0 +1,548 @@
//-----------------------------------------------------------------------------
// 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/console.h"
#include "gfx/gfxDevice.h"
#include "console/consoleTypes.h"
#include "gui/core/guiCanvas.h"
#include "gui/buttons/guiButtonCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiColorPicker.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
/// @name Common colors we use
/// @{
ColorF colorWhite(1.,1.,1.);
ColorF colorWhiteBlend(1.,1.,1.,.75);
ColorF colorBlack(.0,.0,.0);
ColorF colorAlpha(0.0f, 0.0f, 0.0f, 0.0f);
ColorF colorAlphaW(1.0f, 1.0f, 1.0f, 0.0f);
ColorI GuiColorPickerCtrl::mColorRange[7] = {
ColorI(255,0,0), // Red
ColorI(255,0,255), // Pink
ColorI(0,0,255), // Blue
ColorI(0,255,255), // Light blue
ColorI(0,255,0), // Green
ColorI(255,255,0), // Yellow
ColorI(255,0,0), // Red
};
/// @}
IMPLEMENT_CONOBJECT(GuiColorPickerCtrl);
ConsoleDocClass( GuiColorPickerCtrl,
"@brief Editor GUI used for picking a ColorF from a palette.\n\n"
"@note Editor use only.\n\n"
"@internal"
);
//--------------------------------------------------------------------------
GuiColorPickerCtrl::GuiColorPickerCtrl()
{
setExtent(140, 30);
mDisplayMode = pPallet;
mBaseColor = ColorF(1.,.0,1.);
mPickColor = ColorF(.0,.0,.0);
mSelectorPos = Point2I(0,0);
mMouseDown = mMouseOver = false;
mActive = true;
mPositionChanged = false;
mSelectorGap = 1;
mActionOnMove = false;
mShowReticle = true;
}
//--------------------------------------------------------------------------
ImplementEnumType( GuiColorPickMode,
"\n\n"
"@ingroup GuiUtil"
"@internal" )
{ GuiColorPickerCtrl::pPallet, "Pallete" },
{ GuiColorPickerCtrl::pHorizColorRange, "HorizColor"},
{ GuiColorPickerCtrl::pVertColorRange, "VertColor" },
{ GuiColorPickerCtrl::pHorizColorBrightnessRange, "HorizBrightnessColor"},
{ GuiColorPickerCtrl::pVertColorBrightnessRange, "VertBrightnessColor" },
{ GuiColorPickerCtrl::pBlendColorRange, "BlendColor"},
{ GuiColorPickerCtrl::pHorizAlphaRange, "HorizAlpha"},
{ GuiColorPickerCtrl::pVertAlphaRange, "VertAlpha" },
{ GuiColorPickerCtrl::pDropperBackground, "Dropper" },
EndImplementEnumType;
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::initPersistFields()
{
addGroup("ColorPicker");
addField("baseColor", TypeColorF, Offset(mBaseColor, GuiColorPickerCtrl));
addField("pickColor", TypeColorF, Offset(mPickColor, GuiColorPickerCtrl));
addField("selectorGap", TypeS32, Offset(mSelectorGap, GuiColorPickerCtrl));
addField("displayMode", TYPEID< PickMode >(), Offset(mDisplayMode, GuiColorPickerCtrl) );
addField("actionOnMove", TypeBool,Offset(mActionOnMove, GuiColorPickerCtrl));
addField("showReticle", TypeBool, Offset(mShowReticle, GuiColorPickerCtrl));
endGroup("ColorPicker");
Parent::initPersistFields();
}
//--------------------------------------------------------------------------
// Function to draw a box which can have 4 different colors in each corner blended together
void GuiColorPickerCtrl::drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4)
{
GFX->setStateBlock(mStateBlock);
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y;
//A couple of checks to determine if color blend
if(c1 == colorWhite && c3 == colorAlpha && c4 == colorBlack)
{
//Color
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( c2 );
PrimBuild::vertex2i( r, t );
PrimBuild::color( c2 );
PrimBuild::vertex2i( r, b );
PrimBuild::color( c2 );
PrimBuild::vertex2i( l, b );
PrimBuild::color( c2 );
PrimBuild::vertex2i( l, t );
PrimBuild::end();
//White
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( colorAlphaW );
PrimBuild::vertex2i( r, t );
PrimBuild::color( colorAlphaW );
PrimBuild::vertex2i( r, b );
PrimBuild::color( c1 );
PrimBuild::vertex2i( l, b );
PrimBuild::color( c1 );
PrimBuild::vertex2i( l, t );
PrimBuild::end();
//Black
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( c3 );
PrimBuild::vertex2i( r, t );
PrimBuild::color( c4 );
PrimBuild::vertex2i( r, b );
PrimBuild::color( c4 );
PrimBuild::vertex2i( l, b );
PrimBuild::color( c3 );
PrimBuild::vertex2i( l, t );
PrimBuild::end();
}
else
{
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( c1 );
PrimBuild::vertex2i( l, t );
PrimBuild::color( c2 );
PrimBuild::vertex2i( r, t );
PrimBuild::color( c3 );
PrimBuild::vertex2i( r, b );
PrimBuild::color( c4 );
PrimBuild::vertex2i( l, b );
PrimBuild::end();
}
}
//--------------------------------------------------------------------------
/// Function to draw a set of boxes blending throughout an array of colors
void GuiColorPickerCtrl::drawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors)
{
GFX->setStateBlock(mStateBlock);
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x + 4;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y + 4;
// Calculate increment value
S32 x_inc = int(mFloor((r - l) / F32(numColors-1)));
S32 y_inc = int(mFloor((b - t) / F32(numColors-1)));
for( U16 i = 0;i < numColors - 1; i++ )
{
// This is not efficent, but then again it doesn't really need to be. -pw
PrimBuild::begin( GFXTriangleFan, 4 );
if (!vertical) // Horizontal (+x)
{
// First color
PrimBuild::color( colors[i] );
PrimBuild::vertex2i( l, t );
PrimBuild::vertex2i( l, b );
// Second color
PrimBuild::color( colors[i+1] );
PrimBuild::vertex2i( l + x_inc, b );
PrimBuild::vertex2i( l + x_inc, t );
l += x_inc;
}
else // Vertical (+y)
{
// First color
PrimBuild::color( colors[i] );
PrimBuild::vertex2i( l, t );
PrimBuild::vertex2i( r, t );
// Second color
PrimBuild::color( colors[i+1] );
PrimBuild::vertex2i( r, t + y_inc );
PrimBuild::vertex2i( l, t + y_inc );
t += y_inc;
}
PrimBuild::end();
}
}
void GuiColorPickerCtrl::drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode)
{
if( !mShowReticle )
return;
U16 sMax = mSelectorGap*2;
switch (mode)
{
case sVertical:
// Now draw the vertical selector
// Up -> Pos
if (selectorPos.y != bounds.point.y+1)
GFX->getDrawUtil()->drawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend);
// Down -> Pos
if (selectorPos.y != bounds.point.y+bounds.extent.y)
GFX->getDrawUtil()->drawLine(selectorPos.x, selectorPos.y + sMax, selectorPos.x, bounds.point.y + bounds.extent.y, colorWhiteBlend);
break;
case sHorizontal:
// Now draw the horizontal selector
// Left -> Pos
if (selectorPos.x != bounds.point.x)
GFX->getDrawUtil()->drawLine(bounds.point.x, selectorPos.y-1, selectorPos.x-sMax, selectorPos.y-1, colorWhiteBlend);
// Right -> Pos
if (selectorPos.x != bounds.point.x)
GFX->getDrawUtil()->drawLine(bounds.point.x+mSelectorPos.x+sMax, selectorPos.y-1, bounds.point.x + bounds.extent.x, selectorPos.y-1, colorWhiteBlend);
break;
}
}
//--------------------------------------------------------------------------
/// Function to invoke calls to draw the picker box and selector
void GuiColorPickerCtrl::renderColorBox(RectI &bounds)
{
RectI pickerBounds;
pickerBounds.point.x = bounds.point.x+1;
pickerBounds.point.y = bounds.point.y+1;
pickerBounds.extent.x = bounds.extent.x-1;
pickerBounds.extent.y = bounds.extent.y-1;
if (mProfile->mBorder)
GFX->getDrawUtil()->drawRect(bounds, mProfile->mBorderColor);
Point2I selectorPos = Point2I(bounds.point.x+mSelectorPos.x+1, bounds.point.y+mSelectorPos.y+1);
// Draw color box differently depending on mode
RectI blendRect;
switch (mDisplayMode)
{
case pHorizColorRange:
drawBlendRangeBox( pickerBounds, false, 7, mColorRange);
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertColorRange:
drawBlendRangeBox( pickerBounds, true, 7, mColorRange);
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pHorizColorBrightnessRange:
blendRect = pickerBounds;
blendRect.point.y++;
blendRect.extent.y -= 2;
drawBlendRangeBox( pickerBounds, false, 7, mColorRange);
// This is being drawn slightly offset from the larger rect so as to insure 255 and 0
// can both be selected for every color.
drawBlendBox( blendRect, colorAlpha, colorAlpha, colorBlack, colorBlack );
blendRect.point.y += blendRect.extent.y - 1;
blendRect.extent.y = 2;
GFX->getDrawUtil()->drawRect( blendRect, colorBlack);
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertColorBrightnessRange:
drawBlendRangeBox( pickerBounds, true, 7, mColorRange);
drawBlendBox( pickerBounds, colorAlpha, colorBlack, colorBlack, colorAlpha );
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pHorizAlphaRange:
drawBlendBox( pickerBounds, colorBlack, colorWhite, colorWhite, colorBlack );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertAlphaRange:
drawBlendBox( pickerBounds, colorBlack, colorBlack, colorWhite, colorWhite );
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pBlendColorRange:
drawBlendBox( pickerBounds, colorWhite, mBaseColor, colorAlpha, colorBlack );
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pDropperBackground:
break;
case pPallet:
default:
GFX->getDrawUtil()->drawRectFill( pickerBounds, mBaseColor );
break;
}
}
void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect)
{
if (mStateBlock.isNull())
{
GFXStateBlockDesc desc;
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.setZReadWrite(false);
desc.zWriteEnable = false;
desc.setCullMode(GFXCullNone);
mStateBlock = GFX->createStateBlock( desc );
}
RectI boundsRect(offset, getExtent());
renderColorBox(boundsRect);
if (mPositionChanged)
{
mPositionChanged = false;
Point2I extent = getRoot()->getExtent();
// If we are anything but a pallete, change the pick color
if (mDisplayMode != pPallet)
{
Point2I resolution = getRoot()->getExtent();
U32 buf_x = offset.x + mSelectorPos.x + 1;
U32 buf_y = ( extent.y - ( offset.y + mSelectorPos.y + 1 ) );
if(GFX->getAdapterType() != OpenGL)
buf_y = resolution.y - buf_y;
GFXTexHandle bb( resolution.x,
resolution.y,
GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) );
Point2I tmpPt( buf_x, buf_y );
GFXTarget *targ = GFX->getActiveRenderTarget();
targ->resolveTo( bb );
GBitmap bmp( bb.getWidth(), bb.getHeight() );
bb.copyToBmp( &bmp );
//bmp.writePNGDebug( "foo.png" );
ColorI tmp;
bmp.getColor( buf_x, buf_y, tmp );
mPickColor = (ColorF)tmp;
// Now do onAction() if we are allowed
if (mActionOnMove)
onAction();
}
}
//render the children
renderChildControls( offset, updateRect);
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos)
{
Point2I extent = getExtent();
RectI rect;
if (mDisplayMode != pDropperBackground)
{
extent.x -= 3;
extent.y -= 2;
rect = RectI(Point2I(1,1), extent);
}
else
{
rect = RectI(Point2I(0,0), extent);
}
if (rect.pointInRect(pos))
{
mSelectorPos = pos;
mPositionChanged = true;
// We now need to update
setUpdate();
}
else
{
if ((pos.x > rect.point.x) && (pos.x < (rect.point.x + rect.extent.x)))
mSelectorPos.x = pos.x;
else if (pos.x <= rect.point.x)
mSelectorPos.x = rect.point.x;
else if (pos.x >= (rect.point.x + rect.extent.x))
mSelectorPos.x = rect.point.x + rect.extent.x - 1;
if ((pos.y > rect.point.y) && (pos.y < (rect.point.y + rect.extent.y)))
mSelectorPos.y = pos.y;
else if (pos.y <= rect.point.y)
mSelectorPos.y = rect.point.y;
else if (pos.y >= (rect.point.y + rect.extent.y))
mSelectorPos.y = rect.point.y + rect.extent.y - 1;
mPositionChanged = true;
setUpdate();
}
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event)
{
if (!mActive)
return;
if (mDisplayMode == pDropperBackground)
return;
mouseLock(this);
if (mProfile->mCanKeyFocus)
setFirstResponder();
if (mActive && (mDisplayMode != pDropperBackground))
onAction();
// Update the picker cross position
if (mDisplayMode != pPallet)
setSelectorPos(globalToLocalCoord(event.mousePoint));
mMouseDown = true;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseDragged(const GuiEvent &event)
{
if ((mActive && mMouseDown) || (mActive && (mDisplayMode == pDropperBackground)))
{
// Update the picker cross position
if (mDisplayMode != pPallet)
setSelectorPos(globalToLocalCoord(event.mousePoint));
}
if( !mActionOnMove )
execAltConsoleCallback();
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event)
{
// Only for dropper mode
if (mActive && (mDisplayMode == pDropperBackground))
setSelectorPos(globalToLocalCoord(event.mousePoint));
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseEnter(const GuiEvent &event)
{
mMouseOver = true;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseLeave(const GuiEvent &)
{
// Reset state
mMouseOver = false;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseUp(const GuiEvent &)
{
//if we released the mouse within this control, perform the action
if (mActive && mMouseDown && (mDisplayMode != pDropperBackground))
mMouseDown = false;
if (mActive && (mDisplayMode == pDropperBackground))
{
// In a dropper, the alt command executes the mouse up action (to signal stopping)
execAltConsoleCallback();
}
mouseUnlock();
}
//--------------------------------------------------------------------------
const char *GuiColorPickerCtrl::getScriptValue()
{
static char temp[256];
ColorF color = getValue();
dSprintf(temp,256,"%f %f %f %f",color.red, color.green, color.blue, color.alpha);
return temp;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::setScriptValue(const char *value)
{
ColorF newValue;
dSscanf(value, "%f %f %f %f", &newValue.red, &newValue.green, &newValue.blue, &newValue.alpha);
setValue(newValue);
}
ConsoleMethod(GuiColorPickerCtrl, getSelectorPos, const char*, 2, 2, "Gets the current position of the selector")
{
char *temp = Con::getReturnBuffer(256);
Point2I pos;
pos = object->getSelectorPos();
dSprintf(temp,256,"%d %d",pos.x, pos.y);
return temp;
}
ConsoleMethod(GuiColorPickerCtrl, setSelectorPos, void, 3, 3, "Sets the current position of the selector")
{
Point2I newPos;
dSscanf(argv[2], "%d %d", &newPos.x, &newPos.y);
object->setSelectorPos(newPos);
}
ConsoleMethod(GuiColorPickerCtrl, updateColor, void, 2, 2, "Forces update of pick color")
{
object->updateColor();
}

View file

@ -0,0 +1,152 @@
//-----------------------------------------------------------------------------
// 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 _GUICOLORPICKER_H_
#define _GUICOLORPICKER_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
//----------------------------
/// GuiColorPickerCtrl
///
/// This control draws a box containing a color specified by mPickColor,
/// in a way according to one of the PickMode enum's, stored as mDisplayMode.
///
/// The color the box represents is stored as mBaseColour (for pPallete, pBlendColorRange),
/// whilst the color chosen by the box is stored as mPickColor.
///
/// Whenever the control is clicked, it will do one of many things :
///
/// -# If its in pPallete mode, execute the regular "command"
/// -# If its in pBlendColorRange mode, update the selector position. The position will be re-read upon the next render. In addition, a cross will be drawn where the color has been selected from. As with 1, "command" will be executed.
/// -# If its in pHorizColorRange or pVertColorRange mode, it will function in a similar manner to 2, but the selector will resemble a horizontal or vertical bar.
/// -# If its in pHorizAlphaRange or pVertAlphaRange mode, it will also function the same way as 3
/// -# If its in pDropperBackground mode, nothing will happen
///
/// Colours are drawn in different ways according to mDisplayMode:
///
/// -# With pPallete, a box with a blank color, mBaseColor is drawn.
/// -# With pHorizColorRange, a horizontal box with colors blending in the range, mColorRange.
/// -# With pVertColorRange, a vertical box with colors blending in the range, mColorRange.
/// -# With pBlendColorRange, a box, the bottom colors being black, but the top left being white, and the top right being mBaseColor.
/// -# With pHorizAlphaRange, a horizontal box with black blending with an alpha from 0 to 255
/// -# With pVertAlphaRange, a vertical box with black blending with an apha from 0 to 255
/// -# With pDropperBackground, nothing is drawn
class GuiColorPickerCtrl : public GuiControl
{
typedef GuiControl Parent;
public:
enum PickMode
{
pPallet = 0, ///< We just have a solid color; We just act like a pallet
pHorizColorRange, ///< We have a range of base colors going horizontally
pVertColorRange, ///< We have a range of base colors going vertically
pHorizColorBrightnessRange, ///< HorizColorRange with brightness
pVertColorBrightnessRange, ///< VertColorRange with brightness
pBlendColorRange, ///< We have a box which shows a range in brightness of the color
pHorizAlphaRange, ///< We have a box which shows a range in alpha going horizontally
pVertAlphaRange, ///< We have a box which shows a range in alpha going vertically
pDropperBackground ///< The control does not draw anything; Only does something when you click, or move the mouse (when active)
};
enum SelectorMode
{
sHorizontal = 0, ///< Horizontal selector with small gap
sVertical, ///< Vertical selector with small gap
};
protected:
/// @name Core Rendering functions
/// @{
void renderColorBox(RectI &bounds); ///< Function that draws the actual color box
void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode); ///< Function that draws the selection indicator
void drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4);
void drawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors);
/// @}
/// @name Core Variables
/// @{
ColorF mPickColor; ///< Color that has been picked from control
ColorF mBaseColor; ///< Colour we display (in case of pallet and blend mode)
PickMode mDisplayMode; ///< Current color display mode of the selector
Point2I mSelectorPos; ///< Current position of the selector
bool mPositionChanged; ///< Current position has changed since last render?
bool mMouseOver; ///< Mouse is over?
bool mMouseDown; ///< Mouse button down?
bool mActionOnMove; ///< Perform onAction() when position has changed?
S32 mSelectorGap; ///< The half-way "gap" between the selector pos and where the selector is allowed to draw.
GFXStateBlockRef mStateBlock;
static ColorI mColorRange[7]; ///< Color range for pHorizColorRange and pVertColorRange
/// @}
public:
DECLARE_CONOBJECT(GuiColorPickerCtrl);
DECLARE_CATEGORY( "Gui Editor" );
GuiColorPickerCtrl();
static void initPersistFields();
void onRender(Point2I offset, const RectI &updateRect);
bool mShowReticle; ///< Show reticle on render
/// @name Color Value Functions
/// @{
/// NOTE: setValue only sets baseColor, since setting pickColor wouldn't be useful
void setValue(ColorF &value) {mBaseColor = value;}
/// NOTE: getValue() returns baseColor if pallet (since pallet controls can't "pick" colours themselves)
ColorF getValue() {return mDisplayMode == pPallet ? mBaseColor : mPickColor;}
const char *getScriptValue();
void setScriptValue(const char *value);
void updateColor() {mPositionChanged = true;}
/// @}
/// @name Selector Functions
/// @{
void setSelectorPos(const Point2I &pos); ///< Set new pos (in local coords)
Point2I getSelectorPos() {return mSelectorPos;}
/// @}
/// @name Input Events
/// @{
void onMouseDown(const GuiEvent &);
void onMouseUp(const GuiEvent &);
void onMouseMove(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseEnter(const GuiEvent &);
void onMouseLeave(const GuiEvent &);
/// @}
};
typedef GuiColorPickerCtrl::PickMode GuiColorPickMode;
DefineEnumType( GuiColorPickMode );
#endif

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// 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/console.h"
#include "gfx/gfxDrawUtil.h"
#include "gui/core/guiTypes.h"
#include "gui/core/guiControl.h"
#include "gui/controls/guiConsole.h"
#include "gui/containers/guiScrollCtrl.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(GuiConsole);
ConsoleDocClass( GuiConsole,
"@brief The on-screen, in-game console. Calls getLog() to get the on-screen console entries, then renders them as needed.\n\n"
"@tsexample\n"
" new GuiConsole()\n"
" {\n"
" //Properties not specific to this control have been omitted from this example.\n"
" };\n"
"@endtsexample\n\n"
"@see GuiControl\n\n"
"@ingroup GuiCore"
);
IMPLEMENT_CALLBACK( GuiConsole, onMessageSelected, void, ( ConsoleLogEntry::Level level, const char* message ), ( level, message ),
"Called when a message in the log is clicked.\n\n"
"@param level Diagnostic level of the message.\n"
"@param message Message text.\n" );
//-----------------------------------------------------------------------------
GuiConsole::GuiConsole()
{
setExtent(64, 64);
mCellSize.set(1, 1);
mSize.set(1, 0);
}
//-----------------------------------------------------------------------------
bool GuiConsole::onWake()
{
if (! Parent::onWake())
return false;
//get the font
mFont = mProfile->mFont;
return true;
}
//-----------------------------------------------------------------------------
S32 GuiConsole::getMaxWidth(S32 startIndex, S32 endIndex)
{
//sanity check
U32 size;
ConsoleLogEntry *log;
Con::getLockLog(log, size);
if(startIndex < 0 || (U32)endIndex >= size || startIndex > endIndex)
return 0;
S32 result = 0;
for(S32 i = startIndex; i <= endIndex; i++)
result = getMax(result, (S32)(mFont->getStrWidth((const UTF8 *)log[i].mString)));
Con::unlockLog();
return(result + 6);
}
//-----------------------------------------------------------------------------
void GuiConsole::onPreRender()
{
//see if the size has changed
U32 prevSize = getHeight() / mCellSize.y;
U32 size;
ConsoleLogEntry *log;
Con::getLockLog(log, size);
Con::unlockLog(); // we unlock immediately because we only use size here, not log.
if(size != prevSize)
{
//first, find out if the console was scrolled up
bool scrolled = false;
GuiScrollCtrl *parent = dynamic_cast<GuiScrollCtrl*>(getParent());
if(parent)
scrolled = parent->isScrolledToBottom();
//find the max cell width for the new entries
S32 newMax = getMaxWidth(prevSize, size - 1);
if(newMax > mCellSize.x)
mCellSize.set(newMax, mFont->getHeight());
//set the array size
mSize.set(1, size);
//resize the control
setExtent( Point2I(mCellSize.x, mCellSize.y * size));
//if the console was not scrolled, make the last entry visible
if (scrolled)
scrollCellVisible(Point2I(0,mSize.y - 1));
}
}
//-----------------------------------------------------------------------------
void GuiConsole::onRenderCell(Point2I offset, Point2I cell, bool /*selected*/, bool /*mouseOver*/)
{
U32 size;
ConsoleLogEntry *log;
Con::getLockLog(log, size);
ConsoleLogEntry &entry = log[cell.y];
switch (entry.mLevel)
{
case ConsoleLogEntry::Normal: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); break;
case ConsoleLogEntry::Warning: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL); break;
case ConsoleLogEntry::Error: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorNA); break;
default: AssertFatal(false, "GuiConsole::onRenderCell - Unrecognized ConsoleLogEntry type, update this.");
}
GFX->getDrawUtil()->drawText(mFont, Point2I(offset.x + 3, offset.y), entry.mString, mProfile->mFontColors);
Con::unlockLog();
}
//-----------------------------------------------------------------------------
void GuiConsole::onCellSelected( Point2I cell )
{
Parent::onCellSelected( cell );
U32 size;
ConsoleLogEntry* log;
Con::getLockLog(log, size);
ConsoleLogEntry& entry = log[ cell.y ];
onMessageSelected_callback( entry.mLevel, entry.mString );
Con::unlockLog();
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUICONSOLE_H_
#define _GUICONSOLE_H_
#ifndef _GUIARRAYCTRL_H_
#include "gui/core/guiArrayCtrl.h"
#endif
#ifndef _CONSOLE_LOGGER_H_
#include "console/consoleLogger.h"
#endif
class GuiConsole : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
Resource<GFont> mFont;
S32 getMaxWidth(S32 startIndex, S32 endIndex);
protected:
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onMessageSelected, ( ConsoleLogEntry::Level level, const char* message ) );
/// @}
// GuiArrayCtrl.
virtual void onCellSelected( Point2I cell );
public:
GuiConsole();
DECLARE_CONOBJECT(GuiConsole);
DECLARE_CATEGORY( "Gui Editor" );
DECLARE_DESCRIPTION( "Control that displays the console log text." );
// GuiArrayCtrl.
virtual bool onWake();
virtual void onPreRender();
virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
#endif

View file

@ -0,0 +1,145 @@
//-----------------------------------------------------------------------------
// 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/consoleTypes.h"
#include "console/console.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiConsoleEditCtrl.h"
#include "core/frameAllocator.h"
IMPLEMENT_CONOBJECT(GuiConsoleEditCtrl);
ConsoleDocClass( GuiConsoleEditCtrl,
"@brief Text entry element of a GuiConsole.\n\n"
"@tsexample\n"
"new GuiConsoleEditCtrl(ConsoleEntry)\n"
"{\n"
" profile = \"ConsoleTextEditProfile\";\n"
" horizSizing = \"width\";\n"
" vertSizing = \"top\";\n"
" position = \"0 462\";\n"
" extent = \"640 18\";\n"
" minExtent = \"8 8\";\n"
" visible = \"1\";\n"
" altCommand = \"ConsoleEntry::eval();\";\n"
" helpTag = \"0\";\n"
" maxLength = \"255\";\n"
" historySize = \"40\";\n"
" password = \"0\";\n"
" tabComplete = \"0\";\n"
" sinkAllKeyEvents = \"1\";\n"
" useSiblingScroller = \"1\";\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiCore"
);
GuiConsoleEditCtrl::GuiConsoleEditCtrl()
{
mSinkAllKeyEvents = true;
mSiblingScroller = NULL;
mUseSiblingScroller = true;
}
void GuiConsoleEditCtrl::initPersistFields()
{
addGroup("GuiConsoleEditCtrl");
addField("useSiblingScroller", TypeBool, Offset(mUseSiblingScroller, GuiConsoleEditCtrl));
endGroup("GuiConsoleEditCtrl");
Parent::initPersistFields();
}
bool GuiConsoleEditCtrl::onKeyDown(const GuiEvent &event)
{
setUpdate();
if (event.keyCode == KEY_TAB)
{
// Get a buffer that can hold the completed text...
FrameTemp<UTF8> tmpBuff(GuiTextCtrl::MAX_STRING_LENGTH);
// And copy the text to be completed into it.
mTextBuffer.getCopy8(tmpBuff, GuiTextCtrl::MAX_STRING_LENGTH);
// perform the completion
bool forward = (event.modifier & SI_SHIFT) == 0;
mCursorPos = Con::tabComplete(tmpBuff, mCursorPos, GuiTextCtrl::MAX_STRING_LENGTH, forward);
// place results in our buffer.
mTextBuffer.set(tmpBuff);
return true;
}
else if ((event.keyCode == KEY_PAGE_UP) || (event.keyCode == KEY_PAGE_DOWN))
{
// See if there's some other widget that can scroll the console history.
if (mUseSiblingScroller)
{
if (mSiblingScroller)
{
return mSiblingScroller->onKeyDown(event);
}
else
{
// Let's see if we can find it...
SimGroup* pGroup = getGroup();
if (pGroup)
{
// Find the first scroll control in the same group as us.
for (SimSetIterator itr(pGroup); *itr; ++itr)
{
mSiblingScroller = dynamic_cast<GuiScrollCtrl*>(*itr);
if (mSiblingScroller != NULL)
{
return mSiblingScroller->onKeyDown(event);
}
}
}
// No luck... so don't try, next time.
mUseSiblingScroller = false;
}
}
}
else if( event.keyCode == KEY_RETURN || event.keyCode == KEY_NUMPADENTER )
{
if ( event.modifier & SI_SHIFT &&
mTextBuffer.length() + dStrlen("echo();") <= GuiTextCtrl::MAX_STRING_LENGTH )
{
// Wrap the text with echo( %s );
char buf[GuiTextCtrl::MAX_STRING_LENGTH];
getText( buf );
String text( buf );
text.replace( ";", "" );
text = String::ToString( "echo(%s);", text.c_str() );
setText( text );
}
return Parent::dealWithEnter(false);
}
return Parent::onKeyDown(event);
}

View file

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// 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 _GUICONSOLEEDITCTRL_H_
#define _GUICONSOLEEDITCTRL_H_
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUITEXTEDITCTRL_H_
#include "gui/controls/guiTextEditCtrl.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
class GuiConsoleEditCtrl : public GuiTextEditCtrl
{
private:
typedef GuiTextEditCtrl Parent;
protected:
bool mUseSiblingScroller;
GuiScrollCtrl* mSiblingScroller;
public:
GuiConsoleEditCtrl();
DECLARE_CONOBJECT(GuiConsoleEditCtrl);
DECLARE_CATEGORY( "Gui Editor" );
static void initPersistFields();
bool onKeyDown(const GuiEvent &event);
};
#endif //_GUI_TEXTEDIT_CTRL_H

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// 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/controls/guiConsoleTextCtrl.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "core/color.h"
#include "gfx/gfxDrawUtil.h"
#include "gui/core/guiDefaultControlRender.h"
IMPLEMENT_CONOBJECT(GuiConsoleTextCtrl);
ConsoleDocClass( GuiConsoleTextCtrl,
"@brief Used by GUIConsole system internally.\n\n"
"@internal"
);
GuiConsoleTextCtrl::GuiConsoleTextCtrl()
{
}
GuiConsoleTextCtrl::~GuiConsoleTextCtrl()
{
}
void GuiConsoleTextCtrl::initPersistFields()
{
addGroup("GuiConsoleTextCtrl");
addField("expression", TypeRealString, Offset(mConsoleExpression, GuiConsoleTextCtrl));
endGroup("GuiConsoleTextCtrl");
Parent::initPersistFields();
}
bool GuiConsoleTextCtrl::onWake()
{
if (! Parent::onWake())
return false;
mFont = mProfile->mFont;
return true;
}
void GuiConsoleTextCtrl::onSleep()
{
Parent::onSleep();
mFont = NULL;
}
void GuiConsoleTextCtrl::setText(const char *txt)
{
//make sure we don't call this before onAdd();
AssertFatal(mProfile, "Can't call setText() until setProfile() has been called.");
if (txt)
mConsoleExpression = txt;
else
mConsoleExpression = String::EmptyString;
// make sure we have a font
mProfile->incLoadCount();
mFont = mProfile->mFont;
setUpdate();
// decrement the profile reference
mProfile->decLoadCount();
}
void GuiConsoleTextCtrl::calcResize()
{
if ( mResult.isEmpty() )
return;
// The width is the longest line.
U32 ctrlWidth = 0;
for ( U32 i = 0; i < mLineLen.size(); i++ )
{
U32 width = mFont->getStrNWidth( mResult.c_str() + mStartLineOffset[i], mLineLen[i] );
if ( width > ctrlWidth )
ctrlWidth = width;
}
// The height is the number of lines times the height of the font.
U32 ctrlHeight = mLineLen.size() * mFont->getHeight();
setExtent( Point2I( ctrlWidth, ctrlHeight ) + mProfile->mTextOffset * 2 );
}
void GuiConsoleTextCtrl::onPreRender()
{
if ( mConsoleExpression.isNotEmpty() )
{
mResult = Con::evaluatef( "$guiConsoleTextCtrlTemp = %s;", mConsoleExpression.c_str() );
// Of the resulting string we will be printing,
// Find the number of lines and length of each.
mProfile->mFont->wrapString( mResult, U32_MAX, mStartLineOffset, mLineLen );
}
else
mResult = String::EmptyString;
calcResize();
}
void GuiConsoleTextCtrl::onRender( Point2I offset, const RectI &updateRect )
{
RectI ctrlRect( offset, getExtent() );
// if opaque, fill the update rect with the fill color
if ( mProfile->mOpaque )
GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColor );
// if there's a border, draw the border
if ( mProfile->mBorder )
renderBorder( ctrlRect, mProfile );
// If we have text to render.
if ( mResult.isNotEmpty() )
{
GFont *font = mProfile->mFont;
GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
for ( U32 i = 0; i < mLineLen.size(); i++ )
{
Point2I tempOffset = offset;
tempOffset += mProfile->mTextOffset;
tempOffset.y += i * font->getHeight();
const UTF8 *line = mResult.c_str() + mStartLineOffset[i];
U32 lineLen = mLineLen[i];
GFX->getDrawUtil()->drawTextN( font, tempOffset, line, lineLen, mProfile->mFontColors );
}
}
// render the child controlsmResult
renderChildControls(offset, updateRect);
}
const char *GuiConsoleTextCtrl::getScriptValue()
{
return getText();
}
void GuiConsoleTextCtrl::setScriptValue(const char *val)
{
setText(val);
}

View file

@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
// 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 _GUICONSOLETEXTCTRL_H_
#define _GUICONSOLETEXTCTRL_H_
#ifndef _GFONT_H_
#include "gfx/gFont.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiConsoleTextCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
enum Constants { MAX_STRING_LENGTH = 255 };
protected:
String mConsoleExpression;
String mResult;
Resource<GFont> mFont;
Vector<U32> mStartLineOffset;
Vector<U32> mLineLen;
public:
//creation methods
DECLARE_CONOBJECT(GuiConsoleTextCtrl);
DECLARE_CATEGORY( "Gui Editor" );
GuiConsoleTextCtrl();
virtual ~GuiConsoleTextCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
//text methods
virtual void setText( const char *txt = NULL );
const char* getText() { return mConsoleExpression.c_str(); }
//rendering methods
void calcResize();
void onPreRender(); // do special pre render processing
void onRender( Point2I offset, const RectI &updateRect );
//Console methods
const char* getScriptValue();
void setScriptValue( const char *value );
};
#endif //_GUI_TEXT_CONTROL_H_

View file

@ -0,0 +1,238 @@
//-----------------------------------------------------------------------------
// 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/controls/guiDecoyCtrl.h"
#include "gui/buttons/guiButtonBaseCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/primBuilder.h"
//-----------------------------------------------------------------------------
// GuiDecoyCtrl
//-----------------------------------------------------------------------------
/*
So far this control has been designed in mind solely for button controls. I'm pretty sure
it can be used for other things, but to do anything more in depth; it has to be extended.
Make sure you know a little about how guiCanvas hands out signals to gui controls before you tinker
in this class.
Been thinking about this class a little more. I tried pretty hard to protect this class into being
guiControl like agnostic. But I ended up adding a check specifically for buttons in the
onMouseUp function. Its been protected with a dynamic_cast and a NULL check; but in the end, the only way
too solve the main problem, that GuiCanvas cannot process more than one mouse action for more than one
gui control at a time, is for it to get a rewrite.
*/
IMPLEMENT_CONOBJECT(GuiDecoyCtrl);
ConsoleDocClass( GuiDecoyCtrl,
"@brief Designed soley for buttons, primarily used in editor.\n\n"
"Currently editor use only, no real application without extension.\n\n "
"@internal");
GuiDecoyCtrl::GuiDecoyCtrl() : mIsDecoy(true),
mMouseOver(false),
mDecoyReference(NULL)
{
}
GuiDecoyCtrl::~GuiDecoyCtrl()
{
}
void GuiDecoyCtrl::initPersistFields()
{
addField("isDecoy", TypeBool, Offset(mIsDecoy, GuiDecoyCtrl), "Sets this control to decoy mode");
Parent::initPersistFields();
}
void GuiDecoyCtrl::onMouseUp(const GuiEvent &event)
{
mouseUnlock();
setUpdate();
//this code is pretty hacky. right now there is no way that guiCanvas will allow sending more than
//one signal to one gui control at a time.
if(mIsDecoy == true)
{
mVisible = false;
GuiControl *parent = getParent();
Point2I localPoint = parent->globalToLocalCoord(event.mousePoint);
GuiControl *tempControl = parent->findHitControl(localPoint);
//the decoy control has the responsibility of keeping track of the decoyed controls status
if( mDecoyReference != NULL && tempControl == mDecoyReference)
tempControl->onMouseUp(event);
else if(mDecoyReference != NULL && tempControl != mDecoyReference)
{
//as explained earlier, this control was written in the mindset for buttons.
//nothing bad should ever happen if not a button due to the checks in this function though.
GuiButtonBaseCtrl *unCastCtrl = NULL;
unCastCtrl = dynamic_cast<GuiButtonBaseCtrl *>( mDecoyReference );
if(unCastCtrl != NULL)
unCastCtrl->resetState();
}
mVisible = true;
}
}
void GuiDecoyCtrl::onMouseDown(const GuiEvent &event)
{
if ( !mVisible || !mAwake )
return;
mouseLock();
if(mIsDecoy == true)
{
mVisible = false;
GuiControl *parent = getParent();
Point2I localPoint = parent->globalToLocalCoord(event.mousePoint);
GuiControl *tempControl = parent->findHitControl(localPoint);
tempControl->onMouseDown(event);
mVisible = true;
}
execConsoleCallback();
setUpdate();
}
void GuiDecoyCtrl::onMouseMove(const GuiEvent &event)
{
//if this control is a dead end, make sure the event stops here
if ( !mVisible || !mAwake )
return;
//pass the event to the parent
GuiControl *parent = getParent();
if ( parent )
parent->onMouseMove( event );
Point2I localPoint = parent->globalToLocalCoord(event.mousePoint);
//also pretty hacky. since guiCanvas, *NOT* GuiControl, distributes the calls for onMouseEnter
//and onMouseLeave, we simulate those calls here through a series of checks.
if(mIsDecoy == true)
{
mVisible = false;
GuiControl *parent = getParent();
GuiControl *tempControl = parent->findHitControl(localPoint);
//the decoy control has the responsibility of keeping track of the decoyed controls status
if(mMouseOverDecoy == false && mDecoyReference != NULL)
{
tempControl->onMouseEnter(event);
mMouseOverDecoy = true;
}
else if(tempControl != mDecoyReference && mDecoyReference != NULL)
{
mDecoyReference->onMouseLeave(event);
mMouseOverDecoy = false;
}
mDecoyReference = tempControl;
mVisible = true;
}
}
void GuiDecoyCtrl::onMouseDragged(const GuiEvent &event)
{
}
void GuiDecoyCtrl::onMouseEnter(const GuiEvent &event)
{
if ( !mVisible || !mAwake )
return;
setUpdate();
Con::executef( this , "onMouseEnter" );
mMouseOver = true;
}
void GuiDecoyCtrl::onMouseLeave(const GuiEvent &event)
{
if ( !mVisible || !mAwake )
return;
setUpdate();
Con::executef( this , "onMouseLeave" );
mMouseOver = false;
}
bool GuiDecoyCtrl::onMouseWheelUp( const GuiEvent &event )
{
//if this control is a dead end, make sure the event stops here
if ( !mVisible || !mAwake )
return true;
//pass the event to the parent
GuiControl *parent = getParent();
if ( parent )
return parent->onMouseWheelUp( event );
else
return false;
}
bool GuiDecoyCtrl::onMouseWheelDown( const GuiEvent &event )
{
//if this control is a dead end, make sure the event stops here
if ( !mVisible || !mAwake )
return true;
//pass the event to the parent
GuiControl *parent = getParent();
if ( parent )
return parent->onMouseWheelDown( event );
else
return false;
}
void GuiDecoyCtrl::onRightMouseDown(const GuiEvent &)
{
}
void GuiDecoyCtrl::onRightMouseUp(const GuiEvent &)
{
}
void GuiDecoyCtrl::onRightMouseDragged(const GuiEvent &)
{
}
void GuiDecoyCtrl::onMiddleMouseDown(const GuiEvent &)
{
}
void GuiDecoyCtrl::onMiddleMouseUp(const GuiEvent &)
{
}
void GuiDecoyCtrl::onMiddleMouseDragged(const GuiEvent &)
{
}

View file

@ -0,0 +1,69 @@
//-----------------------------------------------------------------------------
// 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 _GUIDECOYCTRL_H_
#define _GUIDECOYCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GuiDecoyCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
// Constructor/Destructor/ConObject Declaration
GuiDecoyCtrl();
virtual ~GuiDecoyCtrl();
DECLARE_CONOBJECT(GuiDecoyCtrl);
DECLARE_CATEGORY( "Gui Other" );
static void initPersistFields();
bool mMouseOver;
bool mIsDecoy;
GuiControl* mDecoyReference;
bool mMouseOverDecoy;
Point2I mMouseDownPosition;
virtual void onMouseUp(const GuiEvent &event);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseMove(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseEnter(const GuiEvent &event);
virtual void onMouseLeave(const GuiEvent &event);
virtual bool onMouseWheelUp(const GuiEvent &event);
virtual bool onMouseWheelDown(const GuiEvent &event);
virtual void onRightMouseDown(const GuiEvent &event);
virtual void onRightMouseUp(const GuiEvent &event);
virtual void onRightMouseDragged(const GuiEvent &event);
virtual void onMiddleMouseDown(const GuiEvent &event);
virtual void onMiddleMouseUp(const GuiEvent &event);
virtual void onMiddleMouseDragged(const GuiEvent &event);
};
#endif

View file

@ -0,0 +1,222 @@
//-----------------------------------------------------------------------------
// 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 "core/strings/findMatch.h"
#include "gui/controls/guiDirectoryFileListCtrl.h"
IMPLEMENT_CONOBJECT( GuiDirectoryFileListCtrl );
ConsoleDocClass( GuiDirectoryFileListCtrl,
"@brief A control that displays a list of files from within a single directory "
"in the game file system.\n\n"
"@tsexample\n\n"
"new GuiDirectoryFileListCtrl()\n"
"{\n"
" filePath = \"art/shapes\";\n"
" fileFilter = \"*.dts\" TAB \"*.dae\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiControls\n"
);
GuiDirectoryFileListCtrl::GuiDirectoryFileListCtrl()
{
mFilePath = StringTable->insert( "" );
mFilter = StringTable->insert( "*.*" );
}
void GuiDirectoryFileListCtrl::initPersistFields()
{
addProtectedField( "filePath", TypeString, Offset( mFilePath, GuiDirectoryFileListCtrl ),
&_setFilePath, &defaultProtectedGetFn, "Path in game directory from which to list files." );
addProtectedField( "fileFilter", TypeString, Offset( mFilter, GuiDirectoryFileListCtrl ),
&_setFilter, &defaultProtectedGetFn, "Tab-delimited list of file name patterns. Only matched files will be displayed." );
Parent::initPersistFields();
}
bool GuiDirectoryFileListCtrl::onWake()
{
if( !Parent::onWake() )
return false;
update();
return true;
}
void GuiDirectoryFileListCtrl::onMouseDown(const GuiEvent &event)
{
Parent::onMouseDown( event );
if( event.mouseClickCount == 2 )
onDoubleClick_callback();
}
void GuiDirectoryFileListCtrl::openDirectory()
{
String path;
if( mFilePath && mFilePath[ 0 ] )
path = String::ToString( "%s/%s", Platform::getMainDotCsDir(), mFilePath );
else
path = Platform::getMainDotCsDir();
Vector<Platform::FileInfo> fileVector;
Platform::dumpPath( path, fileVector, 0 );
// Clear the current file listing
clearItems();
// Does this dir have any files?
if( fileVector.empty() )
return;
// If so, iterate through and list them
Vector<Platform::FileInfo>::iterator i = fileVector.begin();
for( S32 j=0 ; i != fileVector.end(); i++, j++ )
{
if( !mFilter[ 0 ] || FindMatch::isMatchMultipleExprs( mFilter, (*i).pFileName,false ) )
addItem( (*i).pFileName );
}
}
void GuiDirectoryFileListCtrl::setCurrentFilter( const char* filter )
{
if( !filter )
filter = "";
mFilter = StringTable->insert( filter );
// Update our view
openDirectory();
}
DefineEngineMethod( GuiDirectoryFileListCtrl, setFilter, void, ( const char* filter ),,
"Set the file filter.\n\n"
"@param filter Tab-delimited list of file name patterns. Only matched files will be displayed.\n" )
{
object->setCurrentFilter( filter );
}
bool GuiDirectoryFileListCtrl::setCurrentPath( const char* path, const char* filter )
{
if( !path )
return false;
const U32 pathLen = dStrlen( path );
if( pathLen > 0 && path[ pathLen - 1 ] == '/' )
mFilePath = StringTable->insertn( path, pathLen - 1 );
else
mFilePath = StringTable->insert( path );
if( filter )
mFilter = StringTable->insert( filter );
// Update our view
openDirectory();
return true;
}
DefineEngineMethod( GuiDirectoryFileListCtrl, reload, void, (),,
"Update the file list." )
{
object->update();
}
DefineEngineMethod( GuiDirectoryFileListCtrl, setPath, bool, ( const char* path, const char* filter ),,
"Set the search path and file filter.\n\n"
"@param path Path in game directory from which to list files.\n"
"@param filter Tab-delimited list of file name patterns. Only matched files will be displayed.\n" )
{
return object->setCurrentPath( path, filter );
}
DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFiles, const char*, (),,
"Get the list of selected files.\n\n"
"@return A space separated list of selected files" )
{
Vector<S32> ItemVector;
object->getSelectedItems( ItemVector );
if( ItemVector.empty() )
return StringTable->insert( "" );
// Get an adequate buffer
char itemBuffer[256];
dMemset( itemBuffer, 0, 256 );
char* returnBuffer = Con::getReturnBuffer( ItemVector.size() * 64 );
dMemset( returnBuffer, 0, ItemVector.size() * 64 );
// Fetch the first entry
StringTableEntry itemText = object->getItemText( ItemVector[0] );
if( !itemText )
return StringTable->lookup("");
dSprintf( returnBuffer, ItemVector.size() * 64, "%s", itemText );
// If only one entry, return it.
if( ItemVector.size() == 1 )
return returnBuffer;
// Fetch the remaining entries
for( S32 i = 1; i < ItemVector.size(); i++ )
{
StringTableEntry itemText = object->getItemText( ItemVector[i] );
if( !itemText )
continue;
dMemset( itemBuffer, 0, 256 );
dSprintf( itemBuffer, 256, " %s", itemText );
dStrcat( returnBuffer, itemBuffer );
}
return returnBuffer;
}
StringTableEntry GuiDirectoryFileListCtrl::getSelectedFileName()
{
S32 item = getSelectedItem();
if( item == -1 )
return StringTable->lookup("");
StringTableEntry itemText = getItemText( item );
if( !itemText )
return StringTable->lookup("");
return itemText;
}
DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFile, const char*, (),,
"Get the currently selected filename.\n\n"
"@return The filename of the currently selected file\n" )
{
return object->getSelectedFileName();
}

View file

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------------
// 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_DIRECTORYFILELISTCTRL_H_
#define _GUI_DIRECTORYFILELISTCTRL_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _GUI_LISTBOXCTRL_H_
#include "gui/controls/guiListBoxCtrl.h"
#endif
class GuiDirectoryFileListCtrl : public GuiListBoxCtrl
{
private:
typedef GuiListBoxCtrl Parent;
protected:
StringTableEntry mFilePath;
StringTableEntry mFilter;
void openDirectory();
static bool _setFilePath( void *object, const char *index, const char *data )
{
GuiDirectoryFileListCtrl* ctrl = ( GuiDirectoryFileListCtrl* ) object;
ctrl->setCurrentPath( data, ctrl->mFilter );
return false;
}
static bool _setFilter( void *object, const char *index, const char *data )
{
GuiDirectoryFileListCtrl* ctrl = ( GuiDirectoryFileListCtrl* ) object;
ctrl->setCurrentFilter( data );
return false;
}
public:
GuiDirectoryFileListCtrl();
DECLARE_CONOBJECT(GuiDirectoryFileListCtrl);
DECLARE_DESCRIPTION( "A control that displays a list of files from within a single\n"
"directory in the game file system." );
static void initPersistFields();
void update() { openDirectory(); }
/// Set the current path to grab files from
bool setCurrentPath( const char* path, const char* filter );
void setCurrentFilter( const char* filter );
/// Get the currently selected file's name
StringTableEntry getSelectedFileName();
virtual void onMouseDown(const GuiEvent &event);
virtual bool onWake();
};
#endif

View file

@ -0,0 +1,430 @@
//-----------------------------------------------------------------------------
// 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/controls/guiFileTreeCtrl.h"
#include "core/strings/findMatch.h"
#include "core/frameAllocator.h"
#include "core/strings/stringUnit.h"
#include "console/consoleTypes.h"
IMPLEMENT_CONOBJECT(GuiFileTreeCtrl);
ConsoleDocClass( GuiFileTreeCtrl,
"@brief A control that displays a hierarchical tree view of a path in the game file system.\n\n"
"@note Currently not used, most likely existed for editors. Possibly deprecated.\n\n"
"@internal"
);
static bool _isDirInMainDotCsPath(const char* dir)
{
StringTableEntry cs = Platform::getMainDotCsDir();
U32 len = dStrlen(cs) + dStrlen(dir) + 2;
FrameTemp<UTF8> fullpath(len);
dSprintf(fullpath, len, "%s/%s", cs, dir);
return Platform::isDirectory(fullpath);
}
static bool _hasChildren(const char* path)
{
if( Platform::hasSubDirectory(path))
return true;
Vector<StringTableEntry> dummy;
Platform::dumpDirectories( path, dummy, 0, true);
return dummy.size() > 0;
}
GuiFileTreeCtrl::GuiFileTreeCtrl()
: Parent()
{
// Parent configuration
setBounds(0,0,200,100);
mDestroyOnSleep = false;
mSupportMouseDragging = false;
mMultipleSelections = false;
mFileFilter = "*.cs *.gui *.ed.cs";
_initFilters();
}
void GuiFileTreeCtrl::initPersistFields()
{
addGroup( "File Tree" );
addField( "rootPath", TypeRealString, Offset( mRootPath, GuiFileTreeCtrl ), "Path in game directory that should be displayed in the control." );
addProtectedField( "fileFilter", TypeRealString, Offset( mFileFilter, GuiFileTreeCtrl ),
&_setFileFilterValue, &defaultProtectedGetFn, "Vector of file patterns. If not empty, only files matching the pattern will be shown in the control." );
endGroup( "File Tree" );
Parent::initPersistFields();
}
static void _dumpFiles(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth = 0)
{
Vector<Platform::FileInfo> fileVec;
Platform::dumpPath( path, fileVec, depth);
for(U32 i = 0; i < fileVec.size(); i++)
{
directoryVector.push_back( StringTable->insert(fileVec[i].pFileName) );
}
}
void GuiFileTreeCtrl::updateTree()
{
// Kill off any existing items
_destroyTree();
// Here we're going to grab our system volumes from the platform layer and create them as roots
//
// Note : that we're passing a 1 as the last parameter to Platform::dumpDirectories, which tells it
// how deep to dump in recursion. This is an optimization to keep from dumping the whole file system
// to the tree. The tree will dump more paths as necessary when the virtual parents are expanded,
// much as windows does.
// Determine the root path.
String rootPath = Platform::getMainDotCsDir();
if( !mRootPath.isEmpty() )
rootPath = String::ToString( "%s/%s", rootPath.c_str(), mRootPath.c_str() );
// get the files in the main.cs dir
Vector<StringTableEntry> pathVec;
Platform::dumpDirectories( rootPath, pathVec, 0, true);
_dumpFiles( rootPath, pathVec, 0);
if( ! pathVec.empty() )
{
// get the last folder in the path.
char *dirname = dStrdup(rootPath);
U32 last = dStrlen(dirname)-1;
if(dirname[last] == '/')
dirname[last] = '\0';
char* lastPathComponent = dStrrchr(dirname,'/');
if(lastPathComponent)
*lastPathComponent++ = '\0';
else
lastPathComponent = dirname;
// Iterate through the returned paths and add them to the tree
Vector<StringTableEntry>::iterator j = pathVec.begin();
for( ; j != pathVec.end(); j++ )
{
char fullModPathSub [512];
dMemset( fullModPathSub, 0, 512 );
dSprintf( fullModPathSub, 512, "%s/%s", lastPathComponent, (*j) );
addPathToTree( *j );
}
dFree(dirname);
}
}
bool GuiFileTreeCtrl::onWake()
{
if( !Parent::onWake() )
return false;
updateTree();
return true;
}
bool GuiFileTreeCtrl::onVirtualParentExpand(Item *item)
{
if( !item || !item->isExpanded() )
return true;
const char* pathToExpand = item->getValue();
if( !pathToExpand )
{
Con::errorf("GuiFileTreeCtrl::onVirtualParentExpand - Unable to retrieve item value!");
return false;
}
Vector<StringTableEntry> pathVec;
_dumpFiles( pathToExpand, pathVec, 0 );
Platform::dumpDirectories( pathToExpand, pathVec, 0, true);
if( ! pathVec.empty() )
{
// Iterate through the returned paths and add them to the tree
Vector<StringTableEntry>::iterator i = pathVec.begin();
for( ; i != pathVec.end(); i++ )
recurseInsert(item, (*i) );
item->setExpanded( true );
}
item->setVirtualParent( false );
// Update our tree view
buildVisibleTree();
return true;
}
void GuiFileTreeCtrl::addPathToTree( StringTableEntry path )
{
if( !path )
{
Con::errorf("GuiFileTreeCtrl::addPathToTree - Invalid Path!");
return;
}
// Identify which root (volume) this path belongs to (if any)
S32 root = getFirstRootItem();
StringTableEntry ourPath = &path[ dStrcspn( path, "/" ) + 1];
StringTableEntry ourRoot = StringUnit::getUnit( path, 0, "/" );
// There are no current roots, we can safely create one
if( root == 0 )
{
recurseInsert( NULL, path );
}
else
{
while( root != 0 )
{
if( dStricmp( getItemValue( root ), ourRoot ) == 0 )
{
recurseInsert( getItem( root ), ourPath );
break;
}
root = this->getNextSiblingItem( root );
}
// We found none so we'll create one
if ( root == 0 )
{
recurseInsert( NULL, path );
}
}
}
void GuiFileTreeCtrl::onItemSelected( Item *item )
{
Con::executef( this, "onSelectPath", avar("%s",item->getValue()) );
mSelPath = item->getValue();
if( _hasChildren( mSelPath ) )
item->setVirtualParent( true );
}
bool GuiFileTreeCtrl::_setFileFilterValue( void *object, const char *index, const char *data )
{
GuiFileTreeCtrl* ctrl = ( GuiFileTreeCtrl* ) object;
ctrl->mFileFilter = data;
ctrl->_initFilters();
return false;
}
void GuiFileTreeCtrl::_initFilters()
{
mFilters.clear();
U32 index = 0;
while( true )
{
const char* pattern = StringUnit::getUnit( mFileFilter, index, " " );
if( !pattern[ 0 ] )
break;
mFilters.push_back( pattern );
++ index;
}
}
bool GuiFileTreeCtrl::matchesFilters(const char* filename)
{
if( !mFilters.size() )
return true;
for(int i = 0; i < mFilters.size(); i++)
{
if(FindMatch::isMatch( mFilters[i], filename))
return true;
}
return false;
}
void GuiFileTreeCtrl::recurseInsert( Item* parent, StringTableEntry path )
{
if( !path )
return;
char szPathCopy [ 1024 ];
dMemset( szPathCopy, 0, 1024 );
dStrcpy( szPathCopy, path );
// Jump over the first character if it's a root /
char *curPos = szPathCopy;
if( *curPos == '/' )
curPos++;
char szValue[1024];
dMemset( szValue, 0, 1024 );
if( parent )
{
dMemset( szValue, 0, sizeof( szValue ) );
dSprintf( szValue, sizeof( szValue ), "%s/%s", parent->getValue(), curPos );
}
else
{
dStrncpy( szValue, curPos, sizeof( szValue ) );
szValue[ sizeof( szValue ) - 1 ] = 0;
}
const U32 valueLen = dStrlen( szValue );
char* value = new char[ valueLen + 1 ];
dMemcpy( value, szValue, valueLen + 1 );
char *delim = dStrchr( curPos, '/' );
if ( delim )
{
// terminate our / and then move our pointer to the next character (rest of the path)
*delim = 0x00;
delim++;
}
S32 itemIndex = 0;
// only insert blindly if we have no root
if( !parent )
itemIndex = insertItem( 0, curPos, curPos );
else
{
bool allowed = (_isDirInMainDotCsPath(value) || matchesFilters(value));
Item *exists = parent->findChildByValue( szValue );
if( allowed && !exists && dStrcmp( curPos, "" ) != 0 )
{
// Since we're adding a child this parent can't be a virtual parent, so clear that flag
parent->setVirtualParent( false );
itemIndex = insertItem( parent->getID(), curPos);
Item *newitem = getItem(itemIndex);
newitem->setValue( value );
}
else
{
itemIndex = ( parent != NULL ) ? ( ( exists != NULL ) ? exists->getID() : -1 ) : -1;
}
}
Item *newitem = getItem(itemIndex);
if(newitem)
{
newitem->setValue( value );
if( _isDirInMainDotCsPath( value ) )
{
newitem->setNormalImage( Icon_FolderClosed );
newitem->setExpandedImage( Icon_Folder );
newitem->setVirtualParent(true);
newitem->setExpanded(false);
}
else
{
newitem->setNormalImage( Icon_Doc );
}
}
// since we're only dealing with volumes and directories, all end nodes will be virtual parents
// so if we are at the bottom of the rabbit hole, set the item to be a virtual parent
Item* item = getItem( itemIndex );
if(item)
{
item->setExpanded(false);
if(parent && _isDirInMainDotCsPath(item->getValue()) && Platform::hasSubDirectory(item->getValue()))
item->setVirtualParent(true);
}
if( delim )
{
if( ( dStrcmp( delim, "" ) == 0 ) && item )
{
item->setExpanded( false );
if( parent && _hasChildren( item->getValue() ) )
item->setVirtualParent( true );
}
}
else
{
if( item )
{
item->setExpanded( false );
if( parent && _hasChildren( item->getValue() ) )
item->setVirtualParent( true );
}
}
// Down the rabbit hole we go
recurseInsert( getItem( itemIndex ), delim );
}
ConsoleMethod( GuiFileTreeCtrl, getSelectedPath, const char*, 2, 2, "getSelectedPath() - returns the currently selected path in the tree")
{
const String& path = object->getSelectedPath();
return Con::getStringArg( path );
}
ConsoleMethod( GuiFileTreeCtrl, setSelectedPath, bool, 3, 3, "setSelectedPath(path) - expands the tree to the specified path")
{
return object->setSelectedPath( argv[ 2 ] );
}
ConsoleMethod( GuiFileTreeCtrl, reload, void, 2, 2, "() - Reread the directory tree hierarchy." )
{
object->updateTree();
}
bool GuiFileTreeCtrl::setSelectedPath( const char* path )
{
if( !path )
return false;
// Since we only list one deep on paths, we need to add the path to the tree just incase it isn't already indexed in the tree
// or else we wouldn't be able to select a path we hadn't previously browsed to. :)
if( _isDirInMainDotCsPath( path ) )
addPathToTree( path );
// see if we have a child that matches what we want
for(U32 i = 0; i < mItems.size(); i++)
{
if( dStricmp( mItems[i]->getValue(), path ) == 0 )
{
Item* item = mItems[i];
AssertFatal(item,"GuiFileTreeCtrl::setSelectedPath - Item Index Bad, Fatal Mistake!!!");
item->setExpanded( true );
clearSelection();
setItemSelected( item->getID(), true );
// make sure all of it's parents are expanded
S32 parent = getParentItem( item->getID() );
while( parent != 0 )
{
setItemExpanded( parent, true );
parent = getParentItem( parent );
}
// Rebuild our tree just incase we've oops'd
buildVisibleTree();
scrollVisible( item );
}
}
return false;
}

View file

@ -0,0 +1,77 @@
//-----------------------------------------------------------------------------
// 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_FILETREECTRL_H_
#define _GUI_FILETREECTRL_H_
#include "platform/platform.h"
#include "gui/controls/guiTreeViewCtrl.h"
class GuiFileTreeCtrl : public GuiTreeViewCtrl
{
private:
// Utility functions
void recurseInsert( Item* parent, StringTableEntry path );
void addPathToTree( StringTableEntry path );
protected:
String mSelPath;
String mFileFilter;
String mRootPath;
Vector< String > mFilters;
void _initFilters();
static bool _setFileFilterValue( void *object, const char *index, const char *data );
public:
typedef GuiTreeViewCtrl Parent;
enum
{
Icon_Folder = 1,
Icon_FolderClosed = 2,
Icon_Doc = 3
};
GuiFileTreeCtrl();
bool onWake();
bool onVirtualParentExpand(Item *item);
void onItemSelected( Item *item );
const String& getSelectedPath() { return mSelPath; }
bool setSelectedPath( const char* path );
bool matchesFilters(const char* filename);
void updateTree();
DECLARE_CONOBJECT( GuiFileTreeCtrl );
DECLARE_DESCRIPTION( "A control that displays a hierarchical tree view of a path in the game file system.\n"
"Note that to enable expanding/collapsing of directories, the control must be\n"
"placed inside a GuiScrollCtrl." );
static void initPersistFields();
};
#endif //_GUI_FILETREECTRL_H_

View file

@ -0,0 +1,877 @@
//-----------------------------------------------------------------------------
// 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 "guiGameListMenuCtrl.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/gfxDrawUtil.h"
//-----------------------------------------------------------------------------
// GuiGameListMenuCtrl
//-----------------------------------------------------------------------------
GuiGameListMenuCtrl::GuiGameListMenuCtrl()
: mSelected(NO_ROW),
mHighlighted(NO_ROW),
mDebugRender(false)
{
VECTOR_SET_ASSOCIATION(mRows);
// initialize the control callbacks
mCallbackOnA = StringTable->insert("");
mCallbackOnB = mCallbackOnA;
mCallbackOnX = mCallbackOnA;
mCallbackOnY = mCallbackOnA;
}
GuiGameListMenuCtrl::~GuiGameListMenuCtrl()
{
for (S32 i = 0; i < mRows.size(); ++i)
{
delete mRows[i];
}
}
void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect)
{
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
F32 xScale = (float) getWidth() / profile->getRowWidth();
bool profileHasIcons = profile->hasArrows();
S32 rowHeight = profile->getRowHeight();
Point2I currentOffset = offset;
Point2I extent = getExtent();
Point2I rowExtent(extent.x, rowHeight);
Point2I textOffset(profile->mTextOffset.x * xScale, profile->mTextOffset.y);
Point2I textExtent(extent.x - textOffset.x, rowHeight);
Point2I iconExtent, iconOffset(0.0f, 0.0f);
if (profileHasIcons)
{
iconExtent = profile->getIconExtent();
// icon is centered vertically plus any specified offset
S32 iconOffsetY = (rowHeight - iconExtent.y) >> 1;
iconOffsetY += profile->mIconOffset.y;
iconOffset = Point2I(profile->mIconOffset.x * xScale, iconOffsetY);
}
for (Vector<Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
if (row != mRows.begin())
{
// rows other than the first can have padding above them
currentOffset.y += (*row)->mHeightPad;
currentOffset.y += rowHeight;
}
// select appropriate colors and textures
ColorI fontColor;
U32 buttonTextureIndex;
S32 iconIndex = (*row)->mIconIndex;
bool useHighlightIcon = (*row)->mUseHighlightIcon;
if (! (*row)->mEnabled)
{
buttonTextureIndex = Profile::TEX_DISABLED;
fontColor = profile->mFontColorNA;
}
else if (row == &mRows[mSelected])
{
if (iconIndex != NO_ICON)
{
iconIndex++;
}
buttonTextureIndex = Profile::TEX_SELECTED;
fontColor = profile->mFontColorSEL;
}
else if ((mHighlighted != NO_ROW) && (row == &mRows[mHighlighted]))
{
if (iconIndex != NO_ICON && useHighlightIcon)
{
iconIndex++;
}
buttonTextureIndex = Profile::TEX_HIGHLIGHT;
fontColor = profile->mFontColorHL;
}
else
{
buttonTextureIndex = Profile::TEX_NORMAL;
fontColor = profile->mFontColor;
}
// render the row bitmap
GFX->getDrawUtil()->clearBitmapModulation();
GFX->getDrawUtil()->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset, rowExtent), profile->getBitmapArrayRect(buttonTextureIndex));
// render the row icon if it has one
if ((iconIndex != NO_ICON) && profileHasIcons && (! profile->getBitmapArrayRect((U32)iconIndex).extent.isZero()))
{
iconIndex += Profile::TEX_FIRST_ICON;
GFX->getDrawUtil()->clearBitmapModulation();
GFX->getDrawUtil()->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset + iconOffset, iconExtent), profile->getBitmapArrayRect(iconIndex));
}
// render the row text
GFX->getDrawUtil()->setBitmapModulation(fontColor);
renderJustifiedText(currentOffset + textOffset, textExtent, (*row)->mLabel);
}
if (mDebugRender)
{
onDebugRender(offset);
}
renderChildControls(offset, updateRect);
}
void GuiGameListMenuCtrl::onDebugRender(Point2I offset)
{
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
F32 xScale = (float) getWidth() / profile->getRowWidth();
ColorI controlBorderColor(200, 200, 200); // gray
ColorI rowBorderColor(255, 127, 255); // magenta
ColorI hitBorderColor(255, 0, 0); // red
Point2I shrinker(-1, -1);
Point2I extent = getExtent();
// render a border around the entire control
RectI borderRect(offset, extent + shrinker);
GFX->getDrawUtil()->drawRect(borderRect, controlBorderColor);
S32 rowHeight = profile->getRowHeight();
Point2I currentOffset(offset);
Point2I rowExtent(extent.x, rowHeight);
rowExtent += shrinker;
Point2I hitAreaExtent(profile->getHitAreaExtent());
hitAreaExtent.x *= xScale;
hitAreaExtent += shrinker;
Point2I hitAreaOffset = profile->mHitAreaUpperLeft;
hitAreaOffset.x *= xScale;
Point2I upperLeft;
for (Vector<Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
// set the top of the current row
if (row != mRows.begin())
{
// rows other than the first can have padding above them
currentOffset.y += (*row)->mHeightPad;
currentOffset.y += rowHeight;
}
// draw the box around the whole row's extent
upperLeft = currentOffset;
borderRect.point = upperLeft;
borderRect.extent = rowExtent;
GFX->getDrawUtil()->drawRect(borderRect, rowBorderColor);
// draw the box around the hit area of the row
upperLeft = currentOffset + hitAreaOffset;
borderRect.point = upperLeft;
borderRect.extent = hitAreaExtent;
GFX->getDrawUtil()->drawRect(borderRect, hitBorderColor);
}
}
void GuiGameListMenuCtrl::addRow(const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled)
{
Row * row = new Row();
addRow(row, label, callback, icon, yPad, useHighlightIcon, enabled);
}
void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled)
{
row->mLabel = StringTable->insert(label, true);
row->mScriptCallback = (dStrlen(callback) > 0) ? StringTable->insert(callback, true) : NULL;
row->mIconIndex = (icon < 0) ? NO_ICON : icon;
row->mHeightPad = yPad;
row->mUseHighlightIcon = useHighlightIcon;
row->mEnabled = enabled;
mRows.push_back(row);
updateHeight();
if (mSelected == NO_ROW)
{
selectFirstEnabledRow();
}
}
Point2I GuiGameListMenuCtrl::getMinExtent() const
{
Point2I parentMin = Parent::getMinExtent();
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
S32 minHeight = 0;
S32 rowHeight = profile->getRowHeight();
for (Vector<Row *>::const_iterator row = mRows.begin(); row < mRows.end(); ++row)
{
minHeight += rowHeight;
if (row != mRows.begin())
{
minHeight += (*row)->mHeightPad;
}
}
if (minHeight > parentMin.y)
parentMin.y = minHeight;
return parentMin;
}
bool GuiGameListMenuCtrl::onAdd()
{
if( !Parent::onAdd() )
return false;
// If we have a non-GuiGameListMenuProfile profile, try to
// substitute it for DefaultListMenuProfile.
if( !hasValidProfile() )
{
GuiGameListMenuProfile* profile;
if( !Sim::findObject( "DefaultListMenuProfile", profile ) )
{
Con::errorf( "GuiGameListMenuCtrl: %s can't be created with a profile of type %s. Please create it with a profile of type GuiGameListMenuProfile.",
getName(), mProfile->getClassName() );
return false;
}
else
Con::warnf( "GuiGameListMenuCtrl: substituted non-GuiGameListMenuProfile in %s for DefaultListMenuProfile", getName() );
setControlProfile( profile );
}
return true;
}
bool GuiGameListMenuCtrl::onWake()
{
if( !Parent::onWake() )
return false;
if( !hasValidProfile() )
return false;
if( mRows.empty() )
{
Con::errorf( "GuiGameListMenuCtrl: %s can't be woken up without any rows. Please use \"addRow\" to add at least one row to the control before pushing it to the canvas.",
getName() );
return false;
}
enforceConstraints();
selectFirstEnabledRow();
setFirstResponder();
mHighlighted = NO_ROW;
return true;
}
bool GuiGameListMenuCtrl::hasValidProfile() const
{
GuiGameListMenuProfile * profile = dynamic_cast<GuiGameListMenuProfile *>(mProfile);
return profile;
}
void GuiGameListMenuCtrl::enforceConstraints()
{
if( hasValidProfile() )
{
((GuiGameListMenuProfile *)mProfile)->enforceConstraints();
}
updateHeight();
}
void GuiGameListMenuCtrl::updateHeight()
{
S32 minHeight = getMinExtent().y;
if (getHeight() < minHeight)
{
setHeight(minHeight);
}
}
void GuiGameListMenuCtrl::onMouseDown(const GuiEvent &event)
{
S32 hitRow = getRow(event.mousePoint);
if (hitRow != NO_ROW)
{
S32 delta = (mSelected != NO_ROW) ? (hitRow - mSelected) : (mSelected + 1);
changeRow(delta);
}
}
void GuiGameListMenuCtrl::onMouseLeave(const GuiEvent &event)
{
mHighlighted = NO_ROW;
}
void GuiGameListMenuCtrl::onMouseMove(const GuiEvent &event)
{
S32 hitRow = getRow(event.mousePoint);
// allow mHighligetd to be set to NO_ROW so rows can be unhighlighted
mHighlighted = hitRow;
}
void GuiGameListMenuCtrl::onMouseUp(const GuiEvent &event)
{
S32 hitRow = getRow(event.mousePoint);
if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected()))
{
activateRow();
}
}
void GuiGameListMenuCtrl::activateRow()
{
S32 row = getSelected();
if ((row != NO_ROW) && isRowEnabled(row) && (mRows[row]->mScriptCallback != NULL))
{
setThisControl();
if (Con::isFunction(mRows[row]->mScriptCallback))
{
Con::executef(mRows[row]->mScriptCallback);
}
}
}
S32 GuiGameListMenuCtrl::getRow(Point2I globalPoint)
{
Point2I localPoint = globalToLocalCoord(globalPoint);
GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile;
F32 xScale = (float) getWidth() / profile->getRowWidth();
S32 rowHeight = profile->getRowHeight();
Point2I currentOffset(0, 0);
Point2I hitAreaUpperLeft = profile->mHitAreaUpperLeft;
hitAreaUpperLeft.x *= xScale;
Point2I hitAreaLowerRight = profile->mHitAreaLowerRight;
hitAreaLowerRight.x *= xScale;
Point2I upperLeft, lowerRight;
for (Vector<Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
if (row != mRows.begin())
{
// rows other than the first can have padding above them
currentOffset.y += (*row)->mHeightPad;
}
upperLeft = currentOffset + hitAreaUpperLeft;
lowerRight = currentOffset + hitAreaLowerRight;
if ((upperLeft.x <= localPoint.x) && (localPoint.x < lowerRight.x) &&
(upperLeft.y <= localPoint.y) && (localPoint.y < lowerRight.y))
{
return row - mRows.begin();
}
currentOffset.y += rowHeight;
}
return NO_ROW;
}
void GuiGameListMenuCtrl::setSelected(S32 index)
{
if (index == NO_ROW)
{
// deselection
mSelected = NO_ROW;
return;
}
if (! isValidRowIndex(index))
{
return;
}
if (! isRowEnabled(index))
{
// row is disabled, it can't be selected
return;
}
mSelected = mClamp(index, 0, mRows.size() - 1);
}
bool GuiGameListMenuCtrl::isRowEnabled(S32 index) const
{
if (! isValidRowIndex(index))
{
return false;
}
return mRows[index]->mEnabled;
}
void GuiGameListMenuCtrl::setRowEnabled(S32 index, bool enabled)
{
if (! isValidRowIndex(index))
{
return;
}
mRows[index]->mEnabled = enabled;
if (getSelected() == index)
{
selectFirstEnabledRow();
}
}
bool GuiGameListMenuCtrl::isValidRowIndex(S32 index) const
{
return ((0 <= index) && (index < mRows.size()));
}
void GuiGameListMenuCtrl::selectFirstEnabledRow()
{
setSelected(NO_ROW);
for (Vector<Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
if ((*row)->mEnabled)
{
setSelected(row - mRows.begin());
return;
}
}
}
bool GuiGameListMenuCtrl::onKeyDown(const GuiEvent &event)
{
switch (event.keyCode)
{
case KEY_UP:
changeRow(-1);
return true;
case KEY_DOWN:
changeRow(1);
return true;
case KEY_A:
case KEY_RETURN:
case KEY_NUMPADENTER:
case KEY_SPACE:
case XI_A:
case XI_START:
doScriptCommand(mCallbackOnA);
return true;
case KEY_B:
case KEY_ESCAPE:
case KEY_BACKSPACE:
case KEY_DELETE:
case XI_B:
case XI_BACK:
doScriptCommand(mCallbackOnB);
return true;
case KEY_X:
case XI_X:
doScriptCommand(mCallbackOnX);
return true;
case KEY_Y:
case XI_Y:
doScriptCommand(mCallbackOnY);
return true;
default:
break;
}
return Parent::onKeyDown(event);
}
bool GuiGameListMenuCtrl::onGamepadAxisUp(const GuiEvent &event)
{
changeRow(-1);
return true;
}
bool GuiGameListMenuCtrl::onGamepadAxisDown(const GuiEvent &event)
{
changeRow(1);
return true;
}
void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command)
{
if (command && command[0])
{
setThisControl();
Con::evaluate(command, false, __FILE__);
}
}
void GuiGameListMenuCtrl::changeRow(S32 delta)
{
S32 oldRowIndex = getSelected();
S32 newRowIndex = oldRowIndex;
do
{
newRowIndex += delta;
if (newRowIndex >= mRows.size())
{
newRowIndex = 0;
}
else if (newRowIndex < 0)
{
newRowIndex = mRows.size() - 1;
}
}
while ((! mRows[newRowIndex]->mEnabled) && (newRowIndex != oldRowIndex));
setSelected(newRowIndex);
// do the callback
onChange_callback();
}
void GuiGameListMenuCtrl::setThisControl()
{
smThisControl = this;
}
StringTableEntry GuiGameListMenuCtrl::getRowLabel(S32 rowIndex) const
{
AssertFatal(isValidRowIndex(rowIndex), avar("GuiGameListMenuCtrl: You can't get the label from row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d).", rowIndex, getName(), getRowCount()));
if (! isValidRowIndex(rowIndex))
{
// not a valid row index, don't do anything
return StringTable->insert("");
}
return mRows[rowIndex]->mLabel;
}
void GuiGameListMenuCtrl::setRowLabel(S32 rowIndex, const char * label)
{
AssertFatal(isValidRowIndex(rowIndex), avar("GuiGameListMenuCtrl: You can't set the label on row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d).", rowIndex, getName(), getRowCount()));
if (! isValidRowIndex(rowIndex))
{
// not a valid row index, don't do anything
return;
}
mRows[rowIndex]->mLabel = StringTable->insert(label, true);
}
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListMenuCtrl)
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiGameListMenuCtrl);
ConsoleDocClass( GuiGameListMenuCtrl,
"@brief A base class for cross platform menu controls that are gamepad friendly.\n\n"
"This class is used to build row-based menu GUIs that can be easily navigated "
"using the keyboard, mouse or gamepad. The desired row can be selected using "
"the mouse, or by navigating using the Up and Down buttons.\n\n"
"@tsexample\n\n"
"new GuiGameListMenuCtrl()\n"
"{\n"
" debugRender = \"0\";\n"
" callbackOnA = \"applyOptions();\";\n"
" callbackOnB = \"Canvas.setContent(MainMenuGui);\";\n"
" callbackOnX = \"\";\n"
" callbackOnY = \"revertOptions();\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@see GuiGameListMenuProfile\n\n"
"@ingroup GuiGame"
);
IMPLEMENT_CALLBACK( GuiGameListMenuCtrl, onChange, void, (), (),
"Called when the selected row changes." );
void GuiGameListMenuCtrl::initPersistFields()
{
addField("debugRender", TypeBool, Offset(mDebugRender, GuiGameListMenuCtrl),
"Enable debug rendering" );
addField("callbackOnA", TypeString, Offset(mCallbackOnA, GuiGameListMenuCtrl),
"Script callback when the 'A' button is pressed. 'A' inputs are Keyboard: A, Return, Space; Gamepad: A, Start" );
addField("callbackOnB", TypeString, Offset(mCallbackOnB, GuiGameListMenuCtrl),
"Script callback when the 'B' button is pressed. 'B' inputs are Keyboard: B, Esc, Backspace, Delete; Gamepad: B, Back" );
addField("callbackOnX", TypeString, Offset(mCallbackOnX, GuiGameListMenuCtrl),
"Script callback when the 'X' button is pressed. 'X' inputs are Keyboard: X; Gamepad: X" );
addField("callbackOnY", TypeString, Offset(mCallbackOnY, GuiGameListMenuCtrl),
"Script callback when the 'Y' button is pressed. 'Y' inputs are Keyboard: Y; Gamepad: Y" );
Parent::initPersistFields();
}
DefineEngineMethod( GuiGameListMenuCtrl, addRow, void,
( const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled ),
( -1, 0, true, true ),
"Add a row to the list control.\n\n"
"@param label The text to display on the row as a label.\n"
"@param callback Name of a script function to use as a callback when this row is activated.\n"
"@param icon [optional] Index of the icon to use as a marker.\n"
"@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n"
"@param useHighlightIcon [optional] Does this row use the highlight icon?.\n"
"@param enabled [optional] If this row is initially enabled." )
{
object->addRow( label, callback, icon, yPad, useHighlightIcon, enabled );
}
DefineEngineMethod( GuiGameListMenuCtrl, isRowEnabled, bool, ( S32 row ),,
"Determines if the specified row is enabled or disabled.\n\n"
"@param row The row to set the enabled status of.\n"
"@return True if the specified row is enabled. False if the row is not enabled or the given index was not valid." )
{
return object->isRowEnabled( row );
}
DefineEngineMethod( GuiGameListMenuCtrl, setRowEnabled, void, ( S32 row, bool enabled ),,
"Sets a row's enabled status according to the given parameters.\n\n"
"@param row The index to check for validity.\n"
"@param enabled Indicate true to enable the row or false to disable it." )
{
object->setRowEnabled( row, enabled );
}
DefineEngineMethod( GuiGameListMenuCtrl, activateRow, void, (),,
"Activates the current row. The script callback of the current row will be called (if it has one)." )
{
object->activateRow();
}
DefineEngineMethod( GuiGameListMenuCtrl, getRowCount, S32, (),,
"Gets the number of rows on the control.\n\n"
"@return (int) The number of rows on the control." )
{
return object->getRowCount();
}
DefineEngineMethod( GuiGameListMenuCtrl, getRowLabel, const char *, ( S32 row ),,
"Gets the label displayed on the specified row.\n\n"
"@param row Index of the row to get the label of.\n"
"@return The label for the row." )
{
return object->getRowLabel( row );
}
DefineEngineMethod( GuiGameListMenuCtrl, setRowLabel, void, ( S32 row, const char* label ),,
"Sets the label on the given row.\n\n"
"@param row Index of the row to set the label on.\n"
"@param label Text to set as the label of the row.\n" )
{
object->setRowLabel( row, label );
}
DefineEngineMethod( GuiGameListMenuCtrl, setSelected, void, ( S32 row ),,
"Sets the selected row. Only rows that are enabled can be selected.\n\n"
"@param row Index of the row to set as selected." )
{
object->setSelected( row );
}
DefineEngineMethod( GuiGameListMenuCtrl, getSelectedRow, S32, (),,
"Gets the index of the currently selected row.\n\n"
"@return Index of the selected row." )
{
return object->getSelected();
}
//-----------------------------------------------------------------------------
// GuiGameListMenuProfile
//-----------------------------------------------------------------------------
GuiGameListMenuProfile::GuiGameListMenuProfile()
: mHitAreaUpperLeft(0, 0),
mHitAreaLowerRight(0, 0),
mIconOffset(0, 0),
mRowSize(0, 0),
mRowScale(1.0f, 1.0f)
{
}
bool GuiGameListMenuProfile::onAdd()
{
if (! Parent::onAdd())
{
return false;
}
// We can't call enforceConstraints() here because incRefCount initializes
// some of the things to enforce. Do a basic sanity check here instead.
if( !dStrlen(mBitmapName) )
{
Con::errorf( "GuiGameListMenuProfile: %s can't be created without a bitmap. Please add a 'Bitmap' property to the object definition.", getName() );
return false;
}
if( mRowSize.x < 0 )
{
Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative.", getName() );
return false;
}
if( mRowSize.y < 0 )
{
Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative.", getName() );
return false;
}
return true;
}
void GuiGameListMenuProfile::enforceConstraints()
{
if( getBitmapArrayRect(0).extent.isZero() )
Con::errorf( "GuiGameListMenuCtrl: %s can't be created without a bitmap. Please add a bitmap to the profile's definition.", getName() );
if( mRowSize.x < 0 )
Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative.", getName() );
mRowSize.x = getMax(mRowSize.x, 0);
if( mRowSize.y < 0 )
Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative.", getName() );
mRowSize.y = getMax(mRowSize.y, 0);
Point2I rowTexExtent = getBitmapArrayRect(TEX_NORMAL).extent;
mRowScale.x = (float) getRowWidth() / rowTexExtent.x;
mRowScale.y = (float) getRowHeight() / rowTexExtent.y;
}
Point2I GuiGameListMenuProfile::getIconExtent()
{
Point2I iconExtent = getBitmapArrayRect(TEX_FIRST_ICON).extent;
// scale both by y to keep the aspect ratio
iconExtent.x *= mRowScale.y;
iconExtent.y *= mRowScale.y;
return iconExtent;
}
Point2I GuiGameListMenuProfile::getArrowExtent()
{
Point2I arrowExtent = getBitmapArrayRect(TEX_FIRST_ARROW).extent;
// scale both by y to keep the aspect ratio
arrowExtent.x *= mRowScale.y;
arrowExtent.y *= mRowScale.y;
return arrowExtent;
}
Point2I GuiGameListMenuProfile::getHitAreaExtent()
{
if (mHitAreaLowerRight == mHitAreaUpperLeft)
{
return mRowSize;
}
else
{
return mHitAreaLowerRight - mHitAreaUpperLeft;
}
}
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListMenuProfile)
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiGameListMenuProfile);
ConsoleDocClass( GuiGameListMenuProfile,
"@brief A GuiControlProfile with additional fields specific to GuiGameListMenuCtrl.\n\n"
"@tsexample\n"
"new GuiGameListMenuProfile()\n"
"{\n"
" hitAreaUpperLeft = \"10 2\";\n"
" hitAreaLowerRight = \"190 18\";\n"
" iconOffset = \"10 2\";\n"
" rowSize = \"200 20\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiGame"
);
void GuiGameListMenuProfile::initPersistFields()
{
addField( "hitAreaUpperLeft", TypePoint2I, Offset(mHitAreaUpperLeft, GuiGameListMenuProfile),
"Position of the upper left corner of the row hit area (relative to row's top left corner)" );
addField( "hitAreaLowerRight", TypePoint2I, Offset(mHitAreaLowerRight, GuiGameListMenuProfile),
"Position of the lower right corner of the row hit area (relative to row's top left corner)" );
addField( "iconOffset", TypePoint2I, Offset(mIconOffset, GuiGameListMenuProfile),
"Offset from the row's top left corner at which to render the row icon" );
addField( "rowSize", TypePoint2I, Offset(mRowSize, GuiGameListMenuProfile),
"The base size (\"width height\") of a row" );
Parent::initPersistFields();
removeField("tab");
removeField("mouseOverSelected");
removeField("modal");
removeField("opaque");
removeField("fillColor");
removeField("fillColorHL");
removeField("fillColorNA");
removeField("border");
removeField("borderThickness");
removeField("borderColor");
removeField("borderColorHL");
removeField("borderColorNA");
removeField("bevelColorHL");
removeField("bevelColorLL");
removeField("fontColorLink");
removeField("fontColorLinkHL");
removeField("justify");
removeField("returnTab");
removeField("numbersOnly");
removeField("cursorColor");
removeField("profileForChildren");
}

View file

@ -0,0 +1,354 @@
//-----------------------------------------------------------------------------
// 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 _GuiGameListMenuCtrl_H_
#define _GuiGameListMenuCtrl_H_
#include "gui/core/guiControl.h"
class GuiGameListMenuProfile;
/// \class GuiGameListMenuCtrl
/// A base class for cross platform menu controls that are gamepad friendly.
class GuiGameListMenuCtrl : public GuiControl
{
public:
typedef GuiControl Parent;
typedef GuiGameListMenuProfile Profile;
protected:
/// \struct Row
/// Internal data representation of a single row in the control.
struct Row
{
StringTableEntry mLabel; ///< Text to display in the row as a label
StringTableEntry mScriptCallback; ///< Script callback when row is activated
S32 mIconIndex; ///< Index of the icon to display on the row (-1 = no icon)
S32 mHeightPad; ///< Extra amount to pad above this row
bool mUseHighlightIcon; ///< Toggle the use of the highlight icon
bool mEnabled; ///< If this row is enabled or not (grayed out)
virtual ~Row() {}
};
public:
/// \return The index of the highlighted row or NO_ROW if none of the rows
/// are currently highlighted.
virtual S32 getHighlighted() const { return mHighlighted; }
/// \return The index of the selected row or NO_ROW if none of the rows are
/// currently selected.
virtual S32 getSelected() const { return mSelected; }
/// Sets the selected row. Only rows that are enabled can be selected. Input is
/// clamped to [0, mRows.size())
///
/// \param index The index to set as selected.
virtual void setSelected(S32 index);
/// Determines if the specified row is enabled or disabled.
///
/// \param index Index of the row to check.
/// \return True if the specified row is enabled. False if the row is not
/// enabled or the given index was not valid.
virtual bool isRowEnabled(S32 index) const;
/// Sets a row's enabled status according to the given parameters.
///
/// \param index The row to set the enabled status of.
/// \param enabled Indicate true to enable the row or false to disable it.
virtual void setRowEnabled(S32 index, bool enabled);
/// Gets the label displayed on the specified row.
///
/// \param rowIndex Index of the row to get the label of.
/// \return The label for the row.
virtual StringTableEntry getRowLabel(S32 rowIndex) const;
/// Sets the label on the given row.
///
/// \param rowIndex Index of the row to set the label on.
/// \param label Text to set as the label of the row.
virtual void setRowLabel(S32 rowIndex, const char * label);
/// Adds a row to the control.
///
/// \param label The text to display on the row as a label.
/// \param callback Name of a script function to use as a callback when this
/// row is activated.
/// \param icon [optional] Index of the icon to use as a marker. Default -1
/// means no icon will be shown on this row.
/// \param yPad [optional] An extra amount of height padding before the row.
/// \param enabled [optional] If this row is initially enabled. Default true.
virtual void addRow(const char* label, const char* callback, S32 icon = -1, S32 yPad = 0, bool useHighlightIcon = true, bool enabled = true);
/// Activates the current row. The script callback of the current row will
/// be called (if it has one).
virtual void activateRow();
/// Gets the number of rows in the control.
///
/// \return The number of rows in this control.
virtual S32 getRowCount() const { return mRows.size(); }
GuiGameListMenuCtrl();
~GuiGameListMenuCtrl();
void onRender(Point2I offset, const RectI &updateRect);
/// Callback when the object is registered with the sim.
///
/// \return True if the profile was successfully added, false otherwise.
bool onAdd();
/// Callback when the control wakes up.
bool onWake();
/// Callback when a key is pressed.
///
/// \param event The event that triggered this callback.
bool onKeyDown(const GuiEvent &event);
/// Callback when a key is repeating.
///
/// \param event The event that triggered this callback.
bool onKeyRepeat(const GuiEvent &event){ return onKeyDown(event); }
/// Callback when the mouse button is clicked on the control.
///
/// \param event A reference to the event that triggered the callback.
void onMouseDown(const GuiEvent &event);
/// Callback when the mouse is dragged on the control.
///
/// \param event A reference to the event that triggered the callback.
void onMouseDragged(const GuiEvent &event){ onMouseDown(event); }
/// Callback when the mouse leaves the control.
///
/// \param event A reference to the event that triggered the callback.
void onMouseLeave(const GuiEvent &event);
/// Callback when the mouse is moving over this control
///
/// \param event A reference to the event that triggered the callback.
void onMouseMove(const GuiEvent &event);
/// Callback when the mouse button is released.
///
/// \param event A reference to the event that triggered the callback.
void onMouseUp(const GuiEvent &event);
/// Callback when the gamepad axis is activated.
///
/// \param event A reference to the event that triggered the callback.
virtual bool onGamepadAxisUp(const GuiEvent & event);
/// Callback when the gamepad axis is activated.
///
/// \param event A reference to the event that triggered the callback.
virtual bool onGamepadAxisDown(const GuiEvent & event);
DECLARE_CONOBJECT(GuiGameListMenuCtrl);
DECLARE_CATEGORY( "Gui Game" );
DECLARE_DESCRIPTION( "Base class for cross platform menu controls that are gamepad friendly." );
/// Initializes fields accessible through the console.
static void initPersistFields();
static const S32 NO_ROW = -1; ///< Indicates a query result of no row found.
static const S32 NO_ICON = -1; ///< Indicates a row has no extra icon available
protected:
/// Adds a row to the control.
///
/// \param row A reference to the row object to fill.
/// \param label The text to display on the row as a label.
/// \param callback Name of a script function to use as a callback when this
/// row is activated.
/// \param icon [optional] Index of the icon to use as a marker. Default -1
/// means no icon will be shown on this row.
/// \param yPad [optional] An extra amount of height padding before the row.
/// \param enabled [optional] If this row is initially enabled. Default true.
virtual void addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled);
/// Determines if the given index is a valid row index. Any index pointing at
/// an existing row is valid.
///
/// \param index The index to check for validity.
/// \return True if the index points at a valid row, false otherwise.
virtual bool isValidRowIndex(S32 index) const;
/// Sets the script variable $ThisControl to reflect this control.
virtual void setThisControl();
/// Called to implement debug rendering which displays colored lines to
/// provide visual feedback on extents and hit zones.
virtual void onDebugRender(Point2I offset);
/// Looks up the row having a hit area at the given global point.
///
/// \param globalPoint The point we want to check for hitting a row.
/// \return The index of the hit row or NO_ROW if no row was hit.
virtual S32 getRow(Point2I globalPoint);
/// Checks to make sure our control has a profile of the correct type.
///
/// \return True if the profile is of type GuiGameListMenuProfile or false if
/// the profile is of any other type.
virtual bool hasValidProfile() const;
/// Enforces the validity of the fields on this control and its profile (if
/// the profile is valid, see: hasValidProfile).
virtual void enforceConstraints();
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onChange, () );
/// @}
/// Evaluates some script. If the command is empty then nothing is evaluated.
///
/// \param command The script to evaluate.
void doScriptCommand(StringTableEntry command);
StringTableEntry mCallbackOnA; ///< Script callback when the 'A' button is pressed
StringTableEntry mCallbackOnB; ///< Script callback when the 'B' button is pressed
StringTableEntry mCallbackOnX; ///< Script callback when the 'X' button is pressed
StringTableEntry mCallbackOnY; ///< Script callback when the 'Y' button is pressed
bool mDebugRender; ///< Determines when to show debug render lines
Vector<Row *> mRows; ///< Holds data wrappers on all the rows we have
private:
/// Recalculates the height of this control based on the stored row height and
/// and padding on the rows.
virtual Point2I getMinExtent() const;
/// Makes sure the height will allow all rows to be displayed without being
/// truncated.
void updateHeight();
/// Sets the first enabled row as selected. If there are no enabled rows then
/// selected will be set to NO_ROW.
void selectFirstEnabledRow();
/// Changes the currently selected row.
///
/// \param delta The amount to change the row selection by. Typically this will
/// be 1 or -1.
void changeRow(S32 delta);
S32 mSelected; ///< index of the currently selected row
S32 mHighlighted; ///< index of the currently highlighted row
};
/// \class GuiGameListMenuProfile
/// A gui profile with additional fields specific to GuiGameListMenuCtrl.
class GuiGameListMenuProfile : public GuiControlProfile
{
typedef GuiControlProfile Parent;
public:
/// Enforces range constraints on all required fields.
virtual void enforceConstraints();
/// Get the height of rows in this profile. All rows are considered to be the
/// same base height. Rows can have an extra amount of y padding defined when
/// they are added to the control.
///
/// \return The height of rows in this profile.
S32 getRowHeight() { return (mRowSize.y) ? mRowSize.y : getBitmapArrayRect(TEX_NORMAL).extent.y; }
/// Get the width of rows in this profile. All rows are considered to be the
/// same width.
///
/// \return The width of rows in this profile.
S32 getRowWidth() { return (mRowSize.x) ? mRowSize.x : getBitmapArrayRect(TEX_NORMAL).extent.x; }
/// Row scale is the ratio between the defined row size and the raw size of
/// the bitmap.
///
/// \return The row scale.
const Point2F & getRowScale() const { return mRowScale; }
/// Gets the extent of icons for this profile. If there are no icons you will
/// get a point of (0, 0);
///
/// \return The extent of icons or (0, 0) if there aren't any.
Point2I getIconExtent();
/// Gets the extent of arrows for this profile. If there are no arrows you
/// will get a point of (0, 0).
///
/// \return The extent of icons or (0, 0) if there aren't any.
Point2I getArrowExtent();
/// Gets the extent of the defined hit area for this profile. If the hit area
/// is not defined then it defaults to the full size of a row.
///
/// \return The extents of the defined hit area or the full size of the row.
Point2I getHitAreaExtent();
/// Determines if this profile has textures for the left and right arrows.
///
/// \return True if the profile's bitmap has textures for the arrows, false
/// otherwise.
bool hasArrows(){ return (! getBitmapArrayRect(TEX_FIRST_ARROW).extent.isZero()); }
/// Callback when the object is registered with the sim.
///
/// \return True if the profile was successfully added, false otherwise.
bool onAdd();
Point2I mHitAreaUpperLeft; ///< Offset for the upper left corner of the hit area
Point2I mHitAreaLowerRight; ///< Offset for the lower right corner of the hit area
Point2I mIconOffset; ///< Offset for a row's extra icon
Point2I mRowSize; ///< The base size of a row
GuiGameListMenuProfile();
DECLARE_CONOBJECT(GuiGameListMenuProfile);
/// Initializes fields accessible through the console.
static void initPersistFields();
enum
{
TEX_NORMAL = 0, ///< texture index for a normal, unselected row
TEX_SELECTED = 1, ///< texture index for a selected row
TEX_HIGHLIGHT = 2, ///< texture index for a highlighted row (moused over, not selected)
TEX_DISABLED = 3, ///< texture index for a disabled row
TEX_L_ARROW_OFF = 4, ///< texture index for the left arrow of an unselected row
TEX_L_ARROW_ON = 5, ///< texture index for the left arrow of a selected row
TEX_R_ARROW_OFF = 6, ///< texture index for the right arrow of an unselected row
TEX_R_ARROW_ON = 7, ///< texture index for the right arrow of a selected row
TEX_FIRST_ARROW = 4, ///< texture index for the first arrow
TEX_FIRST_ICON = 8, ///< texture index for the first row marker icon
};
private:
Point2F mRowScale; ///< Ratio of row size to actual bitmap size
};
#endif

View file

@ -0,0 +1,538 @@
//-----------------------------------------------------------------------------
// 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 "guiGameListOptionsCtrl.h"
#include "gfx/gfxDrawUtil.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "core/strings/stringUnit.h"
//-----------------------------------------------------------------------------
// GuiGameListOptionsCtrl
//-----------------------------------------------------------------------------
GuiGameListOptionsCtrl::GuiGameListOptionsCtrl()
{
}
GuiGameListOptionsCtrl::~GuiGameListOptionsCtrl()
{
}
bool GuiGameListOptionsCtrl::onAdd()
{
if( !Parent::onAdd() )
return false;
if( !hasValidProfile() )
{
GuiGameListOptionsProfile* profile;
if( !Sim::findObject( "DefaultOptionsMenuProfile", profile ) )
{
Con::errorf( "GuiGameListOptionsCtrl: %s can't be created with a profile of type %s. Please create it with a profile of type GuiGameListOptionsProfile.",
getName(), mProfile->getClassName() );
return false;
}
else
Con::warnf( "GuiGameListOptionsCtrl: substituted non-GuiGameListOptionsProfile in %s for DefaultOptionsMenuProfile", getName() );
setControlProfile( profile );
}
return true;
}
void GuiGameListOptionsCtrl::onRender(Point2I offset, const RectI &updateRect)
{
Parent::onRender(offset, updateRect);
GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile;
F32 xScale = (float) getWidth() / profile->getRowWidth();
S32 rowHeight = profile->getRowHeight();
bool profileHasArrows = profile->hasArrows();
Point2I arrowExtent;
S32 arrowOffsetY(0);
if (profileHasArrows)
{
arrowExtent = profile->getArrowExtent();
// icon is centered vertically
arrowOffsetY = (rowHeight - arrowExtent.y) >> 1;
}
GFXDrawUtil *drawer = GFX->getDrawUtil();
Point2I currentOffset = offset;
Point2I arrowOffset;
S32 columnSplit = profile->mColumnSplit * xScale;
S32 iconIndex;
for (Vector<Parent::Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
Row * myRow = (Row *) *row;
if (row != mRows.begin())
{
// rows other than the first can have padding above them
currentOffset.y += myRow->mHeightPad;
currentOffset.y += rowHeight;
}
bool hasOptions = (myRow->mOptions.size() > 0) && myRow->mSelectedOption > -1;
if (hasOptions)
{
bool isRowSelected = (getSelected() != NO_ROW) && (row == &mRows[getSelected()]);
bool isRowHighlighted = (getHighlighted() != NO_ROW) ? ((row == &mRows[getHighlighted()]) && ((*row)->mEnabled)) : false;
if (profileHasArrows)
{
// render the left arrow
bool arrowOnL = (isRowSelected || isRowHighlighted) && (myRow->mWrapOptions || (myRow->mSelectedOption > 0));
iconIndex = (arrowOnL) ? Profile::TEX_L_ARROW_ON : Profile::TEX_L_ARROW_OFF;
arrowOffset.x = currentOffset.x + columnSplit;
arrowOffset.y = currentOffset.y + arrowOffsetY;
drawer->clearBitmapModulation();
drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex));
// render the right arrow
bool arrowOnR = (isRowSelected || isRowHighlighted) && (myRow->mWrapOptions || (myRow->mSelectedOption < myRow->mOptions.size() - 1));
iconIndex = (arrowOnR) ? Profile::TEX_R_ARROW_ON : Profile::TEX_R_ARROW_OFF;
arrowOffset.x = currentOffset.x + (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale - arrowExtent.x;
arrowOffset.y = currentOffset.y + arrowOffsetY;
drawer->clearBitmapModulation();
drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex));
}
// get the appropriate font color
ColorI fontColor;
if (! myRow->mEnabled)
{
fontColor = profile->mFontColorNA;
}
else if (isRowSelected)
{
fontColor = profile->mFontColorSEL;
}
else if (isRowHighlighted)
{
fontColor = profile->mFontColorHL;
}
else
{
fontColor = profile->mFontColor;
}
// calculate text to be at the center between the arrows
GFont * font = profile->mFont;
StringTableEntry text = myRow->mOptions[myRow->mSelectedOption];
S32 textWidth = font->getStrWidth(text);
S32 columnWidth = profile->mHitAreaLowerRight.x * xScale - profile->mRightPad - columnSplit;
S32 columnCenter = columnSplit + (columnWidth >> 1);
S32 textStartX = columnCenter - (textWidth >> 1);
Point2I textOffset(textStartX, 0);
// render the option text itself
Point2I textExtent(columnWidth, rowHeight);
drawer->setBitmapModulation(fontColor);
renderJustifiedText(currentOffset + textOffset, textExtent, text);
}
}
}
void GuiGameListOptionsCtrl::onDebugRender(Point2I offset)
{
Parent::onDebugRender(offset);
GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile;
F32 xScale = (float) getWidth() / profile->getRowWidth();
ColorI column1Color(255, 255, 0); // yellow
ColorI column2Color(0, 255, 0); // green
Point2I shrinker(-1, -1);
S32 rowHeight = profile->getRowHeight();
Point2I currentOffset(offset);
Point2I rowExtent(getExtent().x, rowHeight);
Point2I hitAreaExtent(profile->getHitAreaExtent());
hitAreaExtent.x *= xScale;
hitAreaExtent += shrinker;
Point2I column1Extent((profile->mColumnSplit - profile->mHitAreaUpperLeft.x) * xScale - 1, hitAreaExtent.y);
Point2I column2Extent(hitAreaExtent.x - column1Extent.x - 1, hitAreaExtent.y);
Point2I hitAreaOffset = profile->mHitAreaUpperLeft;
hitAreaOffset.x *= xScale;
RectI borderRect;
Point2I upperLeft;
for (Vector<Parent::Row *>::iterator row = mRows.begin(); row < mRows.end(); ++row)
{
// set the top of the current row
if (row != mRows.begin())
{
// rows other than the first can have padding above them
currentOffset.y += (*row)->mHeightPad;
currentOffset.y += rowHeight;
}
// draw the box around column 1
upperLeft = currentOffset + hitAreaOffset;
borderRect.point = upperLeft;
borderRect.extent = column1Extent;
GFX->getDrawUtil()->drawRect(borderRect, column1Color);
// draw the box around column 2
upperLeft.x += column1Extent.x + 1;
borderRect.point = upperLeft;
borderRect.extent = column2Extent;
GFX->getDrawUtil()->drawRect(borderRect, column2Color);
}
}
void GuiGameListOptionsCtrl::addRow(const char* label, const char* optionsList, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled)
{
static StringTableEntry DELIM = StringTable->insert("\t", true);
Row * row = new Row();
Vector<StringTableEntry> options( __FILE__, __LINE__ );
S32 count = StringUnit::getUnitCount(optionsList, DELIM);
for (int i = 0; i < count; ++i)
{
const char * option = StringUnit::getUnit(optionsList, i, DELIM);
options.push_back(StringTable->insert(option, true));
}
row->mOptions = options;
bool hasOptions = row->mOptions.size() > 0;
row->mSelectedOption = (hasOptions) ? 0 : NO_OPTION;
row->mWrapOptions = wrapOptions;
Parent::addRow(row, label, callback, icon, yPad, true, (hasOptions) ? enabled : false);
}
void GuiGameListOptionsCtrl::setOptions(S32 rowIndex, const char * optionsList)
{
static StringTableEntry DELIM = StringTable->insert("\t", true);
if (! isValidRowIndex(rowIndex))
{
return;
}
Row * row = (Row *)mRows[rowIndex];
S32 count = StringUnit::getUnitCount(optionsList, DELIM);
row->mOptions.setSize(count);
for (int i = 0; i < count; ++i)
{
const char * option = StringUnit::getUnit(optionsList, i, DELIM);
row->mOptions[i] = StringTable->insert(option, true);
}
if (row->mSelectedOption >= row->mOptions.size())
{
row->mSelectedOption = row->mOptions.size() - 1;
}
}
bool GuiGameListOptionsCtrl::hasValidProfile() const
{
GuiGameListOptionsProfile * profile = dynamic_cast<GuiGameListOptionsProfile *>(mProfile);
return profile;
}
void GuiGameListOptionsCtrl::enforceConstraints()
{
Parent::enforceConstraints();
}
void GuiGameListOptionsCtrl::onMouseUp(const GuiEvent &event)
{
S32 hitRow = getRow(event.mousePoint);
if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected()))
{
S32 xPos = globalToLocalCoord(event.mousePoint).x;
clickOption((Row *) mRows[getSelected()], xPos);
}
}
void GuiGameListOptionsCtrl::clickOption(Row * row, S32 xPos)
{
GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile;
if (! profile->hasArrows())
{
return;
}
F32 xScale = (float) getWidth() / profile->getRowWidth();
S32 bitmapArrowWidth = mProfile->getBitmapArrayRect(Profile::TEX_FIRST_ARROW).extent.x;
S32 leftArrowX1 = profile->mColumnSplit * xScale;
S32 leftArrowX2 = leftArrowX1 + bitmapArrowWidth;
S32 rightArrowX2 = (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale;
S32 rightArrowX1 = rightArrowX2 - bitmapArrowWidth;
if ((leftArrowX1 <= xPos) && (xPos <= leftArrowX2))
{
changeOption(row, -1);
}
else if ((rightArrowX1 <= xPos) && (xPos <= rightArrowX2))
{
changeOption(row, 1);
}
}
bool GuiGameListOptionsCtrl::onKeyDown(const GuiEvent &event)
{
switch (event.keyCode)
{
case KEY_LEFT:
changeOption(-1);
return true;
case KEY_RIGHT:
changeOption(1);
return true;
default:
break;
}
return Parent::onKeyDown(event);
}
bool GuiGameListOptionsCtrl::onGamepadAxisLeft( const GuiEvent &event )
{
changeOption(-1);
return true;
}
bool GuiGameListOptionsCtrl::onGamepadAxisRight( const GuiEvent &event )
{
changeOption(1);
return true;
}
void GuiGameListOptionsCtrl::changeOption(S32 delta)
{
if (getSelected() != NO_ROW)
{
Row * row = (Row *) mRows[getSelected()];
changeOption(row, delta);
}
}
void GuiGameListOptionsCtrl::changeOption(Row * row, S32 delta)
{
S32 optionCount = row->mOptions.size();
S32 newSelection = row->mSelectedOption + delta;
if (optionCount == 0)
{
newSelection = NO_OPTION;
}
else if (! row->mWrapOptions)
{
newSelection = mClamp(newSelection, 0, optionCount - 1);
}
else if (newSelection < 0)
{
newSelection = optionCount - 1;
}
else if (newSelection >= optionCount)
{
newSelection = 0;
}
row->mSelectedOption = newSelection;
static StringTableEntry LEFT = StringTable->insert("LEFT", true);
static StringTableEntry RIGHT = StringTable->insert("RIGHT", true);
if (row->mScriptCallback != NULL)
{
setThisControl();
StringTableEntry direction = NULL;
if (delta < 0)
{
direction = LEFT;
}
else if (delta > 0)
{
direction = RIGHT;
}
if ((direction != NULL) && (Con::isFunction(row->mScriptCallback)))
{
Con::executef(row->mScriptCallback, direction);
}
}
}
StringTableEntry GuiGameListOptionsCtrl::getCurrentOption(S32 rowIndex) const
{
if (isValidRowIndex(rowIndex))
{
Row * row = (Row *) mRows[rowIndex];
if (row->mSelectedOption != NO_OPTION)
{
return row->mOptions[row->mSelectedOption];
}
}
return StringTable->insert("", false);
}
bool GuiGameListOptionsCtrl::selectOption(S32 rowIndex, const char * theOption)
{
if (! isValidRowIndex(rowIndex))
{
return false;
}
Row * row = (Row *) mRows[rowIndex];
for (Vector<StringTableEntry>::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption)
{
if (dStrcmp(*anOption, theOption) == 0)
{
S32 newIndex = anOption - row->mOptions.begin();
row->mSelectedOption = newIndex;
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListOptionsCtrl)
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiGameListOptionsCtrl);
ConsoleDocClass( GuiGameListOptionsCtrl,
"@brief A control for showing pages of options that are gamepad friendly.\n\n"
"Each row in this control allows the selection of one value from a set of "
"options using the keyboard, gamepad or mouse. The row is rendered as 2 "
"columns: the first column contains the row label, the second column "
"contains left and right arrows (for mouse picking) and the currently "
"selected value.\n\n"
"@see GuiGameListOptionsProfile\n\n"
"@ingroup GuiGame"
);
void GuiGameListOptionsCtrl::initPersistFields()
{
Parent::initPersistFields();
}
DefineEngineMethod( GuiGameListOptionsCtrl, addRow, void,
( const char* label, const char* options, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled ),
( -1, 0, true ),
"Add a row to the list control.\n\n"
"@param label The text to display on the row as a label.\n"
"@param options A tab separated list of options.\n"
"@param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping.\n"
"@param callback Name of a script function to use as a callback when this row is activated.\n"
"@param icon [optional] Index of the icon to use as a marker.\n"
"@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n"
"@param enabled [optional] If this row is initially enabled." )
{
object->addRow( label, options, wrapOptions, callback, icon, yPad, enabled );
}
DefineEngineMethod( GuiGameListOptionsCtrl, getCurrentOption, const char *, ( S32 row ),,
"Gets the text for the currently selected option of the given row.\n\n"
"@param row Index of the row to get the option from.\n"
"@return A string representing the text currently displayed as the selected option on the given row. If there is no such displayed text then the empty string is returned." )
{
return object->getCurrentOption( row );
}
DefineEngineMethod( GuiGameListOptionsCtrl, selectOption, bool, ( S32 row, const char* option ),,
"Set the row's current option to the one specified\n\n"
"@param row Index of the row to set an option on.\n"
"@param option The option to be made active.\n"
"@return True if the row contained the option and was set, false otherwise." )
{
return object->selectOption( row, option );
}
DefineEngineMethod( GuiGameListOptionsCtrl, setOptions, void, ( S32 row, const char* optionsList ),,
"Sets the list of options on the given row.\n\n"
"@param row Index of the row to set options on."
"@param optionsList A tab separated list of options for the control." )
{
object->setOptions( row, optionsList );
}
//-----------------------------------------------------------------------------
// GuiGameListOptionsProfile
//-----------------------------------------------------------------------------
GuiGameListOptionsProfile::GuiGameListOptionsProfile()
: mColumnSplit(0),
mRightPad(0)
{
}
void GuiGameListOptionsProfile::enforceConstraints()
{
Parent::enforceConstraints();
if( mHitAreaUpperLeft.x > mColumnSplit || mColumnSplit > mHitAreaLowerRight.x )
Con::errorf( "GuiGameListOptionsProfile: You can't create %s with a ColumnSplit outside the hit area. You set the split to %d. Please change the ColumnSplit to be in the range [%d, %d].",
getName(), mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x);
mColumnSplit = mClamp(mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x);
}
//-----------------------------------------------------------------------------
// Console stuff (GuiGameListOptionsProfile)
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(GuiGameListOptionsProfile);
ConsoleDocClass( GuiGameListOptionsProfile,
"@brief A GuiControlProfile with additional fields specific to GuiGameListOptionsCtrl.\n\n"
"@tsexample\n"
"new GuiGameListOptionsProfile()\n"
"{\n"
" columnSplit = \"100\";\n"
" rightPad = \"4\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiGame"
);
void GuiGameListOptionsProfile::initPersistFields()
{
addField( "columnSplit", TypeS32, Offset(mColumnSplit, GuiGameListOptionsProfile),
"Padding between the leftmost edge of the control, and the row's left arrow." );
addField( "rightPad", TypeS32, Offset(mRightPad, GuiGameListOptionsProfile),
"Padding between the rightmost edge of the control and the row's right arrow." );
Parent::initPersistFields();
}

View file

@ -0,0 +1,188 @@
//-----------------------------------------------------------------------------
// 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 _GuiGameListOptionsCtrl_H_
#define _GuiGameListOptionsCtrl_H_
#include "gui/controls/guiGameListMenuCtrl.h"
/// \class GuiGameListOptionsCtrl
/// A control for showing pages of options that are gamepad friendly.
class GuiGameListOptionsCtrl : public GuiGameListMenuCtrl
{
typedef GuiGameListMenuCtrl Parent;
protected:
/// \struct Row
/// An extension to the parent's row, adding the ability to keep a collection
/// of options and track status related to them.
struct Row : public Parent::Row
{
Vector<StringTableEntry> mOptions; ///< Collection of options available to display
S32 mSelectedOption; ///< Index into mOptions pointing at the selected option
bool mWrapOptions; ///< Determines if options should "wrap around" at the ends
Row()
{
VECTOR_SET_ASSOCIATION( mOptions );
}
};
public:
/// Gets the text for the currently selected option of the given row.
///
/// \param rowIndex Index of the row to get the option from.
/// \return A string representing the text currently displayed as the selected
/// option on the given row. If there is no such displayed text then the empty
/// string is returned.
StringTableEntry getCurrentOption(S32 rowIndex) const;
/// Attempts to set the given row to the specified selected option. The option
/// will only be set if the option exists in the control.
///
/// \param rowIndex Index of the row to set an option on.
/// \param option The option to be made active.
/// \return True if the row contained the option and was set, false otherwise.
bool selectOption(S32 rowIndex, StringTableEntry option);
/// Sets the list of options on the given row.
///
/// \param rowIndex Index of the row to set options on.
/// \param optionsList A tab separated list of options for the control.
void setOptions(S32 rowIndex, const char * optionsList);
/// Adds a row to the control.
///
/// \param label The text to display on the row as a label.
/// \param optionsList A tab separated list of options for the control.
/// \param wrapOptions Specify true to allow options to wrap at the ends or
/// false to prevent wrapping.
/// \param callback [optional] Name of a script function to use as a callback
/// when this row is activated. Default NULL means no callback.
/// \param icon [optional] Index of the icon to use as a marker. Default -1
/// means no icon will be shown on this row.
/// \param yPad [optional] An extra amount of height padding before the row.
/// \param enabled [optional] If this row is initially enabled. Default true.
void addRow(const char* label, const char* optionsList, bool wrapOptions, const char* callback, S32 icon = -1, S32 yPad = 0, bool enabled = true);
void onRender(Point2I offset, const RectI &updateRect);
/// Callback when the mouse button is released.
///
/// \param event A reference to the event that triggered the callback.
void onMouseUp(const GuiEvent &event);
/// Callback when a key is pressed.
///
/// \param event The event that triggered this callback.
bool onKeyDown(const GuiEvent &event);
/// Callback when a key is repeating.
///
/// \param event The event that triggered this callback.
bool onKeyRepeat(const GuiEvent &event){ return onKeyDown(event); }
/// Callback when the gamepad axis is activated.
///
/// \param event A reference to the event that triggered the callback.
virtual bool onGamepadAxisLeft(const GuiEvent &event);
/// Callback when the gamepad axis is activated.
///
/// \param event A reference to the event that triggered the callback.
virtual bool onGamepadAxisRight(const GuiEvent &event);
GuiGameListOptionsCtrl();
~GuiGameListOptionsCtrl();
DECLARE_CONOBJECT(GuiGameListOptionsCtrl);
DECLARE_DESCRIPTION( "A control for showing pages of options that are gamepad friendly." );
virtual bool onAdd();
/// Initializes fields accessible through the console.
static void initPersistFields();
static const S32 NO_OPTION = -1; ///< Indicates there is no option
protected:
/// Checks to make sure our control has a profile of the correct type.
///
/// \return True if the profile is of type GuiGameListOptionsProfile or false
/// if the profile is of any other type.
bool hasValidProfile() const;
/// Enforces the validity of the fields on this control and its profile (if the
/// profile is valid, see: hasValidProfile).
void enforceConstraints();
/// Adds lines around the column divisions to the feedback already provided
/// in the Parent.
void onDebugRender(Point2I offset);
private:
/// Performs a click on the current option row. The x position is used to
/// determine if the left or right arrow were clicked. If one was clicked, the
/// option will be changed. If neither was clicked, the option is unaffected.
/// This method should only be called when there is an actively selected row.
///
/// \param row The row to perform the click on.
/// \param xPos The x position of the the click, relative to the control.
void clickOption(Row * row, S32 xPos);
/// Changes the option on the currently selected row. If there is no row
/// selected, this method does nothing.
///
/// \param delta The amount to change the option selection by. Typically this
/// will be 1 or -1.
void changeOption(S32 delta);
/// Changes the option on the given row.
///
/// \param row The row to change the option on.
/// \param delta The amount to change the option selection by. Typically this
/// will be 1 or -1.
void changeOption(Row * row, S32 delta);
};
/// \class GuiGameListOptionsProfile
/// A gui profile with additional fields specific to GuiGameListOptionsCtrl.
class GuiGameListOptionsProfile : public GuiGameListMenuProfile
{
typedef GuiGameListMenuProfile Parent;
public:
/// Enforces range constraints on all required fields.
void enforceConstraints();
GuiGameListOptionsProfile();
S32 mColumnSplit; ///< Absolute position of the split between columns
S32 mRightPad; ///< Extra padding between the right arrow and the hit area
DECLARE_CONOBJECT(GuiGameListOptionsProfile);
/// Initializes fields accessible through the console.
static void initPersistFields();
};
#endif

View file

@ -0,0 +1,652 @@
//-----------------------------------------------------------------------------
// 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/console.h"
#include "gfx/gfxDevice.h"
#include "console/consoleTypes.h"
#include "gui/core/guiCanvas.h"
#include "gui/buttons/guiButtonCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiGradientCtrl.h"
#include "gui/controls/guiColorPicker.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
// GuiGradientSwatchCtrl
IMPLEMENT_CONOBJECT(GuiGradientSwatchCtrl);
ConsoleDocClass( GuiGradientSwatchCtrl,
"@brief Swatch selector that appears inside the GuiGradientCtrl object. These objects are automatically created by GuiGradientCtrl. \n\n"
"Currently only appears to be editor specific\n\n"
"@see GuiSwatchButtonCtrl\n"
"@see GuiGradientCtrl\n\n"
"@ingroup GuiCore\n"
"@internal"
);
IMPLEMENT_CALLBACK( GuiGradientSwatchCtrl, onMouseDown, void, (),(),
"@brief Called whenever the left mouse button has entered the down state while in this control.\n\n"
"@tsexample\n"
"// The left mouse button is down on the control, causing the callback to occur.\n"
"GuiGradientSwatchCtrl::onMouseDown(%this)\n"
" {\n"
" // Code to run when the callback occurs\n"
" }\n"
"@endtsexample\n\n"
"@see GuiControl\n"
"@see GuiSwatchButtonCtrl\n\n"
"@internal"
);
IMPLEMENT_CALLBACK( GuiGradientSwatchCtrl, onDoubleClick, void, (),(),
"@brief Called whenever the left mouse button performs a double click while in this control.\n\n"
"@tsexample\n"
"// The left mouse button has performed a double click on the control, causing the callback to occur.\n"
"GuiGradientSwatchCtrl::onDoubleClick(%this)\n"
" {\n"
" // Code to run when the callback occurs\n"
" }\n"
"@endtsexample\n\n"
"@see GuiControl\n"
"@see GuiSwatchButtonCtrl\n\n"
"@internal"
);
GuiGradientSwatchCtrl::GuiGradientSwatchCtrl()
{
setPosition(0, 0);
setExtent(14, 14);
mMouseDownPosition = Point2I(0, 0);
mSwatchColor = ColorI( 1, 1, 1, 1 );
mColorFunction = StringTable->insert("getColorF");
setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorSwatchButtonProfile" );
}
bool GuiGradientSwatchCtrl::onWake()
{
if ( !Parent::onWake() )
return false;
if ( mPointer.isNull() )
mPointer.set( "core/art/gui/images/arrowbtn_d", &GFXDefaultGUIProfile, avar("%s() - mGrid (line %d)", __FUNCTION__, __LINE__) );
char* altCommand = Con::getReturnBuffer(512);
dSprintf( altCommand, 512, "%s(%i.color, \"%i.setColor\");", mColorFunction, getId(), getId() );
setField( "altCommand", altCommand );
return true;
}
void GuiGradientSwatchCtrl::onRender( Point2I offset, const RectI &updateRect )
{
bool highlight = mMouseOver;
ColorI backColor = mSwatchColor;
ColorI borderColor = mActive ? ( highlight ? mProfile->mBorderColorHL : mProfile->mBorderColor ) : mProfile->mBorderColorNA;
RectI renderRect( offset, getExtent() );
if ( !highlight )
renderRect.inset( 1, 1 );
GFXDrawUtil *drawer = GFX->getDrawUtil();
drawer->clearBitmapModulation();
// Draw background transparency grid texture...
if ( mGrid.isValid() )
drawer->drawBitmapStretch( mGrid, renderRect );
// Draw swatch color as fill...
drawer->drawRectFill( renderRect, mSwatchColor );
// Draw any borders...
drawer->drawRect( renderRect, borderColor );
}
void GuiGradientSwatchCtrl::onMouseDown(const GuiEvent &event)
{
if (! mActive)
return;
if (mProfile->mCanKeyFocus)
setFirstResponder();
//capture current bounds and mouse down position
mOrigBounds = getBounds();
mMouseDownPosition = event.mousePoint;
if(mUseMouseEvents)
onMouseDown_callback();
//lock the mouse
mouseLock();
mDepressed = true;
// If we have a double click then execute the alt command.
if ( event.mouseClickCount == 2 )
{
onDoubleClick_callback();
execAltConsoleCallback();
}
setUpdate();
}
void GuiGradientSwatchCtrl::onMouseDragged(const GuiEvent &event)
{
//gradientCtrl owns the y, x here however is regulated by the extent currently
//dirty, please fix
GuiGradientCtrl* parent = dynamic_cast<GuiGradientCtrl*>(getParent());
if( !parent )
return;
//use bounds and delta to move the ctrl
Point2I newPosition = mMouseDownPosition;
Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
newPosition.x = mOrigBounds.point.x + deltaMousePosition.x;
// default position but it needs to be standard; currently using this cops out a static y value
newPosition.y = mOrigBounds.point.y;
if( newPosition.x + parent->mSwatchFactor >= parent->mBlendRangeBox.point.x &&
newPosition.x + parent->mSwatchFactor <= parent->mBlendRangeBox.extent.x )
{
setPosition(newPosition);
if( parent )
parent->sortColorRange();
}
}
void GuiGradientSwatchCtrl::onRightMouseDown(const GuiEvent &event)
{
GuiGradientCtrl* parent = dynamic_cast<GuiGradientCtrl*>(getParent());
if( parent )
parent->removeColorRange( this );
}
//-----------------------------------------------------------------------------
// GuiGradientCtrl
static S32 QSORT_CALLBACK _numIncreasing( const void* a, const void* b )
{
GuiGradientCtrl::ColorRange *crA = (GuiGradientCtrl::ColorRange *) (a);
GuiGradientCtrl::ColorRange *crB = (GuiGradientCtrl::ColorRange *) (b);
S32 posA = crA->swatch->getPosition().x;
S32 posB = crB->swatch->getPosition().x;
return ( (posA < posB) ? -1 : ((posA > posB) ? 1 : 0) );
}
ImplementEnumType( GuiGradientPickMode,
"\n\n"
"@ingroup GuiCore"
"@internal")
{ GuiGradientCtrl::pHorizColorRange, "HorizColor"},
{ GuiGradientCtrl::pHorizAlphaRange, "HorizAlpha"},
EndImplementEnumType;
IMPLEMENT_CONOBJECT(GuiGradientCtrl);
ConsoleDocClass( GuiGradientCtrl,
"@brief Visual representation of color box used with the GuiColorPickerCtrl\n\n"
"Editor use only.\n\n"
"@internal"
);
GuiGradientCtrl::GuiGradientCtrl()
{
setExtent(140, 30);
mDisplayMode = pHorizColorRange;
mSaveDisplayMode = pHorizColorRange;
mBaseColor = ColorF(1.,.0,1.);
mPickColor = ColorF(.0,.0,.0);
mMouseDown = mMouseOver = false;
mActive = true;
mPositionChanged = false;
mActionOnMove = false;
mShowReticle = true;
colorWhiteBlend = ColorF(1.,1.,1.,.75);
mSwatchFactor = 7;
}
//--------------------------------------------------------------------------
void GuiGradientCtrl::initPersistFields()
{
addGroup("ColorPicker");
addField("baseColor", TypeColorF, Offset(mBaseColor, GuiGradientCtrl));
addField("pickColor", TypeColorF, Offset(mPickColor, GuiGradientCtrl));
addField("displayMode", TYPEID< PickMode >(), Offset(mDisplayMode, GuiGradientCtrl) );
addField("actionOnMove", TypeBool,Offset(mActionOnMove, GuiGradientCtrl));
addField("showReticle", TypeBool, Offset(mShowReticle, GuiGradientCtrl));
addField("swatchFactor", TypeS32, Offset(mSwatchFactor, GuiGradientCtrl));
endGroup("ColorPicker");
Parent::initPersistFields();
}
bool GuiGradientCtrl::onAdd()
{
Parent::onAdd();
S32 l = getBounds().point.x + mSwatchFactor, r = getBounds().point.x + getBounds().extent.x - mSwatchFactor;
S32 t = getBounds().point.y, b = getBounds().point.y + getBounds().extent.y - mSwatchFactor;
mBlendRangeBox = RectI( Point2I(l, t), Point2I(r, b) );
setupDefaultRange();
reInitSwatches( mDisplayMode );
return true;
}
void GuiGradientCtrl::inspectPreApply()
{
mSaveDisplayMode = mDisplayMode;
}
void GuiGradientCtrl::inspectPostApply()
{
if((mSaveDisplayMode != mDisplayMode) )
reInitSwatches( mDisplayMode );
// Apply any transformations set in the editor
Parent::inspectPostApply();
}
void GuiGradientCtrl::onRender(Point2I offset, const RectI& updateRect)
{
if (mStateBlock.isNull())
{
GFXStateBlockDesc desc;
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
desc.setZReadWrite(false);
desc.zWriteEnable = false;
desc.setCullMode(GFXCullNone);
mStateBlock = GFX->createStateBlock( desc );
}
RectI boundsRect(offset, getExtent());
renderColorBox(boundsRect);
if (mPositionChanged)
{
mPositionChanged = false;
// Now do onAction() if we are allowed
if (mActionOnMove)
onAction();
}
//render the children
renderChildControls( offset, updateRect);
}
/// Function to invoke calls to draw the picker box and swatch controls
void GuiGradientCtrl::renderColorBox(RectI &bounds)
{
// Draw color box differently depending on mode
if( mDisplayMode == pHorizColorRange )
{
drawBlendRangeBox( bounds, false, mColorRange);
}
else if( mDisplayMode == pHorizAlphaRange )
{
drawBlendRangeBox( bounds, false, mAlphaRange);
}
}
/// Function to draw a set of boxes blending throughout an array of colors
void GuiGradientCtrl::drawBlendRangeBox(RectI &bounds, bool vertical, Vector<ColorRange> colorRange)
{
GFX->setStateBlock(mStateBlock);
// Create new global dimensions
S32 l = bounds.point.x + mSwatchFactor, r = bounds.point.x + bounds.extent.x - mSwatchFactor;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - mSwatchFactor;
// Draw border using new global dimensions
if (mProfile->mBorder)
GFX->getDrawUtil()->drawRect( RectI( Point2I(l,t),Point2I(r,b) ), mProfile->mBorderColor);
// Update local dimensions
mBlendRangeBox.point = globalToLocalCoord(Point2I(l, t));
mBlendRangeBox.extent = globalToLocalCoord(Point2I(r, b));
if(colorRange.size() == 1) // Only one color to draw
{
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( colorRange.first().swatch->getColor() );
PrimBuild::vertex2i( l, t );
PrimBuild::vertex2i( l, b );
PrimBuild::color( colorRange.first().swatch->getColor() );
PrimBuild::vertex2i( r, b );
PrimBuild::vertex2i( r, t );
PrimBuild::end();
}
else
{
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( colorRange.first().swatch->getColor() );
PrimBuild::vertex2i( l, t );
PrimBuild::vertex2i( l, b );
PrimBuild::color( colorRange.first().swatch->getColor() );
PrimBuild::vertex2i( l + colorRange.first().swatch->getPosition().x, b );
PrimBuild::vertex2i( l + colorRange.first().swatch->getPosition().x, t );
PrimBuild::end();
for( U16 i = 0;i < colorRange.size() - 1; i++ )
{
PrimBuild::begin( GFXTriangleFan, 4 );
if (!vertical) // Horizontal (+x)
{
// First color
PrimBuild::color( colorRange[i].swatch->getColor() );
PrimBuild::vertex2i( l + colorRange[i].swatch->getPosition().x, t );
PrimBuild::vertex2i( l + colorRange[i].swatch->getPosition().x, b );
// First color
PrimBuild::color( colorRange[i+1].swatch->getColor() );
PrimBuild::vertex2i( l + colorRange[i+1].swatch->getPosition().x, b );
PrimBuild::vertex2i( l + colorRange[i+1].swatch->getPosition().x, t );
}
PrimBuild::end();
}
PrimBuild::begin( GFXTriangleFan, 4 );
PrimBuild::color( colorRange.last().swatch->getColor() );
PrimBuild::vertex2i( l + colorRange.last().swatch->getPosition().x, t );
PrimBuild::vertex2i( l + colorRange.last().swatch->getPosition().x, b );
PrimBuild::color( colorRange.last().swatch->getColor() );
PrimBuild::vertex2i( r, b );
PrimBuild::vertex2i( r, t );
PrimBuild::end();
}
}
void GuiGradientCtrl::onMouseDown(const GuiEvent &event)
{
if (!mActive)
return;
mouseLock(this);
if (mProfile->mCanKeyFocus)
setFirstResponder();
if (mActive)
onAction();
Point2I extent = getRoot()->getExtent();
Point2I resolution = getRoot()->getExtent();
GFXTexHandle bb( resolution.x,
resolution.y,
GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) );
Point2I tmpPt( event.mousePoint.x, event.mousePoint.y );
GFXTarget *targ = GFX->getActiveRenderTarget();
targ->resolveTo( bb );
GBitmap bmp( bb.getWidth(), bb.getHeight() );
bb.copyToBmp( &bmp );
ColorI tmp;
bmp.getColor( event.mousePoint.x, event.mousePoint.y, tmp );
addColorRange( globalToLocalCoord(event.mousePoint), ColorF(tmp) );
mMouseDown = true;
}
void GuiGradientCtrl::onMouseUp(const GuiEvent &)
{
//if we released the mouse within this control, perform the action
if (mActive && mMouseDown )
mMouseDown = false;
mouseUnlock();
}
void GuiGradientCtrl::onMouseEnter(const GuiEvent &event)
{
mMouseOver = true;
}
void GuiGradientCtrl::onMouseLeave(const GuiEvent &)
{
// Reset state
mMouseOver = false;
}
void GuiGradientCtrl::setupDefaultRange()
{
S32 l = mBlendRangeBox.point.x - mSwatchFactor;
S32 r = mBlendRangeBox.extent.x - mSwatchFactor;
//setup alpha range (white/black only)
ColorRange crW;
crW.pos = l;
crW.color = ColorI(255,255,255);
crW.swatch = NULL;
mAlphaRange.push_back( crW );
ColorRange crB;
crB.pos = r;
crB.color = ColorI(0,0,0);
crB.swatch = NULL;
mAlphaRange.push_back( crB );
//setup color range (only 1 color necessary)
ColorRange crD;
crD.pos = l;
crD.color = ColorI(255,0,0);
crD.swatch = NULL;
mColorRange.push_back( crD );
}
void GuiGradientCtrl::reInitSwatches( GuiGradientCtrl::PickMode )
{
//liable to crash in the guiEditor, needs fix
for( S32 i = 0;i < mColorRange.size(); i++ )
{
if(mColorRange[i].swatch != NULL)
{
mColorRange[i].pos = mColorRange[i].swatch->getPosition().x;
mColorRange[i].color = mColorRange[i].swatch->getColor();
mColorRange[i].swatch->deleteObject();
mColorRange[i].swatch = NULL;
}
}
for( S32 i = 0;i < mAlphaRange.size(); i++ )
{
if(mAlphaRange[i].swatch != NULL)
{
mAlphaRange[i].pos = mAlphaRange[i].swatch->getPosition().x;
mAlphaRange[i].color = mAlphaRange[i].swatch->getColor();
mAlphaRange[i].swatch->deleteObject();
mAlphaRange[i].swatch = NULL;
}
}
S32 b = mBlendRangeBox.extent.y - mSwatchFactor;
if( mDisplayMode == pHorizColorRange )
{
for( S32 i = 0;i < mColorRange.size(); i++ )
{
mColorRange[i].swatch = new GuiGradientSwatchCtrl();
mColorRange[i].swatch->registerObject();
addObject(mColorRange[i].swatch);
mColorRange[i].swatch->setPosition( Point2I( mColorRange[i].pos, b ) );// needs to be adjusted
mColorRange[i].swatch->setColor(ColorF(mColorRange[i].color));
}
}
else if( mDisplayMode == pHorizAlphaRange )
{
for( S32 i = 0;i < mAlphaRange.size(); i++ )
{
mAlphaRange[i].swatch = new GuiGradientSwatchCtrl();
mAlphaRange[i].swatch->registerObject();
addObject(mAlphaRange[i].swatch);
mAlphaRange[i].swatch->setPosition( Point2I( mAlphaRange[i].pos, b ) );// needs to be adjusted
mAlphaRange[i].swatch->setColor(ColorF(mAlphaRange[i].color));
}
}
}
void GuiGradientCtrl::addColorRange( Point2I pos, ColorF color )
{
if( pos.x + mSwatchFactor < mBlendRangeBox.point.x &&
pos.x + mSwatchFactor > mBlendRangeBox.extent.x )
{
return;
}
ColorRange range;
range.pos = pos.x - mSwatchFactor;
range.color = color;
S32 b = mBlendRangeBox.extent.y - mSwatchFactor;
range.swatch = new GuiGradientSwatchCtrl();
range.swatch->registerObject();
addObject( range.swatch );
range.swatch->setPosition( pos.x - mSwatchFactor, b );//swatch factor and default location is going to have to be placed
range.swatch->setColor( color );
if( mDisplayMode == pHorizColorRange )
{
mColorRange.push_back( range );
S32 size = mColorRange.size();
if( size > 0 )
dQsort( mColorRange.address(), size, sizeof(ColorRange), _numIncreasing);
}
else if( mDisplayMode == pHorizAlphaRange )
{
mAlphaRange.push_back( range );
S32 size = mAlphaRange.size();
if( size > 0 )
dQsort( mAlphaRange.address(), size, sizeof(ColorRange), _numIncreasing);
}
}
void GuiGradientCtrl::removeColorRange( GuiGradientSwatchCtrl* swatch )
{
if( mDisplayMode == pHorizColorRange )
{
if( mColorRange.size() <= 1 )
return;
for( S32 i = 0;i < mColorRange.size(); i++ )
{
if( mColorRange[i].swatch == swatch )
{
mColorRange.erase( U32(i) );
swatch->safeDeleteObject();
break;
}
}
}
else if( mDisplayMode == pHorizAlphaRange )
{
if( mAlphaRange.size() <= 1 )
return;
for( S32 i = 0;i < mAlphaRange.size(); i++ )
{
if( mAlphaRange[i].swatch == swatch )
{
mAlphaRange.erase( U32(i) );
swatch->safeDeleteObject();
break;
}
}
}
}
void GuiGradientCtrl::sortColorRange()
{
if( mDisplayMode == pHorizColorRange )
dQsort( mColorRange.address(), mColorRange.size(), sizeof(ColorRange), _numIncreasing);
else if( mDisplayMode == pHorizAlphaRange )
dQsort( mAlphaRange.address(), mAlphaRange.size(), sizeof(ColorRange), _numIncreasing);
}
ConsoleMethod(GuiGradientCtrl, getColorCount, S32, 2, 2, "Get color count")
{
if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange )
return object->mColorRange.size();
else if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange )
return object->mColorRange.size();
return 0;
}
ConsoleMethod(GuiGradientCtrl, getColor, const char*, 3, 3, "Get color value")
{
S32 idx = dAtoi(argv[2]);
if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange )
{
if ( idx >= 0 && idx < object->mColorRange.size() )
{
char* rColor = Con::getReturnBuffer(256);
rColor[0] = 0;
dSprintf(rColor, 256, "%f %f %f %f",
object->mColorRange[idx].swatch->getColor().red,
object->mColorRange[idx].swatch->getColor().green,
object->mColorRange[idx].swatch->getColor().blue,
object->mColorRange[idx].swatch->getColor().alpha);
return rColor;
}
}
else if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange )
{
if ( idx >= 0 && idx < object->mAlphaRange.size() )
{
char* rColor = Con::getReturnBuffer(256);
rColor[0] = 0;
dSprintf(rColor, 256, "%f %f %f %f",
object->mAlphaRange[idx].swatch->getColor().red,
object->mAlphaRange[idx].swatch->getColor().green,
object->mAlphaRange[idx].swatch->getColor().blue,
object->mAlphaRange[idx].swatch->getColor().alpha);
return rColor;
}
}
return "1 1 1 1";
}

View file

@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUIGRADIENTCTRL_H_
#define _GUIGRADIENTCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUISWATCHBUTTONCTRL_H_
#include "gui/buttons/guiSwatchButtonCtrl.h"
#endif
class GuiGradientSwatchCtrl : public GuiSwatchButtonCtrl
{
private:
typedef GuiSwatchButtonCtrl Parent;
private:
Point2I mMouseDownPosition;
RectI mOrigBounds;
public:
DECLARE_CONOBJECT(GuiGradientSwatchCtrl);
DECLARE_CALLBACK( void, onMouseDown, ());
DECLARE_CALLBACK( void, onDoubleClick, ());
GuiGradientSwatchCtrl();
void onMouseDown(const GuiEvent &);
void onRightMouseDown(const GuiEvent &);
void onMouseDragged(const GuiEvent &event);
void onRender(Point2I offset, const RectI &updateRect);
bool onWake();
protected:
GFXTexHandle mPointer;
StringTableEntry mColorFunction;
};
//----------------------------
/// GuiGradientCtrl
class GuiGradientCtrl : public GuiControl
{
typedef GuiControl Parent;
public:
enum PickMode
{
pHorizColorRange, ///< We have a range of base colors going horizontally
pHorizAlphaRange, ///< We have a box which shows a range in alpha going horizontally
};
enum SelectorMode
{
sHorizontal = 0, ///< Horizontal selector with small gap
sVertical, ///< Vertical selector with small gap
};
struct ColorRange
{
GuiGradientSwatchCtrl* swatch;
S32 pos;
ColorF color;
};
Vector<ColorRange> mColorRange;
Vector<ColorRange> mAlphaRange;
S32 mSwatchFactor;
RectI mBlendRangeBox;
private:
/// @name Core Rendering functions
/// @{
void renderColorBox(RectI &bounds); ///< Function that draws the actual color box
//void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode); ///< Function that draws the selection indicator
void drawBlendRangeBox(RectI &bounds, bool vertical, Vector<ColorRange> colorRange);
/// @}
/// @name Core Variables
/// @{
ColorF mPickColor; ///< Color that has been picked from control
ColorF mBaseColor; ///< Colour we display (in case of pallet and blend mode)
PickMode mDisplayMode; ///< Current color display mode of the selector
PickMode mSaveDisplayMode;
bool mPositionChanged; ///< Current position has changed since last render?
bool mMouseOver; ///< Mouse is over?
bool mMouseDown; ///< Mouse button down?
bool mActionOnMove; ///< Perform onAction() when position has changed?
GFXStateBlockRef mStateBlock;
ColorF colorWhite;
ColorF colorWhiteBlend;
ColorF colorBlack;
ColorF colorAlpha;
ColorF colorAlphaW;
/// @}
String mColorFunction;
public:
DECLARE_CONOBJECT(GuiGradientCtrl);
DECLARE_CATEGORY( "Gui Editor" );
GuiGradientCtrl();
static void initPersistFields();
void onRender(Point2I offset, const RectI &updateRect);
bool mShowReticle; ///< Show reticle on render
/// @name Color Value Functions
/// @{
/// NOTE: setValue only sets baseColor, since setting pickColor wouldn't be useful
void setValue(ColorF &value) {mBaseColor = value;}
/// NOTE: getValue() returns baseColor if pallet (since pallet controls can't "pick" colours themselves)
ColorF getValue() {return mPickColor;}
void updateColor() {mPositionChanged = true;}
/// @}
/// @name Input Events
/// @{
void onMouseDown(const GuiEvent &);
void onMouseUp(const GuiEvent &);
void onMouseEnter(const GuiEvent &);
void onMouseLeave(const GuiEvent &);
/// @}
void addColorRange(ColorI color);
void setupDefaultRange();
bool onAdd();
void inspectPreApply();
void inspectPostApply();
void reInitSwatches( GuiGradientCtrl::PickMode );
void addColorRange( Point2I pos, ColorF color );
void removeColorRange( GuiGradientSwatchCtrl* swatch );
void sortColorRange();
PickMode getDisplayMode() { return mDisplayMode; }
};
typedef GuiGradientCtrl::PickMode GuiGradientPickMode;
DefineEnumType( GuiGradientPickMode );
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,155 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _GUI_LISTBOXCTRL_H_
#define _GUI_LISTBOXCTRL_H_
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _DGL_H_
#include "gfx/gfxDevice.h"
#endif
#ifndef _H_GUIDEFAULTCONTROLRENDER_
#include "gui/core/guiDefaultControlRender.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
class GuiListBoxCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
public:
GuiListBoxCtrl();
~GuiListBoxCtrl();
DECLARE_CONOBJECT(GuiListBoxCtrl);
DECLARE_CATEGORY( "Gui Lists" );
DECLARE_DESCRIPTION( "Linear list of text items." );
DECLARE_CALLBACK( void, onMouseDragged, ());
DECLARE_CALLBACK( void, onClearSelection, ());
DECLARE_CALLBACK( void, onUnSelect, ( const char* index, const char* itemText));
DECLARE_CALLBACK( void, onSelect, ( const char* index , const char* itemText ));
DECLARE_CALLBACK( void, onDoubleClick, ());
DECLARE_CALLBACK( void, onMouseUp, (const char* itemHit, const char* mouseClickCount));
DECLARE_CALLBACK( void, onDeleteKey, ());
DECLARE_CALLBACK( bool, isObjectMirrored, ( const char* indexIdString ));
struct LBItem
{
StringTableEntry itemText;
String itemTooltip;
bool isSelected;
void* itemData;
ColorF color;
bool hasColor;
};
VectorPtr<LBItem*> mItems;
VectorPtr<LBItem*> mSelectedItems;
VectorPtr<LBItem*> mFilteredItems;
bool mMultipleSelections;
Point2I mItemSize;
bool mFitParentWidth;
bool mColorBullet;
LBItem* mLastClickItem;
// Persistence
static void initPersistFields();
// Item Accessors
S32 getItemCount();
S32 getSelCount();
S32 getSelectedItem();
void getSelectedItems( Vector<S32> &Items );
S32 getItemIndex( LBItem *item );
StringTableEntry getItemText( S32 index );
SimObject* getItemObject( S32 index );
void setCurSel( S32 index );
void setCurSelRange( S32 start, S32 stop );
void setItemText( S32 index, StringTableEntry text );
S32 addItem( StringTableEntry text, void *itemData = NULL );
S32 addItemWithColor( StringTableEntry text, ColorF color = ColorF(-1, -1, -1), void *itemData = NULL);
S32 insertItem( S32 index, StringTableEntry text, void *itemData = NULL );
S32 insertItemWithColor( S32 index, StringTableEntry text, ColorF color = ColorF(-1, -1, -1), void *itemData = NULL);
S32 findItemText( StringTableEntry text, bool caseSensitive = false );
void setItemColor(S32 index, ColorF color);
void clearItemColor(S32 index);
void deleteItem( S32 index );
void clearItems();
void clearSelection();
void removeSelection( LBItem *item, S32 index );
void removeSelection( S32 index );
void addSelection( LBItem *item, S32 index );
void addSelection( S32 index );
inline void setMultipleSelection( bool allowMultipleSelect = true ) { mMultipleSelections = allowMultipleSelect; };
bool hitTest( const Point2I& point, S32& outItem );
// Sizing
void updateSize();
virtual void parentResized(const RectI& oldParentRect, const RectI& newParentRect);
virtual bool onWake();
// Rendering
virtual void onRender( Point2I offset, const RectI &updateRect );
virtual void onRenderItem( RectI itemRect, LBItem *item );
void drawBox( const Point2I &box, S32 size, ColorI &outlineColor, ColorI &boxColor );
bool renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText );
void addFilteredItem( String item );
void removeFilteredItem( String item );
// Mouse/Key Events
virtual void onMouseDown( const GuiEvent &event );
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp( const GuiEvent& event );
virtual bool onKeyDown( const GuiEvent &event );
// String Utility
static U32 getStringElementCount( const char *string );
static const char* getStringElement( const char* inString, const U32 index );
// SimSet Mirroring Stuff
void setMirrorObject( SimSet *inObj );
void _mirror();
StringTableEntry _makeMirrorItemName( SimObject *inObj );
String mMirrorSetName;
String mMakeNameCallback;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,305 @@
//-----------------------------------------------------------------------------
// 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 _GUIMLTEXTCTRL_H_
#define _GUIMLTEXTCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _STRINGBUFFER_H_
#include "core/stringBuffer.h"
#endif
class GFont;
class SFXTrack;
GFX_DeclareTextureProfile(GFXMLTextureProfile);
class GuiMLTextCtrl : public GuiControl
{
typedef GuiControl Parent;
//-------------------------------------- Public interfaces...
public:
enum Justification
{
LeftJustify,
RightJustify,
CenterJustify,
};
struct Font {
char *faceName;
U32 faceNameLen;
U32 size;
Resource<GFont> fontRes;
Font *next;
};
struct Bitmap {
char bitmapName[1024];
U32 bitmapNameLen;
GFXTexHandle bitmapObject;
Bitmap *next;
};
struct URL
{
bool mouseDown;
U32 textStart;
U32 len;
bool noUnderline;
};
struct Style
{
ColorI color;
ColorI shadowColor;
ColorI linkColor;
ColorI linkColorHL;
Point2I shadowOffset;
Font *font;
bool used;
Style *next;
};
struct Atom
{
U32 textStart;
U32 len;
U32 xStart;
U32 yStart;
U32 width;
U32 baseLine;
U32 descent;
Style *style;
bool isClipped;
URL *url;
Atom *next;
};
struct Line {
U32 y;
U32 height;
U32 divStyle;
U32 textStart;
U32 len;
Atom *atomList;
Line *next;
};
struct BitmapRef : public RectI
{
BitmapRef *nextBlocker;
U32 textStart;
U32 len;
Bitmap *bitmap;
BitmapRef *next;
};
struct LineTag {
U32 id;
S32 y;
LineTag *next;
};
GuiMLTextCtrl();
~GuiMLTextCtrl();
DECLARE_CALLBACK( void, onURL, (const char* url));
DECLARE_CALLBACK( void, onResize, ( const char* width, const char* maxY ));
// Text retrieval functions
U32 getNumChars() const;
U32 getText(char* pBuffer, const U32 bufferSize) const;
U32 getWrappedText(char* pBuffer, const U32 bufferSize) const;
const char* getTextContent();
void insertChars(const char* inputChars,
const U32 numInputChars,
const U32 position);
// Text substitution functions
void setText(const char* textBuffer, const U32 numChars);
void addText(const char* textBuffer, const U32 numChars, bool reformat);
void setAlpha(F32 alpha) { mAlpha = alpha;}
bool setCursorPosition(const S32);
void ensureCursorOnScreen();
// Scroll functions
void scrollToTag( U32 id );
void scrollToTop();
void scrollToBottom();
virtual void reflow();
DECLARE_CONOBJECT(GuiMLTextCtrl);
DECLARE_CATEGORY( "Gui Text" );
DECLARE_DESCRIPTION( "A control that displays multiple lines of text." );
static void initPersistFields();
void setScriptValue(const char *value);
const char *getScriptValue();
static char *stripControlChars(const char *inString);
//-------------------------------------- Protected Structures and constants
protected:
bool mIsEditCtrl;
U32 *mTabStops;
U32 mTabStopCount;
U32 mCurTabStop;
F32 mAlpha;
DataChunker mViewChunker;
DataChunker mResourceChunker;
Line *mLineList;
Bitmap *mBitmapList;
BitmapRef *mBitmapRefList;
Font *mFontList;
LineTag *mTagList;
bool mDirty;
Style *mCurStyle;
U32 mCurLMargin;
U32 mCurRMargin;
U32 mCurJustify;
U32 mCurDiv;
U32 mCurY;
U32 mCurClipX;
Atom *mLineAtoms;
Atom **mLineAtomPtr;
Atom *mEmitAtoms;
Atom **mEmitAtomPtr;
BitmapRef mSentinel;
Line **mLineInsert;
BitmapRef *mBlockList;
U32 mScanPos;
U32 mCurX;
U32 mMaxY;
URL *mCurURL;
URL *mHitURL;
void freeLineBuffers();
void freeResources();
Bitmap *allocBitmap(const char *bitmapName, U32 bitmapNameLen);
Font *allocFont(const char *faceName, U32 faceNameLen, U32 size);
LineTag *allocLineTag(U32 id);
void emitNewLine(U32 textStart);
Atom *buildTextAtom(U32 start, U32 len, U32 left, U32 right, URL *url);
void emitTextToken(U32 textStart, U32 len);
void emitBitmapToken(Bitmap *bmp, U32 textStart, bool bitmapBreak);
void processEmitAtoms();
Atom *splitAtomListEmit(Atom *list, U32 width);
void drawAtomText(bool sel, U32 start, U32 end, Atom *atom, Line *line, Point2I offset);
Atom *findHitAtom(const Point2I localCoords);
Style *allocStyle(Style *style);
static const U32 csmTextBufferGrowthSize;
//-------------------------------------- Data...
protected:
// Cursor position should always be <= mCurrTextSize
U32 mCursorPosition;
// Actual text data. The line buffer is rebuilt from the linear text
// given a specific width. TextBuffer is /not/ \0 terminated
StringBuffer mTextBuffer;
U32 mLineStart;
S32 mMaxBufferSize;
StringTableEntry mInitialText;
// Selection information
bool mSelectionActive;
U32 mSelectionStart;
U32 mSelectionEnd;
U32 mVertMoveAnchor;
bool mVertMoveAnchorValid;
S32 mSelectionAnchor;
Point2I mSelectionAnchorDropped;
// Font resource
Resource<GFont> mFont;
// Console settable parameters
U32 mLineSpacingPixels;
bool mAllowColorChars;
bool mUseURLMouseCursor;
// Too many chars sound:
SFXTrack* mDeniedSound;
//-------------------------------------- Protected interface
protected:
// Inserting and deleting character blocks...
void deleteChars(const U32 rangeStart,
const U32 rangeEnd);
void copyToClipboard(const U32 rangeStart,
const U32 rangeEnd);
// Selection maintainence
bool isSelectionActive() const;
void clearSelection();
// Pixel -> text position mappings
S32 getTextPosition(const Point2I& localPosition);
// Gui control overrides
bool onWake();
void onSleep();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void getCursorPositionAndColor(Point2I &cursorTop, Point2I &cursorBottom, ColorI &color);
void inspectPostApply();
void parentResized(const RectI& oldParentRect, const RectI& newParentRect);
bool onKeyDown(const GuiEvent& event);
void onMouseDown(const GuiEvent&);
void onMouseDragged(const GuiEvent&);
void onMouseUp(const GuiEvent&);
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
public:
// Gui control overrides
bool onAdd();
void setSelectionStart( U32 start ) { clearSelection(); mSelectionStart = start; };
void setSelectionEnd( U32 end ) { mSelectionEnd = end;};
void setSelectionActive(bool active) { mSelectionActive = active; };
S32 getCursorPosition() { return( mCursorPosition ); }
virtual bool resize(const Point2I &newPosition, const Point2I &newExtent);
};
#endif // _H_GUIMLTEXTCTRL_

View file

@ -0,0 +1,468 @@
//-----------------------------------------------------------------------------
// 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/controls/guiMLTextEditCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gui/core/guiCanvas.h"
#include "console/consoleTypes.h"
#include "platform/event.h"
#include "core/frameAllocator.h"
#include "core/stringBuffer.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl);
ConsoleDocClass( GuiMLTextEditCtrl,
"@brief A text entry control that accepts the Gui Markup Language ('ML') tags and multiple lines.\n\n"
"@tsexample\n"
"new GuiMLTextEditCtrl()\n"
" {\n"
" lineSpacing = \"2\";\n"
" allowColorChars = \"0\";\n"
" maxChars = \"-1\";\n"
" deniedSound = \"DeniedSoundProfile\";\n"
" text = \"\";\n"
" escapeCommand = \"onEscapeScriptFunction();\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
" };\n"
"@endtsexample\n\n"
"@see GuiMLTextCtrl\n"
"@see GuiControl\n\n"
"@ingroup GuiControls\n"
);
//--------------------------------------------------------------------------
GuiMLTextEditCtrl::GuiMLTextEditCtrl()
{
mEscapeCommand = StringTable->insert( "" );
mIsEditCtrl = true;
mActive = true;
mVertMoveAnchorValid = false;
}
//--------------------------------------------------------------------------
GuiMLTextEditCtrl::~GuiMLTextEditCtrl()
{
}
//--------------------------------------------------------------------------
bool GuiMLTextEditCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
{
// We don't want to get any smaller than our parent:
Point2I newExt = newExtent;
GuiControl* parent = getParent();
if ( parent )
newExt.y = getMax( parent->getHeight(), newExt.y );
return Parent::resize( newPosition, newExt );
}
//--------------------------------------------------------------------------
void GuiMLTextEditCtrl::initPersistFields()
{
addField( "escapeCommand", TypeString, Offset( mEscapeCommand, GuiMLTextEditCtrl ), "Script function to run whenever the 'escape' key is pressed when this control is in focus.\n");
Parent::initPersistFields();
}
//--------------------------------------------------------------------------
void GuiMLTextEditCtrl::setFirstResponder()
{
Parent::setFirstResponder();
GuiCanvas *root = getRoot();
if (root != NULL)
{
root->enableKeyboardTranslation();
// If the native OS accelerator keys are not disabled
// then some key events like Delete, ctrl+V, etc may
// not make it down to us.
root->setNativeAcceleratorsEnabled( false );
}
}
void GuiMLTextEditCtrl::onLoseFirstResponder()
{
GuiCanvas *root = getRoot();
if (root != NULL)
{
root->setNativeAcceleratorsEnabled( true );
root->disableKeyboardTranslation();
}
// Redraw the control:
setUpdate();
}
//--------------------------------------------------------------------------
bool GuiMLTextEditCtrl::onWake()
{
if( !Parent::onWake() )
return false;
getRoot()->enableKeyboardTranslation();
return true;
}
//--------------------------------------------------------------------------
// Key events...
bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event)
{
if ( !isActive() )
return false;
setUpdate();
//handle modifiers first...
if (event.modifier & SI_PRIMARY_CTRL)
{
switch(event.keyCode)
{
//copy/cut
case KEY_C:
case KEY_X:
{
//make sure we actually have something selected
if (mSelectionActive)
{
copyToClipboard(mSelectionStart, mSelectionEnd);
//if we're cutting, also delete the selection
if (event.keyCode == KEY_X)
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd);
mCursorPosition = mSelectionStart;
}
else
mCursorPosition = mSelectionEnd + 1;
}
return true;
}
//paste
case KEY_V:
{
const char *clipBuf = Platform::getClipboard();
if (dStrlen(clipBuf) > 0)
{
// Normal ascii keypress. Go ahead and add the chars...
if (mSelectionActive == true)
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd);
mCursorPosition = mSelectionStart;
}
insertChars(clipBuf, dStrlen(clipBuf), mCursorPosition);
}
return true;
}
default:
break;
}
}
else if ( event.modifier & SI_SHIFT )
{
switch ( event.keyCode )
{
case KEY_TAB:
return( Parent::onKeyDown( event ) );
default:
break;
}
}
else if ( event.modifier == 0 )
{
switch (event.keyCode)
{
// Escape:
case KEY_ESCAPE:
if ( mEscapeCommand[0] )
{
Con::evaluate( mEscapeCommand );
return( true );
}
return( Parent::onKeyDown( event ) );
// Deletion
case KEY_BACKSPACE:
case KEY_DELETE:
handleDeleteKeys(event);
return true;
// Cursor movement
case KEY_LEFT:
case KEY_RIGHT:
case KEY_UP:
case KEY_DOWN:
case KEY_HOME:
case KEY_END:
handleMoveKeys(event);
return true;
// Special chars...
case KEY_TAB:
// insert 3 spaces
if (mSelectionActive == true)
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd);
mCursorPosition = mSelectionStart;
}
insertChars( "\t", 1, mCursorPosition );
return true;
case KEY_RETURN:
// insert carriage return
if (mSelectionActive == true)
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd);
mCursorPosition = mSelectionStart;
}
insertChars( "\n", 1, mCursorPosition );
return true;
default:
break;
}
}
if ( (mFont && mFont->isValidChar(event.ascii)) || (!mFont && event.ascii != 0) )
{
// Normal ascii keypress. Go ahead and add the chars...
if (mSelectionActive == true)
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd);
mCursorPosition = mSelectionStart;
}
UTF8 *outString = NULL;
U32 outStringLen = 0;
#ifdef TORQUE_UNICODE
UTF16 inData[2] = { event.ascii, 0 };
StringBuffer inBuff(inData);
FrameTemp<UTF8> outBuff(4);
inBuff.getCopy8(outBuff, 4);
outString = outBuff;
outStringLen = dStrlen(outBuff);
#else
char ascii = char(event.ascii);
outString = &ascii;
outStringLen = 1;
#endif
insertChars(outString, outStringLen, mCursorPosition);
mVertMoveAnchorValid = false;
return true;
}
// Otherwise, let the parent have the event...
return Parent::onKeyDown(event);
}
//--------------------------------------
void GuiMLTextEditCtrl::handleDeleteKeys(const GuiEvent& event)
{
if ( isSelectionActive() )
{
mSelectionActive = false;
deleteChars(mSelectionStart, mSelectionEnd+1);
mCursorPosition = mSelectionStart;
}
else
{
switch ( event.keyCode )
{
case KEY_BACKSPACE:
if (mCursorPosition != 0)
{
// delete one character left
deleteChars(mCursorPosition-1, mCursorPosition);
setUpdate();
}
break;
case KEY_DELETE:
if (mCursorPosition != mTextBuffer.length())
{
// delete one character right
deleteChars(mCursorPosition, mCursorPosition+1);
setUpdate();
}
break;
default:
AssertFatal(false, "Unknown key code received!");
}
}
}
//--------------------------------------
void GuiMLTextEditCtrl::handleMoveKeys(const GuiEvent& event)
{
if ( event.modifier & SI_SHIFT )
return;
mSelectionActive = false;
switch ( event.keyCode )
{
case KEY_LEFT:
mVertMoveAnchorValid = false;
// move one left
if ( mCursorPosition != 0 )
{
mCursorPosition--;
setUpdate();
}
break;
case KEY_RIGHT:
mVertMoveAnchorValid = false;
// move one right
if ( mCursorPosition != mTextBuffer.length() )
{
mCursorPosition++;
setUpdate();
}
break;
case KEY_UP:
case KEY_DOWN:
{
Line* walk;
for ( walk = mLineList; walk->next; walk = walk->next )
{
if ( mCursorPosition <= ( walk->textStart + walk->len ) )
break;
}
if ( !walk )
return;
if ( event.keyCode == KEY_UP )
{
if ( walk == mLineList )
return;
}
else if ( walk->next == NULL )
return;
Point2I newPos;
newPos.set( 0, walk->y );
// Find the x-position:
if ( !mVertMoveAnchorValid )
{
Point2I cursorTopP, cursorBottomP;
ColorI color;
getCursorPositionAndColor(cursorTopP, cursorBottomP, color);
mVertMoveAnchor = cursorTopP.x;
mVertMoveAnchorValid = true;
}
newPos.x = mVertMoveAnchor;
// Set the new y-position:
if (event.keyCode == KEY_UP)
newPos.y--;
else
newPos.y += (walk->height + 1);
if (setCursorPosition(getTextPosition(newPos)))
mVertMoveAnchorValid = false;
break;
}
case KEY_HOME:
case KEY_END:
{
mVertMoveAnchorValid = false;
Line* walk;
for (walk = mLineList; walk->next; walk = walk->next)
{
if (mCursorPosition <= (walk->textStart + walk->len))
break;
}
if (walk)
{
if (event.keyCode == KEY_HOME)
{
//place the cursor at the beginning of the first atom if there is one
if (walk->atomList)
mCursorPosition = walk->atomList->textStart;
else
mCursorPosition = walk->textStart;
}
else
{
mCursorPosition = walk->textStart;
mCursorPosition += walk->len;
}
setUpdate();
}
break;
}
default:
AssertFatal(false, "Unknown move key code was received!");
}
ensureCursorOnScreen();
}
//--------------------------------------------------------------------------
void GuiMLTextEditCtrl::onRender(Point2I offset, const RectI& updateRect)
{
Parent::onRender(offset, updateRect);
// We are the first responder, draw our cursor in the appropriate position...
if (isFirstResponder())
{
Point2I top, bottom;
ColorI color;
getCursorPositionAndColor(top, bottom, color);
GFX->getDrawUtil()->drawLine(top + offset, bottom + offset, mProfile->mCursorColor);
}
}

View file

@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// 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 _GUIMLTEXTEDITCTRL_H_
#define _GUIMLTEXTEDITCTRL_H_
#ifndef _GUIMLTEXTCTRL_H_
#include "gui/controls/guiMLTextCtrl.h"
#endif
class GuiMLTextEditCtrl : public GuiMLTextCtrl
{
typedef GuiMLTextCtrl Parent;
//-------------------------------------- Overrides
protected:
StringTableEntry mEscapeCommand;
// Events
bool onKeyDown(const GuiEvent&event);
// Event forwards
void handleMoveKeys(const GuiEvent&);
void handleDeleteKeys(const GuiEvent&);
// rendering
void onRender(Point2I offset, const RectI &updateRect);
public:
GuiMLTextEditCtrl();
~GuiMLTextEditCtrl();
virtual void setFirstResponder();
virtual void onLoseFirstResponder();
bool onWake();
bool resize(const Point2I &newPosition, const Point2I &newExtent);
DECLARE_CONOBJECT(GuiMLTextEditCtrl);
DECLARE_DESCRIPTION( "A control that allows to edit multiple lines of text." );
static void initPersistFields();
};
#endif // _H_GUIMLTEXTEDITCTRL_

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// 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/controls/guiMaterialCtrl.h"
#include "materials/baseMatInstance.h"
#include "materials/materialManager.h"
#include "materials/sceneData.h"
#include "core/util/safeDelete.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDevice.h"
#include "math/util/matrixSet.h"
#include "scene/sceneRenderState.h"
IMPLEMENT_CONOBJECT( GuiMaterialCtrl );
ConsoleDocClass( GuiMaterialCtrl,
"@brief Container for GuiMaterialPreview\n\n"
"Editor use only.\n\n"
"@internal"
);
GuiMaterialCtrl::GuiMaterialCtrl()
: mMaterialInst( NULL )
{
}
void GuiMaterialCtrl::initPersistFields()
{
addGroup( "Material" );
addProtectedField( "materialName", TypeStringFilename, Offset( mMaterialName, GuiMaterialCtrl ), &GuiMaterialCtrl::_setMaterial, &defaultProtectedGetFn, "" );
endGroup( "Material" );
Parent::initPersistFields();
}
bool GuiMaterialCtrl::onWake()
{
if ( !Parent::onWake() )
return false;
setActive( true );
setMaterial( mMaterialName );
return true;
}
void GuiMaterialCtrl::onSleep()
{
SAFE_DELETE( mMaterialInst );
Parent::onSleep();
}
bool GuiMaterialCtrl::_setMaterial( void *object, const char *index, const char *data )
{
static_cast<GuiMaterialCtrl *>( object )->setMaterial( data );
// Return false to keep the caller from setting the field.
return false;
}
bool GuiMaterialCtrl::setMaterial( const String &materialName )
{
SAFE_DELETE( mMaterialInst );
mMaterialName = materialName;
if ( mMaterialName.isNotEmpty() && isAwake() )
mMaterialInst = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat<GFXVertexPCT>() );
return true;
}
void GuiMaterialCtrl::inspectPostApply()
{
Parent::inspectPostApply();
}
void GuiMaterialCtrl::onRender( Point2I offset, const RectI &updateRect )
{
Parent::onRender( offset, updateRect );
if ( !mMaterialInst )
return;
// Draw a quad with the material assigned
GFXVertexBufferHandle<GFXVertexPCT> verts( GFX, 4, GFXBufferTypeVolatile );
verts.lock();
F32 screenLeft = updateRect.point.x;
F32 screenRight = (updateRect.point.x + updateRect.extent.x);
F32 screenTop = updateRect.point.y;
F32 screenBottom = (updateRect.point.y + updateRect.extent.y);
const F32 fillConv = GFX->getFillConventionOffset();
verts[0].point.set( screenLeft - fillConv, screenTop - fillConv, 0.f );
verts[1].point.set( screenRight - fillConv, screenTop - fillConv, 0.f );
verts[2].point.set( screenLeft - fillConv, screenBottom - fillConv, 0.f );
verts[3].point.set( screenRight - fillConv, screenBottom - fillConv, 0.f );
verts[0].color = verts[1].color = verts[2].color = verts[3].color = ColorI( 255, 255, 255, 255 );
verts[0].texCoord.set( 0.0f, 0.0f );
verts[1].texCoord.set( 1.0f, 0.0f );
verts[2].texCoord.set( 0.0f, 1.0f );
verts[3].texCoord.set( 1.0f, 1.0f );
verts.unlock();
GFX->setVertexBuffer( verts );
MatrixSet matSet;
matSet.setWorld(GFX->getWorldMatrix());
matSet.setView(GFX->getViewMatrix());
matSet.setProjection(GFX->getProjectionMatrix());
MatrixF cameraMatrix( true );
F32 left, right, top, bottom, nearPlane, farPlane;
bool isOrtho;
GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho );
Frustum frust( isOrtho, left, right, top, bottom, nearPlane, farPlane, cameraMatrix );
SceneRenderState state
(
gClientSceneGraph,
SPT_Diffuse,
SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ),
gClientSceneGraph->getDefaultRenderPass(),
false
);
SceneData sgData;
sgData.init( &state );
sgData.wireframe = false; // Don't wireframe this.
while( mMaterialInst->setupPass( &state, sgData ) )
{
mMaterialInst->setSceneInfo( &state, sgData );
mMaterialInst->setTransforms( matSet, &state );
GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
}
// Clean up
GFX->setShader( NULL );
GFX->setTexture( 0, NULL );
}
ConsoleMethod( GuiMaterialCtrl, setMaterial, bool, 3, 3, "( string materialName )"
"Set the material to be displayed in the control." )
{
return object->setMaterial( argv[2] );
}

View file

@ -0,0 +1,67 @@
//-----------------------------------------------------------------------------
// 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 _GUIMATERIALCTRL_H_
#define _GUIMATERIALCTRL_H_
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
class BaseMatInstance;
///
class GuiMaterialCtrl : public GuiContainer
{
private:
typedef GuiContainer Parent;
protected:
String mMaterialName;
BaseMatInstance *mMaterialInst;
static bool _setMaterial( void *object, const char *index, const char *data );
public:
GuiMaterialCtrl();
// ConsoleObject
static void initPersistFields();
void inspectPostApply();
DECLARE_CONOBJECT(GuiMaterialCtrl);
DECLARE_CATEGORY( "Gui Editor" );
// GuiControl
bool onWake();
void onSleep();
bool setMaterial( const String &materialName );
void onRender( Point2I offset, const RectI &updateRect );
};
#endif // _GUIMATERIALCTRL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,174 @@
//-----------------------------------------------------------------------------
// 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 _GUIPOPUPCTRL_H_
#define _GUIPOPUPCTRL_H_
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
#ifndef _GUITEXTLISTCTRL_H_
#include "gui/controls/guiTextListCtrl.h"
#endif
#ifndef _GUIBUTTONCTRL_H_
#include "gui/buttons/guiButtonCtrl.h"
#endif
#ifndef _GUIBACKGROUNDCTRL_H_
#include "gui/controls/guiBackgroundCtrl.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
class GuiPopUpMenuCtrl;
class GuiPopupTextListCtrl;
class GuiPopUpBackgroundCtrl : public GuiControl
{
protected:
GuiPopUpMenuCtrl *mPopUpCtrl;
GuiPopupTextListCtrl *mTextList;
public:
GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopupTextListCtrl* textList);
void onMouseDown(const GuiEvent &event);
};
class GuiPopupTextListCtrl : public GuiTextListCtrl
{
private:
typedef GuiTextListCtrl Parent;
protected:
GuiPopUpMenuCtrl *mPopUpCtrl;
public:
GuiPopupTextListCtrl(); // for inheritance
GuiPopupTextListCtrl(GuiPopUpMenuCtrl *ctrl);
// GuiArrayCtrl overload:
void onCellSelected(Point2I cell);
// GuiControl overloads:
bool onKeyDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
class GuiPopUpMenuCtrl : public GuiTextCtrl
{
typedef GuiTextCtrl Parent;
public:
struct Entry
{
char buf[256];
S32 id;
U16 ascii;
U16 scheme;
bool usesColorBox; // Added
ColorI colorbox; // Added
};
struct Scheme
{
U32 id;
ColorI fontColor;
ColorI fontColorHL;
ColorI fontColorSEL;
};
bool mBackgroundCancel; // Added
protected:
GuiPopupTextListCtrl *mTl;
GuiScrollCtrl *mSc;
GuiPopUpBackgroundCtrl *mBackground;
Vector<Entry> mEntries;
Vector<Scheme> mSchemes;
S32 mSelIndex;
S32 mMaxPopupHeight;
F32 mIncValue;
F32 mScrollCount;
S32 mLastYvalue;
GuiEvent mEventSave;
S32 mRevNum;
bool mInAction;
bool mReplaceText;
bool mMouseOver; // Added
bool mRenderScrollInNA; // Added
bool mReverseTextList; // Added - Should we reverse the text list if we display up?
StringTableEntry mBitmapName; // Added
Point2I mBitmapBounds; // Added
GFXTexHandle mTextureNormal; // Added
GFXTexHandle mTextureDepressed; // Added
S32 mIdMax;
virtual void addChildren();
virtual void repositionPopup();
public:
GuiPopUpMenuCtrl(void);
~GuiPopUpMenuCtrl();
GuiScrollCtrl::Region mScrollDir;
bool onWake(); // Added
bool onAdd();
void onSleep();
void setBitmap(const char *name); // Added
void sort();
void sortID(); // Added
void addEntry(const char *buf, S32 id = -1, U32 scheme = 0);
void addScheme(U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL);
void onRender(Point2I offset, const RectI &updateRect);
void onAction();
virtual void closePopUp();
void clear();
void clearEntry( S32 entry ); // Added
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event); // Added
void onMouseLeave(const GuiEvent &); // Added
void setupAutoScroll(const GuiEvent &event);
void autoScroll();
bool onKeyDown(const GuiEvent &event);
void reverseTextList();
bool getFontColor(ColorI &fontColor, S32 id, bool selected, bool mouseOver);
bool getColoredBox(ColorI &boxColor, S32 id); // Added
bool setEntryText( S32 id, const char* buf );
S32 getSelected();
void setSelected(S32 id, bool bNotifyScript = true);
void setFirstSelected(bool bNotifyScript = true); // Added
void setNoneSelected(); // Added
const char *getScriptValue();
const char *getTextById(S32 id);
S32 findText( const char* text );
S32 getNumEntries() { return( mEntries.size() ); }
void replaceText(S32);
DECLARE_CONOBJECT( GuiPopUpMenuCtrl );
DECLARE_CATEGORY( "Gui Lists" );
DECLARE_DESCRIPTION( "A control that allows to select a value from a drop-down list." );
static void initPersistFields(void);
};
#endif //_GUI_POPUPMENU_CTRL_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// 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 _GUIPOPUPCTRLEX_H_
#define _GUIPOPUPCTRLEX_H_
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
#ifndef _GUITEXTLISTCTRL_H_
#include "gui/controls/guiTextListCtrl.h"
#endif
#ifndef _GUIBUTTONCTRL_H_
#include "gui/buttons/guiButtonCtrl.h"
#endif
#ifndef _GUISCROLLCTRL_H_
#include "gui/containers/guiScrollCtrl.h"
#endif
class GuiPopUpMenuCtrlEx;
class GuiPopupTextListCtrlEx;
class GuiPopUpBackgroundCtrlEx : public GuiControl
{
protected:
GuiPopUpMenuCtrlEx *mPopUpCtrl;
GuiPopupTextListCtrlEx *mTextList;
public:
GuiPopUpBackgroundCtrlEx(GuiPopUpMenuCtrlEx *ctrl, GuiPopupTextListCtrlEx* textList);
void onMouseDown(const GuiEvent &event);
};
class GuiPopupTextListCtrlEx : public GuiTextListCtrl
{
private:
typedef GuiTextListCtrl Parent;
bool hasCategories();
protected:
GuiPopUpMenuCtrlEx *mPopUpCtrl;
public:
GuiPopupTextListCtrlEx(); // for inheritance
GuiPopupTextListCtrlEx(GuiPopUpMenuCtrlEx *ctrl);
// GuiArrayCtrl overload:
void onCellSelected(Point2I cell);
// GuiControl overloads:
bool onKeyDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
};
class GuiPopUpMenuCtrlEx : public GuiTextCtrl
{
typedef GuiTextCtrl Parent;
public:
struct Entry
{
char buf[256];
S32 id;
U16 ascii;
U16 scheme;
bool usesColorBox; // Added
ColorI colorbox; // Added
};
struct Scheme
{
U32 id;
ColorI fontColor;
ColorI fontColorHL;
ColorI fontColorSEL;
};
bool mBackgroundCancel; // Added
protected:
GuiPopupTextListCtrlEx *mTl;
GuiScrollCtrl *mSc;
GuiPopUpBackgroundCtrlEx *mBackground;
Vector<Entry> mEntries;
Vector<Scheme> mSchemes;
S32 mSelIndex;
S32 mMaxPopupHeight;
F32 mIncValue;
F32 mScrollCount;
S32 mLastYvalue;
GuiEvent mEventSave;
S32 mRevNum;
bool mInAction;
bool mReplaceText;
bool mMouseOver; // Added
bool mRenderScrollInNA; // Added
bool mReverseTextList; // Added - Should we reverse the text list if we display up?
bool mHotTrackItems;
StringTableEntry mBitmapName; // Added
Point2I mBitmapBounds; // Added
GFXTexHandle mTextureNormal; // Added
GFXTexHandle mTextureDepressed; // Added
S32 mIdMax;
virtual void addChildren();
virtual void repositionPopup();
public:
GuiPopUpMenuCtrlEx(void);
~GuiPopUpMenuCtrlEx();
GuiScrollCtrl::Region mScrollDir;
bool onWake(); // Added
bool onAdd();
void onSleep();
void setBitmap(const char *name); // Added
void sort();
void sortID(); // Added
void addEntry(const char *buf, S32 id = -1, U32 scheme = 0);
void addScheme(U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL);
void onRender(Point2I offset, const RectI &updateRect);
void onAction();
virtual void closePopUp();
void clear();
void clearEntry( S32 entry ); // Added
void onMouseDown(const GuiEvent &event);
void onMouseUp(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event); // Added
void onMouseLeave(const GuiEvent &); // Added
void setupAutoScroll(const GuiEvent &event);
void autoScroll();
bool onKeyDown(const GuiEvent &event);
void reverseTextList();
bool getFontColor(ColorI &fontColor, S32 id, bool selected, bool mouseOver);
bool getColoredBox(ColorI &boxColor, S32 id); // Added
S32 getSelected();
void setSelected(S32 id, bool bNotifyScript = true);
void setFirstSelected(bool bNotifyScript = true); // Added
void setNoneSelected(); // Added
const char *getScriptValue();
const char *getTextById(S32 id);
S32 findText( const char* text );
S32 getNumEntries() { return( mEntries.size() ); }
void replaceText(S32);
DECLARE_CONOBJECT(GuiPopUpMenuCtrlEx);
DECLARE_CATEGORY( "Gui Lists" );
DECLARE_DESCRIPTION( "A control that allows to select a value from a drop-down list." );
static void initPersistFields(void);
};
#endif //_GUIIMPROVEDPOPUPCTRL_H_

View file

@ -0,0 +1,584 @@
//-----------------------------------------------------------------------------
// 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/console.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/gfxTextureManager.h"
#include "gui/controls/guiSliderCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "platform/event.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "sfx/sfxSystem.h"
#include "sfx/sfxTrack.h"
IMPLEMENT_CONOBJECT( GuiSliderCtrl );
ConsoleDocClass( GuiSliderCtrl,
"@brief A control that displays a value between its minimal and maximal bounds using a slider placed on a vertical "
"or horizontal axis.\n\n"
"A slider displays a value and allows that value to be changed by dragging a thumb control along the axis of the "
"slider. In this way, the value is changed between its allowed minimum and maximum.\n\n"
"To hook up script code to the value changes of a slider, use the #command and #altCommand properties. #command is "
"executed once the thumb is released by the user whereas #altCommand is called any time the slider value changes. "
"When changing the slider value from script, however, trigger of #altCommand is suppressed by default.\n\n"
"The orientation of a slider is automatically determined from the ratio of its width to its height. If a slider is "
"taller than it is wide, it will be rendered with a vertical orientation. If it is wider than it is tall, it will be "
"rendered with a horizontal orientation.\n\n"
"The rendering of a slider depends on the bitmap in the slider's profile. This bitmap must be a bitmap array comprised "
"of at least five bitmap rectangles. The rectangles are used such that:\n\n"
"- Rectangle #1: Left edge of slider\n"
"- Rectangle #2: Center piece of slider; this is stretched between the left and right edge\n"
"- Rectangle #3: Right edge of slider\n"
"- Rectangle #4: Thumb button in normal state\n"
"- Rectangle #5: Thumb button in highlighted (mouse-over) state\n\n"
"@tsexample\n"
"// Create a sound source and a slider that changes the volume of the source.\n"
"\n"
"%source = sfxPlayOnce( \"art/sound/testing\", AudioLoop2D );\n"
"\n"
"new GuiSlider()\n"
"{\n"
" // Update the sound source volume when the slider is being dragged and released.\n"
" command = %source @ \".setVolume( $ThisControl.value );\";\n"
"\n"
" // Limit the range to 0..1 since that is the allowable range for sound volumes.\n"
" range = \"0 1\";\n"
"};\n"
"@endtsexample\n\n"
"@see GuiTextEditSliderCtrl\n"
"@see GuiTextEditSliderBitmapCtrl\n\n"
"@ingroup GuiValues"
);
IMPLEMENT_CALLBACK( GuiSliderCtrl, onMouseDragged, void, (), (),
"Called when the left mouse button is dragged across the slider." );
//----------------------------------------------------------------------------
GuiSliderCtrl::GuiSliderCtrl()
: mRange( 0., 1.f ),
mTicks( 10 ),
mSnap( false ),
mValue( 0.5f ),
mThumbSize( 8, 20 ),
mShiftPoint( 5 ),
mShiftExtent( 10 ),
mIncAmount( 0.f ),
mDisplayValue( false ),
mMouseOver( false ),
mMouseDragged( false ),
mDepressed( false )
{
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::initPersistFields()
{
addGroup( "Slider" );
addField( "range", TypePoint2F, Offset( mRange, GuiSliderCtrl ),
"Min and max values corresponding to left and right slider position." );
addField( "ticks", TypeS32, Offset( mTicks, GuiSliderCtrl ),
"Spacing between tick marks in pixels. 0=off." );
addField( "snap", TypeBool, Offset( mSnap, GuiSliderCtrl ),
"Whether to snap the slider to tick marks." );
addProtectedField( "value", TypeF32, Offset( mValue, GuiSliderCtrl ),
_setValue, defaultProtectedGetFn,
"The value corresponding to the current slider position." );
endGroup( "Slider" );
Parent::initPersistFields();
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::setValue(F32 val, bool doCallback)
{
_updateThumb( val, mSnap, false, doCallback );
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::setActive( bool value )
{
if( !value && mDepressed )
{
// We're in the middle of a drag. Finish it here as once we've
// been deactivated, we are not going to see a mouse-up event.
mDepressed = false;
mouseUnlock();
execConsoleCallback();
}
Parent::setActive( value );
}
//----------------------------------------------------------------------------
bool GuiSliderCtrl::onWake()
{
if( !Parent::onWake() )
return false;
mHasTexture = mProfile->constructBitmapArray() >= NumBitmaps;
if( mHasTexture )
{
mBitmapBounds = mProfile->mBitmapArrayRects.address();
mThumbSize = Point2I( mBitmapBounds[ SliderButtonNormal ].extent.x, mBitmapBounds[ SliderButtonNormal ].extent.y );
}
F32 value;
if( mConsoleVariable[ 0 ] )
value = getFloatVariable();
else
value = mValue;
mValue = mClampF( value, mRange.x, mRange.y );
// mouse scroll increment percentage is 5% of the range
mIncAmount = ( ( mRange.y - mRange.x ) * 0.05 );
if( ( mThumbSize.y + mProfile->mFont->getHeight() - 4 ) <= getExtent().y )
mDisplayValue = true;
else
mDisplayValue = false;
_updateThumb( mValue, mSnap, true );
return true;
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return;
mouseLock();
setFirstResponder();
mDepressed = true;
Point2I curMousePos = globalToLocalCoord( event.mousePoint );
F32 value;
if (getWidth() >= getHeight())
value = F32(curMousePos.x-mShiftPoint) / F32(getWidth()-mShiftExtent)*(mRange.y-mRange.x) + mRange.x;
else
value = F32(curMousePos.y) / F32(getHeight())*(mRange.y-mRange.x) + mRange.x;
_updateThumb( value, mSnap || ( event.modifier & SI_SHIFT ) );
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseDragged( const GuiEvent &event )
{
if ( !mActive || !mAwake || !mVisible )
return;
mMouseDragged = true;
F32 value = _getThumbValue( event );
_updateThumb( value, mSnap || ( event.modifier & SI_SHIFT ) );
onMouseDragged_callback();
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseUp( const GuiEvent& event )
{
if ( !mActive || !mAwake || !mVisible )
return;
mouseUnlock();
mDepressed = false;
mMouseDragged = false;
_updateThumb( _getThumbValue( event ), event.modifier & SI_SHIFT );
execConsoleCallback();
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseEnter(const GuiEvent &event)
{
setUpdate();
if( isMouseLocked() )
{
mDepressed = true;
mMouseOver = true;
}
else
{
if( mActive && mProfile->mSoundButtonOver )
{
//F32 pan = (F32(event.mousePoint.x)/F32(getRoot()->getWidth())*2.0f-1.0f)*0.8f;
SFX->playOnce( mProfile->mSoundButtonOver );
}
mMouseOver = true;
}
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onMouseLeave(const GuiEvent &)
{
setUpdate();
if( isMouseLocked() )
mDepressed = false;
mMouseOver = false;
}
//----------------------------------------------------------------------------
bool GuiSliderCtrl::onMouseWheelUp(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelUp(event);
_updateThumb( mValue + mIncAmount, ( event.modifier & SI_SHIFT ) );
execConsoleCallback();
return true;
}
//----------------------------------------------------------------------------
bool GuiSliderCtrl::onMouseWheelDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelUp(event);
_updateThumb( mValue - mIncAmount, ( event.modifier & SI_SHIFT ) );
execConsoleCallback();
return true;
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::_updateThumb( F32 _value, bool snap, bool onWake, bool doCallback )
{
if( snap && mTicks > 0 )
{
// If the shift key is held, snap to the nearest tick, if any are being drawn
F32 tickStep = (mRange.y - mRange.x) / F32(mTicks + 1);
F32 tickSteps = (_value - mRange.x) / tickStep;
S32 actualTick = S32(tickSteps + 0.5);
_value = actualTick * tickStep + mRange.x;
}
// Clamp the thumb to legal values.
if( _value < mRange.x )
_value = mRange.x;
if( _value > mRange.y )
_value = mRange.y;
// If value hasn't changed and this isn't the initial update on
// waking, do nothing.
if( mValue == _value && !onWake )
return;
mValue = _value;
Point2I ext = getExtent();
ext.x -= ( mShiftExtent + mThumbSize.x ) / 2;
// update the bounding thumb rect
if (getWidth() >= getHeight())
{ // HORZ thumb
S32 mx = (S32)((F32(ext.x) * (mValue-mRange.x) / (mRange.y-mRange.x)));
S32 my = ext.y/2;
if(mDisplayValue)
my = mThumbSize.y/2;
mThumb.point.x = mx - (mThumbSize.x/2);
mThumb.point.y = my - (mThumbSize.y/2);
mThumb.extent = mThumbSize;
}
else
{ // VERT thumb
S32 mx = ext.x/2;
S32 my = (S32)((F32(ext.y) * (mValue-mRange.x) / (mRange.y-mRange.x)));
mThumb.point.x = mx - (mThumbSize.y/2);
mThumb.point.y = my - (mThumbSize.x/2);
mThumb.extent.x = mThumbSize.y;
mThumb.extent.y = mThumbSize.x;
}
setFloatVariable(mValue);
setUpdate();
// Use the alt console command if you want to continually update:
if ( !onWake && doCallback )
execAltConsoleCallback();
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::onRender(Point2I offset, const RectI &updateRect)
{
Point2I pos(offset.x+mShiftPoint, offset.y);
Point2I ext(getWidth() - mShiftExtent, getHeight());
RectI thumb = mThumb;
if( mHasTexture )
{
if(mTicks > 0)
{
// TODO: tick marks should be positioned based on the bitmap dimensions.
Point2I mid(ext.x, ext.y/2);
Point2I oldpos = pos;
pos += Point2I(1, 0);
PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f );
PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 );
// tick marks
for (U32 t = 0; t <= (mTicks+1); t++)
{
S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x;
S32 y = pos.y + mid.y;
PrimBuild::vertex2i(x, y + mShiftPoint);
PrimBuild::vertex2i(x, y + mShiftPoint*2 + 2);
}
PrimBuild::end();
// TODO: it would be nice, if the primitive builder were a little smarter,
// so that we could change colors midstream.
PrimBuild::color4f(0.9f, 0.9f, 0.9f, 1.0f);
PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 );
// tick marks
for (U32 t = 0; t <= (mTicks+1); t++)
{
S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x + 1;
S32 y = pos.y + mid.y + 1;
PrimBuild::vertex2i(x, y + mShiftPoint );
PrimBuild::vertex2i(x, y + mShiftPoint * 2 + 3);
}
PrimBuild::end();
pos = oldpos;
}
S32 index = SliderButtonNormal;
if(mMouseOver)
index = SliderButtonHighlight;
GFX->getDrawUtil()->clearBitmapModulation();
//left border
GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, Point2I(offset.x,offset.y), mBitmapBounds[SliderLineLeft]);
//right border
GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[SliderLineRight].extent.x, offset.y), mBitmapBounds[SliderLineRight]);
//draw our center piece to our slider control's border and stretch it
RectI destRect;
destRect.point.x = offset.x + mBitmapBounds[SliderLineLeft].extent.x;
destRect.extent.x = getWidth() - mBitmapBounds[SliderLineLeft].extent.x - mBitmapBounds[SliderLineRight].extent.x;
destRect.point.y = offset.y;
destRect.extent.y = mBitmapBounds[SliderLineCenter].extent.y;
RectI stretchRect;
stretchRect = mBitmapBounds[SliderLineCenter];
stretchRect.inset(1,0);
GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, destRect, stretchRect);
//draw our control slider button
thumb.point += pos;
GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject,Point2I(thumb.point.x,offset.y ),mBitmapBounds[index]);
}
else if (getWidth() >= getHeight())
{
Point2I mid(ext.x, ext.y/2);
if(mDisplayValue)
mid.set(ext.x, mThumbSize.y/2);
PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f );
PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 + 2);
// horz rule
PrimBuild::vertex2i( pos.x, pos.y + mid.y );
PrimBuild::vertex2i( pos.x + mid.x, pos.y + mid.y );
// tick marks
for( U32 t = 0; t <= ( mTicks + 1 ); t++ )
{
S32 x = (S32)( F32( mid.x - 1 ) / F32( mTicks + 1 ) * F32( t ) );
PrimBuild::vertex2i( pos.x + x, pos.y + mid.y - mShiftPoint );
PrimBuild::vertex2i( pos.x + x, pos.y + mid.y + mShiftPoint );
}
PrimBuild::end();
}
else
{
Point2I mid(ext.x/2, ext.y);
PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f );
PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 + 2);
// horz rule
PrimBuild::vertex2i( pos.x + mid.x, pos.y );
PrimBuild::vertex2i( pos.x + mid.x, pos.y + mid.y );
// tick marks
for( U32 t = 0; t <= ( mTicks + 1 ); t++ )
{
S32 y = (S32)( F32( mid.y - 1 ) / F32( mTicks + 1 ) * F32( t ) );
PrimBuild::vertex2i( pos.x + mid.x - mShiftPoint, pos.y + y );
PrimBuild::vertex2i( pos.x + mid.x + mShiftPoint, pos.y + y );
}
PrimBuild::end();
mDisplayValue = false;
}
// draw the thumb
thumb.point += pos;
renderRaisedBox(thumb, mProfile);
if(mDisplayValue)
{
char buf[20];
dSprintf(buf,sizeof(buf),"%0.3f",mValue);
Point2I textStart = thumb.point;
S32 txt_w = mProfile->mFont->getStrWidth((const UTF8 *)buf);
textStart.x += (S32)((thumb.extent.x/2.0f));
textStart.y += thumb.extent.y - 2; //19
textStart.x -= (txt_w/2);
if(textStart.x < offset.x)
textStart.x = offset.x;
else if(textStart.x + txt_w > offset.x+getWidth())
textStart.x -=((textStart.x + txt_w) - (offset.x+getWidth()));
GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor);
GFX->getDrawUtil()->drawText(mProfile->mFont, textStart, buf, mProfile->mFontColors);
}
renderChildControls(offset, updateRect);
}
//----------------------------------------------------------------------------
bool GuiSliderCtrl::resize( const Point2I& newPosition, const Point2I& newSize )
{
if( !Parent::resize( newPosition, newSize ) )
return false;
_updateThumb( mValue, false, true, false );
return true;
}
//----------------------------------------------------------------------------
void GuiSliderCtrl::parentResized( const RectI& oldParentRect, const RectI& newParentRect )
{
Parent::parentResized( oldParentRect, newParentRect );
_updateThumb( mValue, false, true, false );
}
//----------------------------------------------------------------------------
F32 GuiSliderCtrl::_getThumbValue( const GuiEvent& event )
{
Point2I curMousePos = globalToLocalCoord( event.mousePoint );
F32 value;
if( getWidth() >= getHeight() )
value = F32( curMousePos.x - mShiftPoint ) / F32( getWidth() - mShiftExtent ) * ( mRange.y - mRange.x ) + mRange.x;
else
value = F32( curMousePos.y ) / F32( getHeight() ) * ( mRange.y - mRange.x ) + mRange.x;
if(value > mRange.y )
value = mRange.y;
else if( value < mRange.x )
value = mRange.x;
if( mSnap || ( event.modifier & SI_SHIFT && mTicks >= 1 ) )
{
// If the shift key is held, snap to the nearest tick, if any are being drawn
F32 tickStep = ( mRange.y - mRange.x ) / F32( mTicks + 1 );
F32 tickSteps = (value - mRange.x ) / tickStep;
S32 actualTick = S32( tickSteps + 0.5 );
value = actualTick * tickStep + mRange.x;
AssertFatal( value <= mRange.y && value >= mRange.x, "Error, out of bounds value generated from shift-snap of slider" );
}
return value;
}
//=============================================================================
// Console Methods.
//=============================================================================
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiSliderCtrl, getValue, F32, (),,
"Get the current value of the slider based on the position of the thumb.\n"
"@return Slider position (from range.x to range.y)." )
{
return object->getValue();
}
//----------------------------------------------------------------------------
DefineEngineMethod( GuiSliderCtrl, setValue, void, ( F32 pos, bool doCallback ), ( false ),
"Set position of the thumb on the slider.\n"
"@param pos New slider position (from range.x to range.y)\n"
"@param doCallback If true, the altCommand callback will be invoked\n" )
{
object->setValue( pos, doCallback );
}
//----------------------------------------------------------------------------
DefineEngineMethod( GuiSliderCtrl, isThumbBeingDragged, bool, (),,
"Returns true if the thumb is currently being dragged by the user. This method is mainly useful "
"for scrubbing type sliders where the slider position is sync'd to a changing value. When the "
"user is dragging the thumb, however, the sync'ing should pause and not get in the way of the user." )
{
return object->isThumbBeingDragged();
}

View file

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// 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 _GUISLIDERCTRL_H_
#define _GUISLIDERCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
/// A slider control that selects out of a floating-point value range.
class GuiSliderCtrl : public GuiControl
{
public:
typedef GuiControl Parent;
protected:
Point2F mRange;
U32 mTicks;
bool mSnap;
F32 mValue;
RectI mThumb;
Point2I mThumbSize;
S32 mShiftPoint;
S32 mShiftExtent;
F32 mIncAmount;
bool mDisplayValue;
bool mDepressed;
bool mMouseOver;
bool mMouseDragged;
bool mHasTexture;
enum
{
SliderLineLeft = 0,
SliderLineCenter,
SliderLineRight,
SliderButtonNormal,
SliderButtonHighlight,
NumBitmaps
};
RectI *mBitmapBounds;
F32 _getThumbValue( const GuiEvent& event );
void _updateThumb( F32 value, bool snap = true, bool onWake = false, bool doCallback = true );
/// @name Callbacks
/// @{
DECLARE_CALLBACK( void, onMouseDragged, () );
/// @}
static bool _setValue( void* object, const char* index, const char* data ) { static_cast< GuiSliderCtrl* >( object )->setValue( dAtof( data ) ); return false; }
public:
GuiSliderCtrl();
bool isThumbBeingDragged() const { return mDepressed; }
const Point2F& getRange() const { return mRange; }
// GuiControl.
bool onWake();
void onMouseDown(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
void onMouseUp(const GuiEvent &);
void onMouseLeave(const GuiEvent &);
void onMouseEnter(const GuiEvent &);
bool onMouseWheelUp(const GuiEvent &event);
bool onMouseWheelDown(const GuiEvent &event);
void setActive( bool value );
F32 getValue() const { return mValue; }
void setScriptValue(const char *val) { setValue(dAtof(val)); }
void setValue(F32 val, bool doCallback=false);
void onRender(Point2I offset, const RectI &updateRect);
virtual bool resize( const Point2I& newSize, const Point2I& newExtent );
virtual void parentResized( const RectI& oldParentRect, const RectI& newParentRect );
static void initPersistFields();
DECLARE_CONOBJECT(GuiSliderCtrl);
DECLARE_CATEGORY( "Gui Values" );
DECLARE_DESCRIPTION( "A control that implements a horizontal or vertical slider to\n"
"select/represent values in a certain range." )
};
#endif

View file

@ -0,0 +1,234 @@
//-----------------------------------------------------------------------------
// 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/consoleTypes.h"
#include "console/console.h"
#include "console/engineAPI.h"
#include "gfx/gfxDevice.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiTabPageCtrl.h"
#include "gui/containers/guiTabBookCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/editor/guiEditCtrl.h"
IMPLEMENT_CONOBJECT(GuiTabPageCtrl);
ConsoleDocClass( GuiTabPageCtrl,
"@brief A single page in a GuiTabBookCtrl.\n\n"
"@tsexample\n\n"
"new GuiTabPageCtrl()\n"
"{\n"
" fitBook = \"1\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@ingroup GuiContainers"
);
GuiTabPageCtrl::GuiTabPageCtrl(void)
{
setExtent(Point2I(100, 200));
mFitBook = false;
dStrcpy(mText,(UTF8*)"TabPage");
mActive = true;
mIsContainer = true;
}
void GuiTabPageCtrl::initPersistFields()
{
addField( "fitBook", TypeBool, Offset( mFitBook, GuiTabPageCtrl ),
"Determines whether to resize this page when it is added to the tab book. "
"If true, the page will be resized according to the tab book extents and "
"<i>tabPosition</i> property." );
Parent::initPersistFields();
}
bool GuiTabPageCtrl::onWake()
{
if (! Parent::onWake())
return false;
return true;
}
void GuiTabPageCtrl::onSleep()
{
Parent::onSleep();
}
GuiControl* GuiTabPageCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
{
return Parent::findHitControl(pt, initialLayer);
}
void GuiTabPageCtrl::onMouseDown(const GuiEvent &event)
{
setUpdate();
Point2I localPoint = globalToLocalCoord( event.mousePoint );
GuiControl *ctrl = findHitControl(localPoint);
if (ctrl && ctrl != this)
{
ctrl->onMouseDown(event);
}
}
bool GuiTabPageCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset )
{
#ifdef TORQUE_TOOLS
// This shouldn't be called if it's not design time, but check just incase
if ( GuiControl::smDesignTime )
{
GuiEditCtrl* edit = GuiControl::smEditorHandle;
if( edit )
edit->select( this );
}
return Parent::onMouseDownEditor( event, offset );
#else
return false;
#endif
}
GuiControl *GuiTabPageCtrl::findNextTabable(GuiControl *curResponder, bool firstCall)
{
//set the global if this is the first call (directly from the canvas)
if (firstCall)
{
GuiControl::smCurResponder = NULL;
}
//if the window does not already contain the first responder, return false
//ie. Can't tab into or out of a window
if (! controlIsChild(curResponder))
{
return NULL;
}
//loop through, checking each child to see if it is the one that follows the firstResponder
GuiControl *tabCtrl = NULL;
iterator i;
for (i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
tabCtrl = ctrl->findNextTabable(curResponder, false);
if (tabCtrl) break;
}
//to ensure the tab cycles within the current window...
if (! tabCtrl)
{
tabCtrl = findFirstTabable();
}
mFirstResponder = tabCtrl;
return tabCtrl;
}
GuiControl *GuiTabPageCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall)
{
if (firstCall)
{
GuiControl::smPrevResponder = NULL;
}
//if the window does not already contain the first responder, return false
//ie. Can't tab into or out of a window
if (! controlIsChild(curResponder))
{
return NULL;
}
//loop through, checking each child to see if it is the one that follows the firstResponder
GuiControl *tabCtrl = NULL;
iterator i;
for (i = begin(); i != end(); i++)
{
GuiControl *ctrl = static_cast<GuiControl *>(*i);
tabCtrl = ctrl->findPrevTabable(curResponder, false);
if (tabCtrl) break;
}
//to ensure the tab cycles within the current window...
if (! tabCtrl)
{
tabCtrl = findLastTabable();
}
mFirstResponder = tabCtrl;
return tabCtrl;
}
void GuiTabPageCtrl::setText(const char *txt)
{
Parent::setText( txt );
GuiControl *parent = getParent();
if( parent )
parent->setUpdate();
};
void GuiTabPageCtrl::selectWindow(void)
{
//first make sure this window is the front most of its siblings
GuiControl *parent = getParent();
if (parent)
{
parent->pushObjectToBack(this);
}
//also set the first responder to be the one within this window
setFirstResponder(mFirstResponder);
}
void GuiTabPageCtrl::onRender(Point2I offset,const RectI &updateRect)
{
// Call directly into GuiControl to skip the GuiTextCtrl parent render
GuiControl::onRender( offset, updateRect );
}
void GuiTabPageCtrl::inspectPostApply()
{
Parent::inspectPostApply();
if( mFitBook )
{
GuiTabBookCtrl* book = dynamic_cast< GuiTabBookCtrl* >( getParent() );
if( book )
book->fitPage( this );
}
}
DefineEngineMethod( GuiTabPageCtrl, select, void, (),,
"Select this page in its tab book." )
{
GuiTabBookCtrl* book = dynamic_cast< GuiTabBookCtrl* >( object->getParent() );
if( !book )
return;
book->selectPage( object );
}

View file

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// 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 _GUITABPAGECTRL_H_
#define _GUITABPAGECTRL_H_
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
class GuiTabPageCtrl : public GuiTextCtrl
{
private:
typedef GuiTextCtrl Parent;
bool mFitBook; ///< Resize to fit book when first added
S32 mTabIndex;
public:
GuiTabPageCtrl();
DECLARE_CONOBJECT(GuiTabPageCtrl);
DECLARE_CATEGORY( "Gui Containers" );
DECLARE_DESCRIPTION( "A page in a GuiTabBookCtrl." );
static void initPersistFields();
bool onWake(); ///< The page awakens (becomes active)!
void onSleep(); ///< The page sleeps (zzzzZZ - becomes inactive)
void inspectPostApply();
bool getFitBook() { return mFitBook; }
void setFitBook(bool state) { mFitBook = state; }
GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1); ///< Find which control is hit by the mouse starting at a specified layer
void onMouseDown(const GuiEvent &event); ///< Called when a mouseDown event occurs
bool onMouseDownEditor(const GuiEvent &event, Point2I offset ); ///< Called when a mouseDown event occurs and the GUI editor is active
S32 getTabIndex(void) { return mTabIndex; } ///< Get the tab index of this control
//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);
void selectWindow(void); ///< Select this window
virtual void setText(const char *txt = NULL); ///< Override setText function to signal parent we need to update.
void onRender(Point2I offset, const RectI &updateRect); ///< Called when it's time to render this page to the scene
};
#endif //_GUI_WINDOW_CTRL_H

View file

@ -0,0 +1,246 @@
//-----------------------------------------------------------------------------
// 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/controls/guiTextCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "core/color.h"
#include "i18n/lang.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT( GuiTextCtrl );
ConsoleDocClass( GuiTextCtrl,
"@brief GUI control object this displays a single line of text, without TorqueML.\n\n"
"@tsexample\n"
" new GuiTextCtrl()\n"
" {\n"
" text = \"Hello World\";\n"
" textID = \"\"STR_HELLO\"\";\n"
" maxlength = \"1024\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
" };\n"
"@endtsexample\n\n"
"@see GuiControl\n"
"@see Localization\n\n"
"@ingroup GuiCore\n"
);
GuiTextCtrl::GuiTextCtrl()
{
//default fonts
mInitialText = StringTable->insert("");
mInitialTextID = StringTable->insert("");
mText[0] = '\0';
mMaxStrLen = GuiTextCtrl::MAX_STRING_LENGTH;
}
DefineEngineMethod( GuiTextCtrl, setText, void, (const char* text),,
"@brief Sets the text in the control.\n\n"
"@param text Text to display in the control.\n"
"@tsexample\n"
"// Set the text to show in the control\n"
"%text = \"Gideon - Destroyer of World\";\n\n"
"// Inform the GuiTextCtrl control to change its text to the defined value\n"
"%thisGuiTextCtrl.setText(%text);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->setText( text );
}
DefineEngineMethod( GuiTextCtrl, setTextID, void, (const char* textID),,
"@brief Maps the text ctrl to a variable used in localization, rather than raw text.\n\n"
"@param textID Name of variable text should be mapped to\n"
"@tsexample\n"
"// Inform the GuiTextCtrl control of the textID to use\n"
"%thisGuiTextCtrl.setTextID(\"STR_QUIT\");\n"
"@endtsexample\n\n"
"@see GuiControl"
"@see Localization")
{
object->setTextID( textID );
}
void GuiTextCtrl::initPersistFields()
{
addProtectedField("text", TypeCaseString, Offset(mInitialText, GuiTextCtrl), setText, getTextProperty,
"The text to show on the control.");
addField( "textID", TypeString, Offset( mInitialTextID, GuiTextCtrl ),
"Maps the text of this control to a variable used in localization, rather than raw text.");
addField( "maxLength", TypeS32, Offset( mMaxStrLen, GuiTextCtrl ),
"Defines the maximum length of the text. The default is 1024." );
Parent::initPersistFields();
}
bool GuiTextCtrl::onAdd()
{
if(!Parent::onAdd())
return false;
dStrncpy(mText, (UTF8*)mInitialText, MAX_STRING_LENGTH);
mText[MAX_STRING_LENGTH] = '\0';
return true;
}
void GuiTextCtrl::inspectPostApply()
{
Parent::inspectPostApply();
if(mInitialTextID && *mInitialTextID != 0)
setTextID(mInitialTextID);
else if( mConsoleVariable[ 0 ] )
setText( getVariable() );
else
setText(mInitialText);
}
bool GuiTextCtrl::onWake()
{
if ( !Parent::onWake() )
return false;
if( !mProfile->mFont )
{
Con::errorf( "GuiTextCtrl::onWake() - no valid font in profile '%s'", mProfile->getName() );
return false;
}
if(mInitialTextID && *mInitialTextID != 0)
setTextID(mInitialTextID);
if ( mConsoleVariable[0] )
{
const char *txt = Con::getVariable( mConsoleVariable );
if ( txt )
{
if ( dStrlen( txt ) > mMaxStrLen )
{
char* buf = new char[mMaxStrLen + 1];
dStrncpy( buf, txt, mMaxStrLen );
buf[mMaxStrLen] = 0;
setScriptValue( buf );
delete [] buf;
}
else
setScriptValue( txt );
}
}
//resize
autoResize();
return true;
}
void GuiTextCtrl::autoResize()
{
if( mProfile->mAutoSizeWidth || mProfile->mAutoSizeHeight)
{
if( !mProfile->mFont )
{
mProfile->loadFont();
if( !mProfile->mFont )
return;
}
Point2I newExtents = getExtent();
if ( mProfile->mAutoSizeWidth )
newExtents.x = mProfile->mFont->getStrWidth((const UTF8 *) mText );
if ( mProfile->mAutoSizeHeight )
newExtents.y = mProfile->mFont->getHeight() + 4;
setExtent( newExtents );
}
}
void GuiTextCtrl::setText(const char *txt)
{
//make sure we don't call this before onAdd();
if( !mProfile )
return;
if (txt)
dStrncpy(mText, (UTF8*)txt, MAX_STRING_LENGTH);
mText[MAX_STRING_LENGTH] = '\0';
setVariable((char*)mText);
setUpdate();
autoResize();
}
void GuiTextCtrl::setTextID(const char *id)
{
S32 n = Con::getIntVariable(id, -1);
if(n != -1)
{
mInitialTextID = StringTable->insert(id);
setTextID(n);
}
}
void GuiTextCtrl::setTextID(S32 id)
{
const UTF8 *str = getGUIString(id);
if(str)
setText((const char*)str);
//mInitialTextID = id;
}
void GuiTextCtrl::onPreRender()
{
Parent::onPreRender();
const char * var = getVariable();
if(var && var[0] && dStricmp((char*)mText, var))
setText(var);
}
void GuiTextCtrl::onRender(Point2I offset, const RectI &updateRect)
{
renderBorder( RectI( offset, getExtent() ), mProfile );
GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
renderJustifiedText(offset, getExtent(), (char*)mText);
//render the child controls
renderChildControls(offset, updateRect);
}
const char *GuiTextCtrl::getScriptValue()
{
return getText();
}
void GuiTextCtrl::setScriptValue(const char *val)
{
setText(val);
}

View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// 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 _GUITEXTCTRL_H_
#define _GUITEXTCTRL_H_
#ifndef _GFONT_H_
#include "gfx/gFont.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUICONTAINER_H_
#include "gui/containers/guiContainer.h"
#endif
class GuiTextCtrl : public GuiContainer
{
private:
typedef GuiContainer Parent;
public:
enum Constants { MAX_STRING_LENGTH = 1024 };
protected:
StringTableEntry mInitialText;
StringTableEntry mInitialTextID;
UTF8 mText[MAX_STRING_LENGTH + 1];
S32 mMaxStrLen; // max string len, must be less then or equal to 255
public:
//creation methods
DECLARE_CONOBJECT(GuiTextCtrl);
DECLARE_CATEGORY( "Gui Text" );
DECLARE_DESCRIPTION( "A control that displays a single line of text." );
GuiTextCtrl();
static void initPersistFields();
//Parental methods
bool onAdd();
virtual bool onWake();
//text methods
virtual void setText(const char *txt = NULL);
virtual void setTextID(S32 id);
virtual void setTextID(const char *id);
const char *getText() { return (const char*)mText; }
// Text Property Accessors
static bool setText(void *object, const char *index, const char *data)
{ static_cast<GuiTextCtrl*>(object)->setText(data); return true; }
static const char* getTextProperty(void* obj, const char* data)
{ return static_cast<GuiTextCtrl*>(obj)->getText(); }
void inspectPostApply();
//rendering methods
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
void displayText( S32 xOffset, S32 yOffset );
// resizing
void autoResize();
//Console methods
const char *getScriptValue();
void setScriptValue(const char *value);
};
#endif //_GUI_TEXT_CONTROL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,156 @@
//-----------------------------------------------------------------------------
// 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 _GUITEXTEDITCTRL_H_
#define _GUITEXTEDITCTRL_H_
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUITEXTCTRL_H_
#include "gui/controls/guiTextCtrl.h"
#endif
#ifndef _STRINGBUFFER_H_
#include "core/stringBuffer.h"
#endif
class SFXTrack;
class GuiTextEditCtrl : public GuiTextCtrl
{
private:
typedef GuiTextCtrl Parent;
protected:
DECLARE_CALLBACK( void, onTabComplete, (const char* val));
DECLARE_CALLBACK( void, onReturn, ());
DECLARE_CALLBACK( void, onValidate, ());
StringBuffer mTextBuffer;
String mValidateCommand;
String mEscapeCommand;
SFXTrack* mDeniedSound;
// for animating the cursor
S32 mNumFramesElapsed;
U32 mTimeLastCursorFlipped;
ColorI mCursorColor;
bool mCursorOn;
bool mInsertOn;
S32 mMouseDragStart;
Point2I mTextOffset;
bool mTextOffsetReset;
bool mDragHit;
bool mTabComplete;
S32 mScrollDir;
//undo members
StringBuffer mUndoText;
S32 mUndoBlockStart;
S32 mUndoBlockEnd;
S32 mUndoCursorPos;
void saveUndoState();
S32 mBlockStart;
S32 mBlockEnd;
S32 mCursorPos;
virtual S32 calculateCursorPos( const Point2I &globalPos );
bool mHistoryDirty;
S32 mHistoryLast;
S32 mHistoryIndex;
S32 mHistorySize;
bool mPasswordText;
StringTableEntry mPasswordMask;
/// If set, any non-ESC key is handled here or not at all
bool mSinkAllKeyEvents;
UTF16 **mHistoryBuf;
void updateHistory(StringBuffer *txt, bool moveIndex);
void playDeniedSound();
void execConsoleCallback();
virtual void handleCharInput( U16 ascii );
S32 findNextWord();
S32 findPrevWord();
public:
GuiTextEditCtrl();
~GuiTextEditCtrl();
DECLARE_CONOBJECT(GuiTextEditCtrl);
DECLARE_DESCRIPTION( "A control that allows to edit a single line of text. ");
static void initPersistFields();
bool onAdd();
/// Get the contents of the control.
///
/// dest should be of size GuiTextCtrl::MAX_STRING_LENGTH+1.
void getText(char *dest);
virtual void getRenderText(char *dest);
void setText(S32 tag);
virtual void setText(const UTF8* txt);
virtual void setText(const UTF16* txt);
S32 getCursorPos() { return( mCursorPos ); }
void setCursorPos( const S32 newPos );
bool isAllTextSelected();
void selectAllText();
void clearSelectedText();
void forceValidateText();
const char *getScriptValue();
void setScriptValue(const char *value);
bool getSinkAllKeys() { return mSinkAllKeyEvents; }
void setSinkAllKeys(bool state) { mSinkAllKeyEvents = state; }
virtual bool onKeyDown(const GuiEvent &event);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
void onCopy(bool andCut);
void onPaste();
void onUndo();
virtual void setFirstResponder();
virtual void onLoseFirstResponder();
bool hasText();
void onStaticModified(const char* slotName, const char* newValue = NULL);
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
virtual void drawText( const RectI &drawRect, bool isFocused );
bool dealWithEnter( bool clearResponder );
};
#endif //_GUI_TEXTEDIT_CTRL_H

View file

@ -0,0 +1,447 @@
//-----------------------------------------------------------------------------
// 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/controls/guiTextEditSliderBitmapCtrl.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "gui/core/guiCanvas.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT(GuiTextEditSliderBitmapCtrl);
ConsoleDocClass( GuiTextEditSliderBitmapCtrl,
"@brief GUI Control which displays a numerical value which can be increased "
"or decreased using a pair of bitmap up/down buttons. \n\n"
"This control uses the bitmap specified in it's profile "
"(GuiControlProfile::bitmapName). It takes this image and breaks up aspects "
"of it to render the up and down arrows. It is also important to set "
"GuiControlProfile::hasBitmapArray to true on the profile as well.\n\n"
"The bitmap referenced should be broken up into a 1 x 4 grid (using the top "
"left color pixel as a border color between each of the images) in which it "
"will map to the following places:\n"
"<ol>\n"
"<li>Up arrow active</li>\n"
"<li>Up arrow inactive</li>\n"
"<li>Down arrow active</li>\n"
"<li>Down arrow inactive</li>\n"
"</ol>\n\n"
"<pre>\n"
"1\n"
"2\n"
"3\n"
"4</pre>\n\n"
"@tsexample\n"
"singleton GuiControlProfile (SliderBitmapGUIProfile)\n"
"{\n"
" bitmap = \"core/art/gui/images/sliderArray\";\n"
" hasBitmapArray = true;\n"
" opaque = false;\n"
"};\n\n"
"new GuiTextEditSliderBitmapCtrl()\n"
"{\n"
" profile = \"SliderBitmapGUIProfile\";\n"
" format = \"%3.2f\";\n"
" range = \"-1e+03 1e+03\";\n"
" increment = \"0.1\";\n"
" focusOnMouseWheel = \"0\";\n"
" bitmap = \"\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@see GuiTextEditSliderCtrl\n\n"
"@see GuiTextEditCtrl\n\n"
"@ingroup GuiCore\n"
);
GuiTextEditSliderBitmapCtrl::GuiTextEditSliderBitmapCtrl()
{
mRange.set(0.0f, 1.0f);
mIncAmount = 1.0f;
mValue = 0.0f;
mMulInc = 0;
mIncCounter = 0.0f;
mFormat = StringTable->insert("%3.2f");
mTextAreaHit = None;
mFocusOnMouseWheel = false;
mBitmapName = StringTable->insert( "" );
}
GuiTextEditSliderBitmapCtrl::~GuiTextEditSliderBitmapCtrl()
{
}
void GuiTextEditSliderBitmapCtrl::initPersistFields()
{
addField("format", TypeString, Offset(mFormat, GuiTextEditSliderBitmapCtrl), "Character format type to place in the control.\n");
addField("range", TypePoint2F, Offset(mRange, GuiTextEditSliderBitmapCtrl), "Maximum vertical and horizontal range to allow in the control.\n");
addField("increment", TypeF32, Offset(mIncAmount, GuiTextEditSliderBitmapCtrl), "How far to increment the slider on each step.\n");
addField("focusOnMouseWheel", TypeBool, Offset(mFocusOnMouseWheel, GuiTextEditSliderBitmapCtrl), "If true, the control will accept giving focus to the user when the mouse wheel is used.\n");
addField("bitmap", TypeFilename,Offset(mBitmapName, GuiTextEditSliderBitmapCtrl), "Unused" );
Parent::initPersistFields();
}
void GuiTextEditSliderBitmapCtrl::getText(char *dest)
{
Parent::getText(dest);
}
void GuiTextEditSliderBitmapCtrl::setText(const char *txt)
{
mValue = dAtof(txt);
checkRange();
setValue();
}
bool GuiTextEditSliderBitmapCtrl::onKeyDown(const GuiEvent &event)
{
return Parent::onKeyDown(event);
}
void GuiTextEditSliderBitmapCtrl::checkRange()
{
if(mValue < mRange.x)
mValue = mRange.x;
else if(mValue > mRange.y)
mValue = mRange.y;
}
void GuiTextEditSliderBitmapCtrl::setValue()
{
char buf[20];
// For some reason this sprintf is failing to convert
// a floating point number to anything with %d, so cast it.
if( dStricmp( mFormat, "%d" ) == 0 )
dSprintf(buf,sizeof(buf),mFormat, (S32)mValue);
else
dSprintf(buf,sizeof(buf),mFormat, mValue);
Parent::setText(buf);
}
void GuiTextEditSliderBitmapCtrl::onMouseDown(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseDown(event);
return;
}
char txt[20];
Parent::getText(txt);
mValue = dAtof(txt);
mMouseDownTime = Sim::getCurrentTime();
GuiControl *parent = getParent();
if(!parent)
return;
Point2I camPos = event.mousePoint;
Point2I point = parent->localToGlobalCoord(getPosition());
if(camPos.x > point.x + getExtent().x - 14)
{
if(camPos.y > point.y + (getExtent().y/2))
{
mValue -=mIncAmount;
mTextAreaHit = ArrowDown;
mMulInc = -0.15f;
}
else
{
mValue +=mIncAmount;
mTextAreaHit = ArrowUp;
mMulInc = 0.15f;
}
checkRange();
setValue();
mouseLock();
// We should get the focus and set the
// cursor to the start of the text to
// mimic the standard Windows behavior.
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return;
}
Parent::onMouseDown(event);
}
void GuiTextEditSliderBitmapCtrl::onMouseDragged(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseDragged(event);
return;
}
if(mTextAreaHit == None || mTextAreaHit == Slider)
{
mTextAreaHit = Slider;
GuiControl *parent = getParent();
if(!parent)
return;
Point2I camPos = event.mousePoint;
Point2I point = parent->localToGlobalCoord(getPosition());
F32 maxDis = 100;
F32 val;
if(camPos.y < point.y)
{
if((F32)point.y < maxDis)
maxDis = (F32)point.y;
val = point.y - maxDis;
if(point.y > 0)
mMulInc= 1.0f-(((float)camPos.y - val) / maxDis);
else
mMulInc = 1.0f;
checkIncValue();
return;
}
else if(camPos.y > point.y + getExtent().y)
{
GuiCanvas *root = getRoot();
val = (F32)(root->getHeight() - (point.y + getHeight()));
if(val < maxDis)
maxDis = val;
if( val > 0)
mMulInc= -(F32)(camPos.y - (point.y + getHeight()))/maxDis;
else
mMulInc = -1.0f;
checkIncValue();
return;
}
mTextAreaHit = None;
Parent::onMouseDragged(event);
}
}
void GuiTextEditSliderBitmapCtrl::onMouseUp(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseUp(event);
return;
}
mMulInc = 0.0f;
mouseUnlock();
if ( mTextAreaHit != None )
//if we released the mouse within this control, then the parent will call
//the mConsoleCommand other wise we have to call it.
Parent::onMouseUp(event);
//if we didn't release the mouse within this control, then perform the action
// if (!cursorInControl())
execConsoleCallback();
execAltConsoleCallback();
mTextAreaHit = None;
}
bool GuiTextEditSliderBitmapCtrl::onMouseWheelUp(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelUp(event);
if ( !isFirstResponder() && !mFocusOnMouseWheel )
return false;
mValue += mIncAmount;
checkRange();
setValue();
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return true;
}
bool GuiTextEditSliderBitmapCtrl::onMouseWheelDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelDown(event);
if ( !isFirstResponder() && !mFocusOnMouseWheel )
return false;
mValue -= mIncAmount;
checkRange();
setValue();
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return true;
}
void GuiTextEditSliderBitmapCtrl::checkIncValue()
{
if(mMulInc > 1.0f)
mMulInc = 1.0f;
else if(mMulInc < -1.0f)
mMulInc = -1.0f;
}
void GuiTextEditSliderBitmapCtrl::timeInc(U32 elapseTime)
{
S32 numTimes = elapseTime / 750;
if(mTextAreaHit != Slider && numTimes > 0)
{
if(mTextAreaHit == ArrowUp)
mMulInc = 0.15f * numTimes;
else
mMulInc = -0.15f * numTimes;
checkIncValue();
}
}
bool GuiTextEditSliderBitmapCtrl::onWake()
{
if(!Parent::onWake())
return false;
mNumberOfBitmaps = mProfile->constructBitmapArray();
return true;
}
void GuiTextEditSliderBitmapCtrl::onPreRender()
{
if (isFirstResponder())
{
U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped;
mNumFramesElapsed++;
if ((timeElapsed > 500) && (mNumFramesElapsed > 3))
{
mCursorOn = !mCursorOn;
mTimeLastCursorFlipped = Sim::getCurrentTime();
mNumFramesElapsed = 0;
setUpdate();
}
//update the cursor if the text is scrolling
if (mDragHit)
{
if ((mScrollDir < 0) && (mCursorPos > 0))
{
mCursorPos--;
}
else if ((mScrollDir > 0) && (mCursorPos < (S32)dStrlen(mText)))
{
mCursorPos++;
}
}
}
}
void GuiTextEditSliderBitmapCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if(mTextAreaHit != None)
{
U32 elapseTime = Sim::getCurrentTime() - mMouseDownTime;
if(elapseTime > 750 || mTextAreaHit == Slider)
{
timeInc(elapseTime);
mIncCounter += mMulInc;
if(mIncCounter >= 1.0f || mIncCounter <= -1.0f)
{
mValue = (mMulInc > 0.0f) ? mValue+mIncAmount : mValue-mIncAmount;
mIncCounter = (mIncCounter > 0.0f) ? mIncCounter-1 : mIncCounter+1;
checkRange();
setValue();
mCursorPos = 0;
}
}
}
Parent::onRender(offset, updateRect);
// Arrow placement coordinates
Point2I arrowUpStart(offset.x + getWidth() - 14, offset.y + 1 );
Point2I arrowUpEnd(13, getExtent().y/2);
Point2I arrowDownStart(offset.x + getWidth() - 14, offset.y + 1 + getExtent().y/2);
Point2I arrowDownEnd(13, getExtent().y/2);
// Draw the line that splits the number and bitmaps
GFX->getDrawUtil()->drawLine(Point2I(offset.x + getWidth() - 14 -2, offset.y + 1 ),
Point2I(arrowUpStart.x -2, arrowUpStart.y + getExtent().y),
mProfile->mBorderColor);
GFX->getDrawUtil()->clearBitmapModulation();
if(mNumberOfBitmaps == 0)
Con::warnf("No image provided for GuiTextEditSliderBitmapCtrl; do not render");
else
{
// This control needs 4 images in order to render correctly
if(mTextAreaHit == ArrowUp)
GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowUpStart,arrowUpEnd), mProfile->mBitmapArrayRects[0] );
else
GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowUpStart,arrowUpEnd), mProfile->mBitmapArrayRects[1] );
if(mTextAreaHit == ArrowDown)
GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowDownStart,arrowDownEnd), mProfile->mBitmapArrayRects[2] );
else
GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowDownStart,arrowDownEnd), mProfile->mBitmapArrayRects[3] );
}
}
void GuiTextEditSliderBitmapCtrl::setBitmap(const char *name)
{
bool awake = mAwake;
if(awake)
onSleep();
mBitmapName = StringTable->insert(name);
if(awake)
onWake();
setUpdate();
}

View file

@ -0,0 +1,95 @@
//-----------------------------------------------------------------------------
// 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 _GUITEXTEDITSLIDERBITMAPCTRL_H_
#define _GUITEXTEDITSLIDERBITMAPCTRL_H_
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUITEXTEDITCTRL_H_
#include "gui/controls/guiTextEditCtrl.h"
#endif
class GuiTextEditSliderBitmapCtrl : public GuiTextEditCtrl
{
typedef GuiTextEditCtrl Parent;
public:
enum CtrlArea
{
None,
Slider,
ArrowUp,
ArrowDown
};
GuiTextEditSliderBitmapCtrl();
~GuiTextEditSliderBitmapCtrl();
DECLARE_CONOBJECT(GuiTextEditSliderBitmapCtrl);
DECLARE_CATEGORY( "Gui Values" );
DECLARE_DESCRIPTION( "A text control that display a numeric value and bitmapped up/down sliders." );
static void initPersistFields();
virtual void getText(char *dest); // dest must be of size
// StructDes::MAX_STRING_LEN + 1
virtual void setText(const char *txt);
void setValue();
void checkRange();
void checkIncValue();
void timeInc(U32 elapseTime);
virtual bool onKeyDown(const GuiEvent &event);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual bool onMouseWheelUp(const GuiEvent &event);
virtual bool onMouseWheelDown(const GuiEvent &event);
bool onWake();
virtual void onPreRender();
virtual void onRender(Point2I offset, const RectI &updateRect);
void setBitmap(const char *name);
protected:
Point2F mRange;
F32 mIncAmount;
F32 mValue;
F32 mIncCounter;
F32 mMulInc;
StringTableEntry mFormat;
U32 mMouseDownTime;
bool mFocusOnMouseWheel;
S32 mNumberOfBitmaps;
StringTableEntry mBitmapName;
CtrlArea mTextAreaHit;
};
#endif //_GUITEXTEDITSLIDERBITMAPCTRL_H_

View file

@ -0,0 +1,429 @@
//-----------------------------------------------------------------------------
// 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/controls/guiTextEditSliderCtrl.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "gui/core/guiCanvas.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(GuiTextEditSliderCtrl);
ConsoleDocClass( GuiTextEditSliderCtrl,
"@brief GUI Control which displays a numerical value which can be increased or "
"decreased using a pair of arrows.\n\n"
"@tsexample\n"
"new GuiTextEditSliderCtrl()\n"
"{\n"
" format = \"%3.2f\";\n"
" range = \"-1e+03 1e+03\";\n"
" increment = \"0.1\";\n"
" focusOnMouseWheel = \"0\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
"};\n"
"@endtsexample\n\n"
"@see GuiTextEditCtrl\n\n"
"@ingroup GuiCore\n"
);
GuiTextEditSliderCtrl::GuiTextEditSliderCtrl()
{
mRange.set(0.0f, 1.0f);
mIncAmount = 1.0f;
mValue = 0.0f;
mMulInc = 0;
mIncCounter = 0.0f;
mFormat = StringTable->insert("%3.2f");
mTextAreaHit = None;
mFocusOnMouseWheel = false;
}
GuiTextEditSliderCtrl::~GuiTextEditSliderCtrl()
{
}
void GuiTextEditSliderCtrl::initPersistFields()
{
addField("format", TypeString, Offset(mFormat, GuiTextEditSliderCtrl), "Character format type to place in the control.\n");
addField("range", TypePoint2F, Offset(mRange, GuiTextEditSliderCtrl), "Maximum vertical and horizontal range to allow in the control.\n");
addField("increment", TypeF32, Offset(mIncAmount, GuiTextEditSliderCtrl), "How far to increment the slider on each step.\n");
addField("focusOnMouseWheel", TypeBool, Offset(mFocusOnMouseWheel, GuiTextEditSliderCtrl), "If true, the control will accept giving focus to the user when the mouse wheel is used.\n");
Parent::initPersistFields();
}
void GuiTextEditSliderCtrl::getText(char *dest)
{
Parent::getText(dest);
}
void GuiTextEditSliderCtrl::setText(const char *txt)
{
mValue = dAtof(txt);
checkRange();
setValue();
}
bool GuiTextEditSliderCtrl::onKeyDown(const GuiEvent &event)
{
return Parent::onKeyDown(event);
}
void GuiTextEditSliderCtrl::checkRange()
{
if(mValue < mRange.x)
mValue = mRange.x;
else if(mValue > mRange.y)
mValue = mRange.y;
}
void GuiTextEditSliderCtrl::setValue()
{
char buf[20];
// For some reason this sprintf is failing to convert
// a floating point number to anything with %d, so cast it.
if( dStricmp( mFormat, "%d" ) == 0 )
dSprintf(buf,sizeof(buf),mFormat, (S32)mValue);
else
dSprintf(buf,sizeof(buf),mFormat, mValue);
Parent::setText(buf);
}
void GuiTextEditSliderCtrl::onMouseDown(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseDown(event);
return;
}
char txt[20];
Parent::getText(txt);
mValue = dAtof(txt);
mMouseDownTime = Sim::getCurrentTime();
GuiControl *parent = getParent();
if(!parent)
return;
Point2I camPos = event.mousePoint;
Point2I point = parent->localToGlobalCoord(getPosition());
if(camPos.x > point.x + getExtent().x - 14)
{
if(camPos.y > point.y + (getExtent().y/2))
{
mValue -=mIncAmount;
mTextAreaHit = ArrowDown;
mMulInc = -0.15f;
}
else
{
mValue +=mIncAmount;
mTextAreaHit = ArrowUp;
mMulInc = 0.15f;
}
checkRange();
setValue();
mouseLock();
// We should get the focus and set the
// cursor to the start of the text to
// mimic the standard Windows behavior.
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return;
}
Parent::onMouseDown(event);
}
void GuiTextEditSliderCtrl::onMouseDragged(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseDragged(event);
return;
}
if(mTextAreaHit == None || mTextAreaHit == Slider)
{
mTextAreaHit = Slider;
GuiControl *parent = getParent();
if(!parent)
return;
Point2I camPos = event.mousePoint;
Point2I point = parent->localToGlobalCoord(getPosition());
F32 maxDis = 100;
F32 val;
if(camPos.y < point.y)
{
if((F32)point.y < maxDis)
maxDis = (F32)point.y;
val = point.y - maxDis;
if(point.y > 0)
mMulInc= 1.0f-(((float)camPos.y - val) / maxDis);
else
mMulInc = 1.0f;
checkIncValue();
return;
}
else if(camPos.y > point.y + getExtent().y)
{
GuiCanvas *root = getRoot();
val = (F32)(root->getHeight() - (point.y + getHeight()));
if(val < maxDis)
maxDis = val;
if( val > 0)
mMulInc= -(F32)(camPos.y - (point.y + getHeight()))/maxDis;
else
mMulInc = -1.0f;
checkIncValue();
return;
}
mTextAreaHit = None;
Parent::onMouseDragged(event);
}
}
void GuiTextEditSliderCtrl::onMouseUp(const GuiEvent &event)
{
// If we're not active then skip out.
if ( !mActive || !mAwake || !mVisible )
{
Parent::onMouseUp(event);
return;
}
mMulInc = 0.0f;
mouseUnlock();
if ( mTextAreaHit != None )
selectAllText();
//if we released the mouse within this control, then the parent will call
//the mConsoleCommand other wise we have to call it.
Parent::onMouseUp(event);
//if we didn't release the mouse within this control, then perform the action
// if (!cursorInControl())
execConsoleCallback();
execAltConsoleCallback();
//Set the cursor position to where the user clicked
mCursorPos = calculateCursorPos( event.mousePoint );
mTextAreaHit = None;
}
bool GuiTextEditSliderCtrl::onMouseWheelUp(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelUp(event);
if ( !isFirstResponder() && !mFocusOnMouseWheel )
{
GuiControl *parent = getParent();
if ( parent )
return parent->onMouseWheelUp( event );
return false;
}
mValue += mIncAmount;
checkRange();
setValue();
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return true;
}
bool GuiTextEditSliderCtrl::onMouseWheelDown(const GuiEvent &event)
{
if ( !mActive || !mAwake || !mVisible )
return Parent::onMouseWheelDown(event);
if ( !isFirstResponder() && !mFocusOnMouseWheel )
{
GuiControl *parent = getParent();
if ( parent )
return parent->onMouseWheelUp( event );
return false;
}
mValue -= mIncAmount;
checkRange();
setValue();
setFirstResponder();
mCursorPos = mBlockStart = mBlockEnd = 0;
setUpdate();
return true;
}
void GuiTextEditSliderCtrl::checkIncValue()
{
if(mMulInc > 1.0f)
mMulInc = 1.0f;
else if(mMulInc < -1.0f)
mMulInc = -1.0f;
}
void GuiTextEditSliderCtrl::timeInc(U32 elapseTime)
{
S32 numTimes = elapseTime / 750;
if(mTextAreaHit != Slider && numTimes > 0)
{
if(mTextAreaHit == ArrowUp)
mMulInc = 0.15f * numTimes;
else
mMulInc = -0.15f * numTimes;
checkIncValue();
}
}
void GuiTextEditSliderCtrl::onRender(Point2I offset, const RectI &updateRect)
{
if(mTextAreaHit != None)
{
U32 elapseTime = Sim::getCurrentTime() - mMouseDownTime;
if(elapseTime > 750 || mTextAreaHit == Slider)
{
timeInc(elapseTime);
mIncCounter += mMulInc;
if(mIncCounter >= 1.0f || mIncCounter <= -1.0f)
{
mValue = (mMulInc > 0.0f) ? mValue+mIncAmount : mValue-mIncAmount;
mIncCounter = (mIncCounter > 0.0f) ? mIncCounter-1 : mIncCounter+1;
checkRange();
setValue();
mCursorPos = 0;
}
}
}
Parent::onRender(offset, updateRect);
Point2I start(offset.x + getWidth() - 14, offset.y);
Point2I midPoint(start.x + 7, start.y + (getExtent().y/2));
GFX->getDrawUtil()->drawRectFill(Point2I(start.x+1,start.y+1), Point2I(start.x+13,start.y+getExtent().y-1) , mProfile->mFillColor);
GFX->getDrawUtil()->drawLine(start, Point2I(start.x, start.y+getExtent().y),mProfile->mFontColor);
GFX->getDrawUtil()->drawLine(Point2I(start.x,midPoint.y),
Point2I(start.x+14,midPoint.y),
mProfile->mFontColor);
GFXVertexBufferHandle<GFXVertexPC> verts(GFX, 6, GFXBufferTypeVolatile);
verts.lock();
verts[0].color.set( 0, 0, 0 );
verts[1].color.set( 0, 0, 0 );
verts[2].color.set( 0, 0, 0 );
verts[3].color.set( 0, 0, 0 );
verts[4].color.set( 0, 0, 0 );
verts[5].color.set( 0, 0, 0 );
if(mTextAreaHit == ArrowUp)
{
verts[0].point.set( (F32)midPoint.x, (F32)start.y + 1.0f, 0.0f );
verts[1].point.set( (F32)start.x + 11.0f, (F32)midPoint.y - 2.0f, 0.0f );
verts[2].point.set( (F32)start.x + 3.0f, (F32)midPoint.y - 2.0f, 0.0f );
}
else
{
verts[0].point.set( (F32)midPoint.x, (F32)start.y + 2.0f, 0.0f );
verts[1].point.set( (F32)start.x + 11.0f, (F32)midPoint.y - 1.0f, 0.0f );
verts[2].point.set( (F32)start.x + 3.0f, (F32)midPoint.y - 1.0f, 0.0f );
}
if(mTextAreaHit == ArrowDown)
{
verts[3].point.set( (F32)midPoint.x, (F32)(start.y + getExtent().y - 1), 0.0f );
verts[4].point.set( (F32)start.x + 11.0f, (F32)midPoint.y + 3.0f, 0.0f );
verts[5].point.set( (F32)start.x + 3.0f, (F32)midPoint.y + 3.0f, 0.0f );
}
else
{
verts[3].point.set( (F32)midPoint.x, (F32)(start.y + getExtent().y - 2), 0.0f );
verts[4].point.set( (F32)start.x + 11.0f, (F32)midPoint.y + 2.0f, 0.0f );
verts[5].point.set( (F32)start.x + 3.0f, (F32)midPoint.y + 2.0f, 0.0f );
}
verts.unlock();
GFX->setVertexBuffer( verts );
GFX->drawPrimitive( GFXTriangleList, 0, 2 );
}
void GuiTextEditSliderCtrl::onPreRender()
{
if (isFirstResponder())
{
U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped;
mNumFramesElapsed++;
if ((timeElapsed > 500) && (mNumFramesElapsed > 3))
{
mCursorOn = !mCursorOn;
mTimeLastCursorFlipped = Sim::getCurrentTime();
mNumFramesElapsed = 0;
setUpdate();
}
//update the cursor if the text is scrolling
if (mDragHit)
{
if ((mScrollDir < 0) && (mCursorPos > 0))
{
mCursorPos--;
}
else if ((mScrollDir > 0) && (mCursorPos < (S32)dStrlen(mText)))
{
mCursorPos++;
}
}
}
}

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// 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 _GUITEXTEDITSLIDERCTRL_H_
#define _GUITEXTEDITSLIDERCTRL_H_
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _GUITEXTEDITCTRL_H_
#include "gui/controls/guiTextEditCtrl.h"
#endif
class GuiTextEditSliderCtrl : public GuiTextEditCtrl
{
typedef GuiTextEditCtrl Parent;
public:
enum CtrlArea
{
None,
Slider,
ArrowUp,
ArrowDown
};
GuiTextEditSliderCtrl();
~GuiTextEditSliderCtrl();
DECLARE_CONOBJECT(GuiTextEditSliderCtrl);
DECLARE_CATEGORY( "Gui Values" );
DECLARE_DESCRIPTION( "A text that shows a numeric value and up/down arrows to\n"
"increase/decrease the value." );
static void initPersistFields();
virtual void getText(char *dest); // dest must be of size
// StructDes::MAX_STRING_LEN + 1
virtual void setText(const char *txt);
void setValue();
void checkRange();
void checkIncValue();
void timeInc(U32 elapseTime);
virtual bool onKeyDown(const GuiEvent &event);
virtual void onMouseDown(const GuiEvent &event);
virtual void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
virtual bool onMouseWheelUp(const GuiEvent &event);
virtual bool onMouseWheelDown(const GuiEvent &event);
virtual void onPreRender();
virtual void onRender(Point2I offset, const RectI &updateRect);
protected:
Point2F mRange;
F32 mIncAmount;
F32 mValue;
F32 mIncCounter;
F32 mMulInc;
StringTableEntry mFormat;
U32 mMouseDownTime;
bool mFocusOnMouseWheel;
CtrlArea mTextAreaHit;
};
#endif //_GUITEXTEDITSLIDERCTRL_H_

View file

@ -0,0 +1,846 @@
//-----------------------------------------------------------------------------
// 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/controls/guiTextListCtrl.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "gui/containers/guiScrollCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gfx/gfxDrawUtil.h"
#include "console/engineAPI.h"
IMPLEMENT_CONOBJECT(GuiTextListCtrl);
ConsoleDocClass( GuiTextListCtrl,
"@brief GUI control that displays a list of text. Text items in the list can be individually selected.\n\n"
"@tsexample\n"
" new GuiTextListCtrl(EndGameGuiList)\n"
" {\n"
" columns = \"0 256\";\n"
" fitParentWidth = \"1\";\n"
" clipColumnText = \"0\";\n"
" //Properties not specific to this control have been omitted from this example.\n"
" };\n"
"@endtsexample\n\n"
"@see Reference\n\n"
"@ingroup GuiControls\n"
);
IMPLEMENT_CALLBACK( GuiTextListCtrl, onSelect, void, (const char* cellid, const char* text),( cellid , text ),
"@brief Called whenever an item in the list is selected.\n\n"
"@param cellid The ID of the cell that was selected\n"
"@param text The text in the selected cel\n\n"
"@tsexample\n"
"// A cel in the control was selected, causing the callback to occur\n"
"GuiTextListCtrl::onSelect(%this,%callid,%text)\n"
" {\n"
" // Code to run when a cel item is selected\n"
" }\n"
"@endtsexample\n\n"
"@see GuiControl\n\n"
);
IMPLEMENT_CALLBACK( GuiTextListCtrl, onDeleteKey, void, ( const char* id ),( id ),
"@brief Called when the delete key has been pressed.\n\n"
"@param id Id of the selected item in the list\n"
"@tsexample\n"
"// The delete key was pressed while the GuiTextListCtrl was in focus, causing the callback to occur.\n"
"GuiTextListCtrl::onDeleteKey(%this,%id)\n"
" {\n"
" // Code to run when the delete key is pressed\n"
" }\n"
"@endtsexample\n\n"
"@see GuiControl\n\n"
);
static int sortColumn;
static bool sIncreasing;
static const char *getColumn(const char *text)
{
int ct = sortColumn;
while(ct--)
{
text = dStrchr(text, '\t');
if(!text)
return "";
text++;
}
return text;
}
static S32 QSORT_CALLBACK textCompare( const void* a, const void* b )
{
GuiTextListCtrl::Entry *ea = (GuiTextListCtrl::Entry *) (a);
GuiTextListCtrl::Entry *eb = (GuiTextListCtrl::Entry *) (b);
S32 result = dStrnatcasecmp( getColumn( ea->text ), getColumn( eb->text ) );
return ( sIncreasing ? result : -result );
}
static S32 QSORT_CALLBACK numCompare(const void *a,const void *b)
{
GuiTextListCtrl::Entry *ea = (GuiTextListCtrl::Entry *) (a);
GuiTextListCtrl::Entry *eb = (GuiTextListCtrl::Entry *) (b);
const char* aCol = getColumn( ea->text );
const char* bCol = getColumn( eb->text );
F32 result = dAtof(aCol) - dAtof(bCol);
S32 res = result < 0 ? -1 : (result > 0 ? 1 : 0);
return ( sIncreasing ? res : -res );
}
GuiTextListCtrl::GuiTextListCtrl()
{
VECTOR_SET_ASSOCIATION(mList);
VECTOR_SET_ASSOCIATION(mColumnOffsets);
mActive = true;
mSize.set(1, 0);
mColumnOffsets.push_back(0);
mFitParentWidth = true;
mClipColumnText = false;
}
void GuiTextListCtrl::initPersistFields()
{
addField("columns", TypeS32Vector, Offset(mColumnOffsets, GuiTextListCtrl), "A vector of column offsets. The number of values determines the number of columns in the table.\n" );
addField("fitParentWidth", TypeBool, Offset(mFitParentWidth, GuiTextListCtrl), "If true, the width of this control will match the width of its parent.\n");
addField("clipColumnText", TypeBool, Offset(mClipColumnText, GuiTextListCtrl), "If true, text exceeding a column's given width will get clipped.\n" );
Parent::initPersistFields();
}
bool GuiTextListCtrl::onWake()
{
if(!Parent::onWake())
return false;
setSize(mSize);
return true;
}
U32 GuiTextListCtrl::getSelectedId()
{
if (mSelectedCell.y == -1)
return InvalidId;
return mList[mSelectedCell.y].id;
}
U32 GuiTextListCtrl::getSelectedRow()
{
return mSelectedCell.y;
}
bool GuiTextListCtrl::cellSelected(Point2I cell)
{
// Is the selection being cleared?
if( cell.x == -1 && cell.y == -1)
return Parent::cellSelected(cell);
// Do not allow selection of inactive cells
if (cell.y >= 0 && cell.y < mSize.y && mList[cell.y].active)
return Parent::cellSelected(cell);
else
return false;
}
void GuiTextListCtrl::onCellSelected(Point2I cell)
{
onSelect_callback(Con::getIntArg(mList[cell.y].id), mList[cell.y].text);
execConsoleCallback();
}
void GuiTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver)
{
if ( mList[cell.y].active )
{
if (selected || (mProfile->mMouseOverSelected && mouseOver))
{
RectI highlightRect = RectI(offset.x, offset.y, mCellSize.x, mCellSize.y);
highlightRect.inset( 0, -1 );
renderFilledBorder( highlightRect, mProfile->mBorderColorHL, mProfile->mFillColorHL);
GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL);
}
else
GFX->getDrawUtil()->setBitmapModulation(mouseOver ? mProfile->mFontColorHL : mProfile->mFontColor);
}
else
GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColorNA );
const char *text = mList[cell.y].text;
for(U32 index = 0; index < mColumnOffsets.size(); index++)
{
const char *nextCol = dStrchr(text, '\t');
if(mColumnOffsets[index] >= 0)
{
dsize_t slen;
if(nextCol)
slen = nextCol - text;
else
slen = dStrlen(text);
Point2I pos(offset.x + 4 + mColumnOffsets[index], offset.y);
RectI saveClipRect;
bool clipped = false;
if(mClipColumnText && (index != (mColumnOffsets.size() - 1)))
{
saveClipRect = GFX->getClipRect();
RectI clipRect(pos, Point2I(mColumnOffsets[index+1] - mColumnOffsets[index] - 4, mCellSize.y));
if(clipRect.intersect(saveClipRect))
{
clipped = true;
GFX->setClipRect( clipRect );
}
}
GFX->getDrawUtil()->drawTextN(mFont, pos, text, slen, mProfile->mFontColors);
if(clipped)
GFX->setClipRect( saveClipRect );
}
if(!nextCol)
break;
text = nextCol+1;
}
}
U32 GuiTextListCtrl::getRowWidth(Entry *row)
{
U32 width = 1;
const char *text = row->text;
for(U32 index = 0; index < mColumnOffsets.size(); index++)
{
const char *nextCol = dStrchr(text, '\t');
U32 textWidth;
if(nextCol)
textWidth = mFont->getStrNWidth((const UTF8*)text, nextCol - text);
else
textWidth = mFont->getStrWidth((const UTF8*)text);
if(mColumnOffsets[index] >= 0)
width = getMax(width, mColumnOffsets[index] + textWidth);
if(!nextCol)
break;
text = nextCol+1;
}
return width;
}
void GuiTextListCtrl::insertEntry(U32 id, const char *text, S32 index)
{
Entry e;
e.text = dStrdup(text);
e.id = id;
e.active = true;
if(!mList.size())
mList.push_back(e);
else
{
if(index > mList.size())
index = mList.size();
mList.insert(index);
mList[index] = e;
}
setSize(Point2I(1, mList.size()));
}
void GuiTextListCtrl::addEntry(U32 id, const char *text)
{
Entry e;
e.text = dStrdup(text);
e.id = id;
e.active = true;
mList.push_back(e);
setSize(Point2I(1, mList.size()));
}
void GuiTextListCtrl::setEntry(U32 id, const char *text)
{
S32 e = findEntryById(id);
if(e == -1)
addEntry(id, text);
else
{
dFree(mList[e].text);
mList[e].text = dStrdup(text);
// Still have to call this to make sure cells are wide enough for new values:
setSize( Point2I( 1, mList.size() ) );
}
setUpdate();
}
void GuiTextListCtrl::setEntryActive(U32 id, bool active)
{
S32 index = findEntryById( id );
if ( index == -1 )
return;
if ( mList[index].active != active )
{
mList[index].active = active;
// You can't have an inactive entry selected...
if ( !active && mSelectedCell.y >= 0 && mSelectedCell.y < mList.size()
&& mList[mSelectedCell.y].id == id )
setSelectedCell( Point2I( -1, -1 ) );
setUpdate();
}
}
S32 GuiTextListCtrl::findEntryById(U32 id)
{
for(U32 i = 0; i < mList.size(); i++)
if(mList[i].id == id)
return i;
return -1;
}
S32 GuiTextListCtrl::findEntryByText(const char *text)
{
for(U32 i = 0; i < mList.size(); i++)
if(!dStricmp(mList[i].text, text))
return i;
return -1;
}
bool GuiTextListCtrl::isEntryActive(U32 id)
{
S32 index = findEntryById( id );
if ( index == -1 )
return( false );
return( mList[index].active );
}
void GuiTextListCtrl::setSize(Point2I newSize)
{
mSize = newSize;
if ( bool( mFont ) )
{
if ( mSize.x == 1 && mFitParentWidth )
{
GuiScrollCtrl* parent = dynamic_cast<GuiScrollCtrl *>(getParent());
if ( parent )
mCellSize.x = parent->getContentExtent().x;
}
else
{
// Find the maximum width cell:
S32 maxWidth = 1;
for ( U32 i = 0; i < mList.size(); i++ )
{
U32 rWidth = getRowWidth( &mList[i] );
if ( rWidth > maxWidth )
maxWidth = rWidth;
}
mCellSize.x = maxWidth + 8;
}
mCellSize.y = mFont->getHeight() + 2;
}
Point2I newExtent( newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y );
setExtent( newExtent );
}
void GuiTextListCtrl::clear()
{
while (mList.size())
removeEntry(mList[0].id);
mMouseOverCell.set( -1, -1 );
setSelectedCell(Point2I(-1, -1));
}
void GuiTextListCtrl::sort(U32 column, bool increasing)
{
if (getNumEntries() < 2)
return;
sortColumn = column;
sIncreasing = increasing;
dQsort((void *)&(mList[0]), mList.size(), sizeof(Entry), textCompare);
}
void GuiTextListCtrl::sortNumerical( U32 column, bool increasing )
{
if ( getNumEntries() < 2 )
return;
sortColumn = column;
sIncreasing = increasing;
dQsort( (void*) &( mList[0] ), mList.size(), sizeof( Entry ), numCompare );
}
void GuiTextListCtrl::onRemove()
{
clear();
Parent::onRemove();
}
U32 GuiTextListCtrl::getNumEntries()
{
return mList.size();
}
void GuiTextListCtrl::removeEntryByIndex(S32 index)
{
if(index < 0 || index >= mList.size())
return;
dFree(mList[index].text);
mList.erase(index);
setSize(Point2I( 1, mList.size()));
setSelectedCell(Point2I(-1, -1));
}
void GuiTextListCtrl::removeEntry(U32 id)
{
S32 index = findEntryById(id);
removeEntryByIndex(index);
}
const char *GuiTextListCtrl::getSelectedText()
{
if (mSelectedCell.y == -1)
return NULL;
return mList[mSelectedCell.y].text;
}
const char *GuiTextListCtrl::getScriptValue()
{
return getSelectedText();
}
void GuiTextListCtrl::setScriptValue(const char *val)
{
S32 e = findEntryByText(val);
if(e == -1)
setSelectedCell(Point2I(-1, -1));
else
setSelectedCell(Point2I(0, e));
}
bool GuiTextListCtrl::onKeyDown( const GuiEvent &event )
{
//if this control is a dead end, make sure the event stops here
if ( !mVisible || !mActive || !mAwake )
return true;
S32 yDelta = 0;
switch( event.keyCode )
{
case KEY_RETURN:
execAltConsoleCallback();
break;
case KEY_LEFT:
case KEY_UP:
if ( mSelectedCell.y > 0 )
{
mSelectedCell.y--;
yDelta = -mCellSize.y;
}
break;
case KEY_DOWN:
case KEY_RIGHT:
if ( mSelectedCell.y < ( mList.size() - 1 ) )
{
mSelectedCell.y++;
yDelta = mCellSize.y;
}
break;
case KEY_HOME:
if ( mList.size() )
{
mSelectedCell.y = 0;
yDelta = -(mCellSize.y * mList.size() + 1 );
}
break;
case KEY_END:
if ( mList.size() )
{
mSelectedCell.y = mList.size() - 1;
yDelta = (mCellSize.y * mList.size() + 1 );
}
break;
case KEY_DELETE:
if ( mSelectedCell.y >= 0 && mSelectedCell.y < mList.size() )
onDeleteKey_callback(Con::getIntArg( mList[mSelectedCell.y].id ) );
break;
default:
return( Parent::onKeyDown( event ) );
break;
};
GuiScrollCtrl* parent = dynamic_cast<GuiScrollCtrl *>(getParent());
if ( parent )
parent->scrollDelta( 0, yDelta );
return ( true );
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
DefineEngineMethod( GuiTextListCtrl, getSelectedId, S32, (),,
"@brief Get the ID of the currently selected item.\n\n"
"@tsexample\n"
"// Acquire the ID of the selected item in the list.\n"
"%id = %thisGuiTextListCtrl.getSelectedId();\n"
"@endtsexample\n\n"
"@return The id of the selected item in the list.\n\n"
"@see GuiControl")
{
return object->getSelectedId();
}
DefineEngineMethod( GuiTextListCtrl, setSelectedById, void, (int id),,
"@brief Finds the specified entry by id, then marks its row as selected.\n\n"
"@param id Entry within the text list to make selected.\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"5\";\n\n"
"// Inform the GuiTextListCtrl control to set the defined id entry as selected\n"
"%thisGuiTextListCtrl.setSelectedById(%id);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
S32 index = object->findEntryById(id);
if(index < 0)
return ;
object->setSelectedCell(Point2I(0, index));
}
DefineEngineMethod( GuiTextListCtrl, setSelectedRow, void, (int rowNum),,
"@briefSelects the specified row.\n\n"
"@param rowNum Row number to set selected.\n"
"@tsexample\n"
"// Define the row number to set selected\n"
"%rowNum = \"4\";\n\n"
"%guiTextListCtrl.setSelectedRow(%rowNum);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->setSelectedCell( Point2I( 0, rowNum ) );
}
DefineEngineMethod( GuiTextListCtrl, getSelectedRow, S32, (),,
"@brief Returns the selected row index (not the row ID).\n\n"
"@tsexample\n"
"// Acquire the selected row index\n"
"%rowIndex = %thisGuiTextListCtrl.getSelectedRow();\n"
"@endtsexample\n\n"
"@return Index of the selected row\n\n"
"@see GuiControl")
{
return object->getSelectedRow();
}
DefineEngineMethod( GuiTextListCtrl, clearSelection, void, (),,
"@brief Set the selection to nothing.\n\n"
"@tsexample\n"
"// Deselect anything that is currently selected\n"
"%thisGuiTextListCtrl.clearSelection();\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->setSelectedCell(Point2I(-1, -1));
}
DefineEngineMethod( GuiTextListCtrl, addRow, S32, (int id, const char* text, int index),(0,"",-1),
"@brief Adds a new row at end of the list with the defined id and text.\n"
"If index is used, then the new row is inserted at the row location of 'index'.\n\n"
"@param id Id of the new row.\n"
"@param text Text to display at the new row.\n"
"@param index Index to insert the new row at. If not used, new row will be placed at the end of the list.\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"4\";\n\n"
"// Define the text to display\n"
"%text = \"Display Text\"\n\n"
"// Define the index (optional)\n"
"%index = \"2\"\n\n"
"// Inform the GuiTextListCtrl control to add the new row with the defined information.\n"
"%rowIndex = %thisGuiTextListCtrl.addRow(%id,%text,%index);\n"
"@endtsexample\n\n"
"@return Returns the row index of the new row. If 'index' was defined, then this just returns the number of rows in the list.\n\n"
"@see References")
{
S32 ret = object->mList.size();
if(index == -1)
object->addEntry(id, text);
else
object->insertEntry(id, text, index);
return ret;
}
DefineEngineMethod( GuiTextListCtrl, setRowById, void, (int id, const char* text),,
"@brief Sets the text at the defined id.\n\n"
"@param id Id to change.\n"
"@param text Text to use at the Id.\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"4\";\n\n"
"// Define the text\n"
"%text = \"Text To Display\";\n\n"
"// Inform the GuiTextListCtrl control to display the defined text at the defined id\n"
"%thisGuiTextListCtrl.setRowById(%id,%text);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->setEntry(id, text);
}
DefineEngineMethod( GuiTextListCtrl, sort, void, ( int columnId, bool increasing ), ( true ),
"@brief Performs a standard (alphabetical) sort on the values in the specified column.\n\n"
"@param columnId Column ID to perform the sort on.\n"
"@param increasing If false, sort will be performed in reverse.\n"
"@tsexample\n"
"// Define the columnId\n"
"%id = \"1\";\n\n"
"// Define if we are increasing or not\n"
"%increasing = \"false\";\n\n"
"// Inform the GuiTextListCtrl to perform the sort operation\n"
"%thisGuiTextListCtrl.sort(%id,%increasing);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->sort( columnId, increasing );
}
DefineEngineMethod( GuiTextListCtrl, sortNumerical, void, (int columnID, bool increasing), ( true ),
"@brief Perform a numerical sort on the values in the specified column.\n\n"
"Detailed description\n\n"
"@param columnId Column ID to perform the sort on.\n"
"@param increasing If false, sort will be performed in reverse.\n"
"@tsexample\n"
"// Define the columnId\n"
"%id = \"1\";\n\n"
"// Define if we are increasing or not\n"
"%increasing = \"false\";\n\n"
"// Inform the GuiTextListCtrl to perform the sort operation\n"
"%thisGuiTextListCtrl.sortNumerical(%id,%increasing);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->sortNumerical( columnID, increasing );
}
DefineEngineMethod( GuiTextListCtrl, clear, void, (),,
"@brief Clear the list.\n\n"
"@tsexample\n"
"// Inform the GuiTextListCtrl control to clear its contents\n"
"%thisGuiTextListCtrl.clear();\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->clear();
}
DefineEngineMethod( GuiTextListCtrl, rowCount, S32, (),,
"@brief Get the number of rows.\n\n"
"@tsexample\n"
"// Get the number of rows in the list\n"
"%rowCount = %thisGuiTextListCtrl.rowCount();\n"
"@endtsexample\n\n"
"@return Number of rows in the list.\n\n"
"@see GuiControl")
{
return object->getNumEntries();
}
DefineEngineMethod( GuiTextListCtrl, getRowId, S32, (int index),,
"@brief Get the row ID for an index.\n\n"
"@param index Index to get the RowID at\n"
"@tsexample\n"
"// Define the index\n"
"%index = \"3\";\n\n"
"// Request the row ID at the defined index\n"
"%rowId = %thisGuiTextListCtrl.getRowId(%index);\n"
"@endtsexample\n\n"
"@return RowId at the defined index.\n\n"
"@see GuiControl")
{
if(index >= object->getNumEntries())
return -1;
return object->mList[index].id;
}
DefineEngineMethod( GuiTextListCtrl, getRowTextById, const char*, (int id),,
"@brief Get the text of a row with the specified id.\n\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"4\";\n\n"
"// Inform the GuiTextListCtrl control to return the text at the defined row id\n"
"%rowText = %thisGuiTextListCtrl.getRowTextById(%id);\n"
"@endtsexample\n\n"
"@return Row text at the requested row id.\n\n"
"@see GuiControl")
{
S32 index = object->findEntryById(id);
if(index < 0)
return "";
return object->mList[index].text;
}
DefineEngineMethod( GuiTextListCtrl, getRowNumById, S32, (int id),,
"@brief Get the row number for a specified id.\n\n"
"@param id Id to get the row number at\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"4\";\n\n"
"// Request the row number from the GuiTextListCtrl control at the defined id.\n"
"%rowNumber = %thisGuiTextListCtrl.getRowNumById(%id);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
S32 index = object->findEntryById(id);
if(index < 0)
return -1;
return index;
}
DefineEngineMethod( GuiTextListCtrl, getRowText, const char*, (int index),,
"@brief Get the text of the row with the specified index.\n\n"
"@param index Row index to acquire the text at.\n"
"@tsexample\n"
"// Define the row index\n"
"%index = \"5\";\n\n"
"// Request the text from the row at the defined index\n"
"%rowText = %thisGuiTextListCtrl.getRowText(%index);\n"
"@endtsexample\n\n"
"@return Text at the defined row index.\n\n"
"@see GuiControl")
{
if(index < 0 || index >= object->mList.size())
return "";
return object->mList[index].text;
}
DefineEngineMethod( GuiTextListCtrl, removeRowById, void, (int id),,
"@brief Remove row with the specified id.\n\n"
"@param id Id to remove the row entry at\n"
"@tsexample\n"
"// Define the id\n"
"%id = \"4\";\n\n"
"// Inform the GuiTextListCtrl control to remove the row at the defined id\n"
"%thisGuiTextListCtrl.removeRowById(%id);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->removeEntry(id);
}
DefineEngineMethod( GuiTextListCtrl, removeRow, void, (int index),,
"@brief Remove a row from the table, based on its index.\n\n"
"@param index Row index to remove from the list.\n"
"@tsexample\n"
"// Define the row index\n"
"%index = \"4\";\n\n"
"// Inform the GuiTextListCtrl control to remove the row at the defined row index\n"
"%thisGuiTextListCtrl.removeRow(%index);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->removeEntryByIndex(index);
}
DefineEngineMethod( GuiTextListCtrl, scrollVisible, void, (int rowNum),,
"@brief Scroll so the specified row is visible\n\n"
"@param rowNum Row number to make visible\n"
"@tsexample\n"
"// Define the row number to make visible\n"
"%rowNum = \"4\";\n\n"
"// Inform the GuiTextListCtrl control to scroll the list so the defined rowNum is visible.\n"
"%thisGuiTextListCtrl.scrollVisible(%rowNum);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->scrollCellVisible(Point2I(0, rowNum));
}
DefineEngineMethod( GuiTextListCtrl, findTextIndex, S32, (const char* needle),,
"@brief Find needle in the list, and return the row number it was found in.\n\n"
"@param needle Text to find in the list.\n"
"@tsexample\n"
"// Define the text to find in the list\n"
"%needle = \"Text To Find\";\n\n"
"// Request the row number that contains the defined text to find\n\n"
"%rowNumber = %thisGuiTextListCtrl.findTextIndex(%needle);\n\n"
"@endtsexample\n\n"
"@return Row number that the defined text was found in,\n\n"
"@see GuiControl")
{
return( object->findEntryByText(needle) );
}
DefineEngineMethod( GuiTextListCtrl, setRowActive, void, (int rowNum, bool active),,
"@brief Mark a specified row as active/not.\n\n"
"@param rowNum Row number to change the active state.\n"
"@param active Boolean active state to set the row number.\n"
"@tsexample\n"
"// Define the row number\n"
"%rowNum = \"4\";\n\n"
"// Define the boolean active state\n"
"%active = \"true\";\n\n"
"// Informthe GuiTextListCtrl control to set the defined active state at the defined row number.\n"
"%thisGuiTextListCtrl.setRowActive(%rowNum,%active);\n"
"@endtsexample\n\n"
"@see GuiControl")
{
object->setEntryActive( U32( rowNum ), active );
}
DefineEngineMethod( GuiTextListCtrl, isRowActive, bool, (int rowNum),,
"@brief Check if the specified row is currently active or not.\n\n"
"@param rowNum Row number to check the active state.\n"
"@tsexample\n"
"// Define the row number\n"
"%rowNum = \"5\";\n\n"
"// Request the active state of the defined row number from the GuiTextListCtrl control.\n"
"%rowActiveState = %thisGuiTextListCtrl.isRowActive(%rowNum);\n"
"@endtsexample\n\n"
"@return Active state of the defined row number.\n\n"
"@see GuiControl")
{
return( object->isEntryActive( U32( rowNum ) ) );
}

View file

@ -0,0 +1,114 @@
//-----------------------------------------------------------------------------
// 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 _GUITEXTLISTCTRL_H_
#define _GUITEXTLISTCTRL_H_
#ifndef _GUIARRAYCTRL_H_
#include "gui/core/guiArrayCtrl.h"
#endif
class GuiTextListCtrl : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
public:
struct Entry
{
char *text;
U32 id;
bool active;
};
Vector<Entry> mList;
protected:
enum ScrollConst
{
UP = 0,
DOWN = 1
};
enum {
InvalidId = 0xFFFFFFFF
};
Vector<S32> mColumnOffsets;
bool mFitParentWidth;
bool mClipColumnText;
U32 getRowWidth(Entry *row);
bool cellSelected(Point2I cell);
void onCellSelected(Point2I cell);
public:
GuiTextListCtrl();
DECLARE_CONOBJECT(GuiTextListCtrl);
DECLARE_CATEGORY( "Gui Lists" );
DECLARE_DESCRIPTION( "A control that displays text in tabular form." );
DECLARE_CALLBACK( void, onSelect, (const char* cellid, const char* text));
DECLARE_CALLBACK( void, onDeleteKey, ( const char* id ));
static void initPersistFields();
virtual void setCellSize( const Point2I &size ){ mCellSize = size; }
virtual void getCellSize( Point2I &size ){ size = mCellSize; }
const char *getScriptValue();
void setScriptValue(const char *value);
U32 getNumEntries();
void clear();
virtual void addEntry(U32 id, const char *text);
virtual void insertEntry(U32 id, const char *text, S32 index);
void setEntry(U32 id, const char *text);
void setEntryActive(U32 id, bool active);
S32 findEntryById(U32 id);
S32 findEntryByText(const char *text);
bool isEntryActive(U32 id);
U32 getEntryId(U32 index);
bool onWake();
void removeEntry(U32 id);
virtual void removeEntryByIndex(S32 id);
virtual void sort(U32 column, bool increasing = true);
virtual void sortNumerical(U32 column, bool increasing = true);
U32 getSelectedId();
U32 getSelectedRow();
const char *getSelectedText();
bool onKeyDown(const GuiEvent &event);
virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver);
void setSize(Point2I newSize);
void onRemove();
void addColumnOffset(S32 offset) { mColumnOffsets.push_back(offset); }
void clearColumnOffsets() { mColumnOffsets.clear(); }
};
#endif //_GUI_TEXTLIST_CTRL_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,606 @@
//-----------------------------------------------------------------------------
// 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_TREEVIEWCTRL_H
#define _GUI_TREEVIEWCTRL_H
#include "core/bitSet.h"
#include "math/mRect.h"
#include "gfx/gFont.h"
#include "gui/core/guiControl.h"
#include "gui/core/guiArrayCtrl.h"
class GuiTextEditCtrl;
//------------------------------------------------------------------------------
class GuiTreeViewCtrl : public GuiArrayCtrl
{
private:
typedef GuiArrayCtrl Parent;
public:
/// @section GuiControl_Intro Introduction
/// @nosubgrouping
///
class Item
{
public:
enum ItemState
{
Selected = BIT( 0 ),
Expanded = BIT( 1 ),
Marked = BIT( 2 ), ///< Marked items are drawn with a border around them. This is
/// different than "Selected" because it can only be set by script.
Filtered = BIT( 3 ), ///< Whether the item is currently filtered out.
MouseOverBmp = BIT( 4 ),
MouseOverText = BIT( 5 ),
MouseOverIcon = BIT( 6 ),
InspectorData = BIT( 7 ), ///< Set if we're representing some inspector
/// info (ie, use mInspectorInfo, not mScriptInfo)
VirtualParent = BIT( 8 ), ///< This indicates that we should be rendered as
/// a parent even though we don't have any children.
/// This is useful for preventing scenarios where
/// we might want to create thousands of
/// Items that might never be shown (for instance
/// if we're browsing the object hierarchy in
/// Torque, which might have thousands of objects).
RebuildVisited = BIT( 9 ), ///< Rebuild traversal for virtual parents has visited and validated this item.
ShowObjectId = BIT( 10 ),
ShowClassName = BIT( 11 ),
ShowObjectName = BIT( 12 ),
ShowInternalName = BIT( 13 ),
ShowClassNameForUnnamed = BIT( 14 )
};
GuiTreeViewCtrl* mParentControl;
BitSet32 mState;
SimObjectPtr< GuiControlProfile > mProfile;
S16 mId;
U16 mTabLevel;
Item* mParent;
Item* mChild;
Item* mNext;
Item* mPrevious;
String mTooltip;
S32 mIcon; //stores the icon that will represent the item in the tree
S32 mDataRenderWidth; /// this stores the pixel width needed
/// to render the item's data in the
/// onRenderCell function to optimize
/// for speed.
Item( GuiTreeViewCtrl* parent, GuiControlProfile *pProfile );
~Item();
struct ScriptTag
{
S8 mNormalImage;
S8 mExpandedImage;
StringTableEntry mText;
StringTableEntry mValue;
} mScriptInfo;
struct InspectorTag
{
SimObjectPtr<SimObject> mObject;
} mInspectorInfo;
/// @name Get Methods
/// @{
///
S8 getNormalImage() const;
S8 getExpandedImage() const;
StringTableEntry getText();
StringTableEntry getValue();
inline const S16 getID() const { return mId; };
SimObject *getObject();
U32 getDisplayTextLength();
S32 getDisplayTextWidth(GFont *font);
void getDisplayText(U32 bufLen, char *buf);
bool hasObjectBasedTooltip();
void getTooltipText(U32 bufLen, char *buf);
/// @}
/// @name Set Methods
/// @{
/// Set whether an item is expanded or not (showing children or having them hidden)
void setExpanded( const bool f = true );
/// Set the image to display when an item IS expanded
void setExpandedImage(const S8 id);
/// Set the image to display when an item is NOT expanded
void setNormalImage(const S8 id);
/// Assign a SimObject pointer to an inspector data item
void setObject(SimObject *obj);
/// Set the items displayable text (caption)
void setText(StringTableEntry txt);
/// Set the items script value (data)
void setValue(StringTableEntry val);
/// Set the items virtual parent flag
void setVirtualParent( bool value );
/// Set whether the item is filtered out or not.
void setFiltered( bool value ) { mState.set( Filtered ); }
/// @}
/// @name State Retrieval
/// @{
/// Returns true if this item is expanded. For
/// inspector objects, the expansion is stored
/// on the SimObject, for other things we use our
/// bit vector.
bool isExpanded() const;
/// Return whether the item is current filtered out or not.
/// @note Parent items may be filtered and yet still be visible if they have
/// children that are not filtered.
bool isFiltered() const { return mState.test( Filtered ); }
/// Returns true if an item is inspector data
/// or false if it's just an item.
bool isInspectorData() const { return mState.test(InspectorData); };
/// Returns true if we should show the expand art
/// and make the item interact with the mouse as if
/// it were a parent.
bool isParent() const;
/// Return true if text label for inspector item should include internal name only.
bool showInternalNameOnly() const { return mState.test( ShowInternalName ) && !mState.test( ShowObjectName | ShowClassName | ShowObjectId ); }
/// Return true if text label for inspector item should include object name only.
bool showObjectNameOnly() const { return mState.test( ShowObjectName ) && !mState.test( ShowInternalName | ShowClassName | ShowObjectId ); }
/// @}
/// @name Searching Methods
/// @{
/// Find a regular data item by it's script name.
Item* findChildByName( const char* name );
/// Find an inspector data item by it's SimObject pointer
Item* findChildByValue(const SimObject *obj);
/// Find a regular data item by it's script value
Item* findChildByValue(StringTableEntry Value);
/// @}
/// Sort the childs of the item by their text.
///
/// @param caseSensitive If true, sorting is case-sensitive.
/// @param traverseHierarchy If true, also triggers a sort() on all child items.
/// @param parentsFirst If true, parents are grouped before children in the resulting sort.
void sort( bool caseSensitive = true, bool traverseHierarchy = false, bool parentsFirst = false );
private:
void _connectMonitors();
void _disconnectMonitors();
};
friend class Item; // _onInspectorSetObjectModified
/// @name Enums
/// @{
///
enum TreeState
{
RebuildVisible = BIT(0), ///< Temporary flag, we have to rebuild the tree.
IsInspector = BIT(1), ///< We are mapping a SimObject hierarchy.
IsEditable = BIT(2), ///< We allow items to be moved around.
ShowTreeLines = BIT(3), ///< Should we render tree lines or just icons?
BuildingVisTree = BIT(4), ///< We are currently building the visible tree (prevent recursion)
};
protected:
enum
{
MaxIcons = 32,
};
enum Icons
{
Default1 = 0,
SimGroup1,
SimGroup2,
SimGroup3,
SimGroup4,
Hidden,
Lock1,
Lock2,
Default,
Icon31,
Icon32
};
enum mDragMidPointFlags
{
NomDragMidPoint,
AbovemDragMidPoint,
BelowmDragMidPoint
};
///
enum HitFlags
{
OnIndent = BIT(0),
OnImage = BIT(1),
OnIcon = BIT(2),
OnText = BIT(3),
OnRow = BIT(4),
};
///
enum BmpIndices
{
BmpFirstChild,
BmpLastChild,
BmpChild,
BmpExp,
BmpExpN,
BmpExpP,
BmpExpPN,
BmpCon,
BmpConN,
BmpConP,
BmpConPN,
BmpLine,
BmpGlow,
};
/// @}
/// @name Callbacks
/// @{
DECLARE_CALLBACK( bool, onDeleteObject, ( SimObject* object ) );
DECLARE_CALLBACK( bool, isValidDragTarget, ( S32 id, const char* value ) );
DECLARE_CALLBACK( void, onDefineIcons, () );
DECLARE_CALLBACK( void, onAddGroupSelected, ( SimGroup* group ) );
DECLARE_CALLBACK( void, onAddSelection, ( S32 itemOrObjectId, bool isLastSelection ) );
DECLARE_CALLBACK( void, onSelect, ( S32 itemOrObjectId ) );
DECLARE_CALLBACK( void, onInspect, ( S32 itemOrObjectId ) );
DECLARE_CALLBACK( void, onRemoveSelection, ( S32 itemOrObjectId ) );
DECLARE_CALLBACK( void, onUnselect, ( S32 itemOrObjectId ) );
DECLARE_CALLBACK( void, onDeleteSelection, () );
DECLARE_CALLBACK( void, onObjectDeleteCompleted, () );
DECLARE_CALLBACK( void, onKeyDown, ( S32 modifier, S32 keyCode ) );
DECLARE_CALLBACK( void, onMouseUp, ( S32 hitItemId, S32 mouseClickCount ) );
DECLARE_CALLBACK( void, onMouseDragged, () );
DECLARE_CALLBACK( void, onRightMouseDown, ( S32 itemId, const Point2I& mousePos, SimObject* object = NULL ) );
DECLARE_CALLBACK( void, onRightMouseUp, ( S32 itemId, const Point2I& mousePos, SimObject* object = NULL ) );
DECLARE_CALLBACK( void, onBeginReparenting, () );
DECLARE_CALLBACK( void, onEndReparenting, () );
DECLARE_CALLBACK( void, onReparent, ( S32 itemOrObjectId, S32 oldParentItemOrObjectId, S32 newParentItemOrObjectId ) );
DECLARE_CALLBACK( void, onDragDropped, () );
DECLARE_CALLBACK( void, onAddMultipleSelectionBegin, () );
DECLARE_CALLBACK( void, onAddMultipleSelectionEnd, () );
DECLARE_CALLBACK( bool, canRenameObject, ( SimObject* object ) );
DECLARE_CALLBACK( bool, handleRenameObject, ( const char* newName, SimObject* object ) );
DECLARE_CALLBACK( void, onClearSelection, () );
/// @}
///
Vector<Item*> mItems;
Vector<Item*> mVisibleItems;
Vector<Item*> mSelectedItems;
/// Used for tracking stuff that was selected, but may not have been
/// created at time of selection.
Vector<S32> mSelected;
S32 mItemCount;
/// We do our own free list, as we we want to be able to recycle
/// item ids and do some other clever things.
Item* mItemFreeList;
Item* mRoot;
S32 mMaxWidth;
S32 mSelectedItem;
S32 mDraggedToItem;
S32 mStart;
/// A combination of TreeState flags.
BitSet32 mFlags;
Item* mPossibleRenameItem;
Item* mRenamingItem;
Item* mTempItem;
GuiTextEditCtrl* mRenameCtrl;
/// Current filter that determines which items in the tree are displayed and which are hidden.
String mFilterText;
/// If true, a trace of actions taken by the control is logged to the console. Can
/// be turned on with the setDebug() script method.
bool mDebug;
GFXTexHandle mIconTable[MaxIcons];
S32 mTabSize;
S32 mTextOffset;
bool mFullRowSelect;
S32 mItemHeight;
bool mDestroyOnSleep;
bool mSupportMouseDragging;
bool mMultipleSelections;
bool mDeleteObjectAllowed;
bool mDragToItemAllowed;
bool mClearAllOnSingleSelection; ///< When clicking on an already selected item, clear all other selections
bool mCompareToObjectID;
/// Used to hide the root tree element, defaults to true.
bool mShowRoot;
/// If true, object IDs will be included in inspector tree item labels.
bool mShowObjectIds;
/// If true, class names will be included in inspector tree item labels.
bool mShowClassNames;
/// If true, object names will be included in inspector tree item labels.
bool mShowObjectNames;
/// If true, internal names will be included in inspector tree item labels.
bool mShowInternalNames;
/// If true, class names will be used as object names for unnamed objects.
bool mShowClassNameForUnnamedObjects;
/// If true then tooltips will be automatically
/// generated for all Inspector items
bool mUseInspectorTooltips;
/// If true then only render item tooltips if the item
/// extends past the displayable width
bool mTooltipOnWidthOnly;
/// If true clicking on a selected item ( that is an object )
/// will allow you to rename it.
bool mCanRenameObjects;
/// If true then object renaming operates on the internalName rather than
/// the object name.
bool mRenameInternal;
S32 mCurrentDragCell;
S32 mPreviousDragCell;
S32 mDragMidPoint;
bool mMouseDragged;
bool mDragStartInSelection;
Point2I mMouseDownPoint;
StringTableEntry mBitmapBase;
GFXTexHandle mTexRollover;
GFXTexHandle mTexSelected;
ColorI mAltFontColor;
ColorI mAltFontColorHL;
ColorI mAltFontColorSE;
SimObjectPtr<SimObject> mRootObject;
void _destroyChildren( Item* item, Item* parent, bool deleteObjects=true);
void _destroyItem( Item* item, bool deleteObject=true);
void _destroyTree();
void _deleteItem(Item* item);
void _buildItem(Item* item, U32 tabLevel, bool bForceFullUpdate = false);
Item* _findItemByAmbiguousId( S32 itemOrObjectId, bool buildVirtual = true );
void _expandObjectHierarchy( SimGroup* group );
bool _hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags);
S32 getInspectorItemIconsWidth(Item* & item);
virtual bool onVirtualParentBuild(Item *item, bool bForceFullUpdate = false);
virtual bool onVirtualParentExpand(Item *item);
virtual bool onVirtualParentCollapse(Item *item);
virtual void onItemSelected( Item *item );
virtual void onRemoveSelection( Item *item );
virtual void onClearSelection() {};
Item* addInspectorDataItem(Item *parent, SimObject *obj);
virtual bool isValidDragTarget( Item* item );
bool _isRootLevelItem( Item* item ) const
{
return ( item == mRoot && mShowRoot ) || ( item->mParent == mRoot && !mShowRoot );
}
/// For inspector tree views, this is hooked to the SetModificationSignal of sets
/// so that the tree view knows when it needs to refresh.
void _onInspectorSetObjectModified( SetModification modification, SimSet* set, SimObject* object );
public:
GuiTreeViewCtrl();
virtual ~GuiTreeViewCtrl();
/// Used for syncing the mSelected and mSelectedItems lists.
void syncSelection();
void lockSelection(bool lock);
void hideSelection(bool hide);
void toggleLockSelection();
void toggleHideSelection();
virtual bool canAddSelection( Item *item ) { return true; };
void addSelection(S32 itemId, bool update = true, bool isLastSelection = true);
const Vector< Item* >& getSelectedItems() const { return mSelectedItems; }
const Vector< S32 >& getSelected() const { return mSelected; }
bool isSelected(S32 itemId)
{
return isSelected( getItem( itemId ) );
}
bool isSelected(Item *item)
{
if ( !item )
return false;
return mSelectedItems.contains( item );
}
void setDebug( bool value ) { mDebug = value; }
/// Should use addSelection and removeSelection when calling from script
/// instead of setItemSelected. Use setItemSelected when you want to select
/// something in the treeview as it has script call backs.
void removeSelection(S32 itemId);
/// Sets the flag of the item with the matching itemId.
bool setItemSelected(S32 itemId, bool select);
bool setItemExpanded(S32 itemId, bool expand);
bool setItemValue(S32 itemId, StringTableEntry Value);
const char * getItemText(S32 itemId);
const char * getItemValue(S32 itemId);
StringTableEntry getTextToRoot(S32 itemId, const char *delimiter = "");
Item* getRootItem() const { return mRoot; }
Item * getItem(S32 itemId) const;
Item * createItem(S32 icon);
bool editItem( S32 itemId, const char* newText, const char* newValue );
bool markItem( S32 itemId, bool mark );
bool isItemSelected( S32 itemId );
// insertion/removal
void unlinkItem(Item * item);
S32 insertItem(S32 parentId, const char * text, const char * value = "", const char * iconString = "", S16 normalImage = 0, S16 expandedImage = 1);
bool removeItem(S32 itemId, bool deleteObjects=true);
void removeAllChildren(S32 itemId); // Remove all children of the given item
bool buildIconTable(const char * icons);
bool setAddGroup(SimObject * obj);
S32 getIcon(const char * iconString);
// tree items
const S32 getFirstRootItem() const;
S32 getChildItem(S32 itemId);
S32 getParentItem(S32 itemId);
S32 getNextSiblingItem(S32 itemId);
S32 getPrevSiblingItem(S32 itemId);
S32 getItemCount();
S32 getSelectedItem();
S32 getSelectedItem(S32 index); // Given an item's index in the selection list, return its itemId
S32 getSelectedItemsCount() {return mSelectedItems.size();} // Returns the number of selected items
void moveItemUp( S32 itemId );
void moveItemDown( S32 itemId );
// misc.
bool scrollVisible( Item *item );
bool scrollVisible( S32 itemId );
bool scrollVisibleByObjectId( S32 objID );
void deleteSelection();
void clearSelection();
S32 findItemByName(const char *name);
S32 findItemByValue(const char *name);
S32 findItemByObjectId(S32 iObjId);
void sortTree( bool caseSensitive, bool traverseHierarchy, bool parentsFirst );
/// @name Filtering
/// @{
/// Get the current filter expression. Only tree items whose text matches this expression
/// are displayed. By default, the expression is empty and all items are shown.
const String& getFilterText() const { return mFilterText; }
/// Set the pattern by which to filter items in the tree. Only items in the tree whose text
/// matches this pattern are displayed.
void setFilterText( const String& text );
/// Clear the current item filtering pattern.
void clearFilterText() { setFilterText( String::EmptyString ); }
/// @}
// GuiControl
bool onAdd();
bool onWake();
void onSleep();
void onPreRender();
bool onKeyDown( const GuiEvent &event );
void onMouseDown(const GuiEvent &event);
void onMiddleMouseDown(const GuiEvent &event);
void onMouseMove(const GuiEvent &event);
void onMouseEnter(const GuiEvent &event);
void onMouseLeave(const GuiEvent &event);
void onRightMouseDown(const GuiEvent &event);
void onRightMouseUp(const GuiEvent &event);
void onMouseDragged(const GuiEvent &event);
virtual void onMouseUp(const GuiEvent &event);
/// Returns false if the object is a child of one of the inner items.
bool childSearch(Item * item, SimObject *obj, bool yourBaby);
/// Find immediately available inspector items (eg ones that aren't children of other inspector items)
/// and then update their sets
void inspectorSearch(Item * item, Item * parent, SimSet * parentSet, SimSet * newParentSet);
/// Find the Item associated with a sceneObject, returns true if it found one
bool objectSearch( const SimObject *object, Item **item );
// GuiArrayCtrl
void onRenderCell(Point2I offset, Point2I cell, bool, bool);
void onRender(Point2I offset, const RectI &updateRect);
bool renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText );
static void initPersistFields();
void inspectObject(SimObject * obj, bool okToEdit);
void buildVisibleTree(bool bForceFullUpdate = false);
void cancelRename();
void onRenameValidate();
void showItemRenameCtrl( Item* item );
DECLARE_CONOBJECT(GuiTreeViewCtrl);
DECLARE_CATEGORY( "Gui Lists" );
DECLARE_DESCRIPTION( "Hierarchical list of text items with optional icons.\nCan also be used to inspect SimObject hierarchies." );
};
#endif